259 lines
9.2 KiB
Python
259 lines
9.2 KiB
Python
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
|
|
import copy
|
|
import glob
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
|
|
import paddle
|
|
|
|
input_ids = paddle.to_tensor([[0, 345, 232, 328, 740, 140, 1695, 69, 6078, 1588, 2]])
|
|
|
|
|
|
def prepare_default_config(config):
|
|
config = copy.deepcopy(config)
|
|
config.hidden_size = 512
|
|
config.num_layers = 2
|
|
config.num_hidden_layers = 2
|
|
config.num_attention_heads = 16
|
|
config.num_key_value_heads = 16
|
|
config.intermediate_size = config.hidden_size
|
|
config.word_embed_proj_dim = 512
|
|
return config
|
|
|
|
|
|
def prepare_split_config(config):
|
|
config = prepare_default_config(config)
|
|
config = copy.deepcopy(config)
|
|
config.fuse_attention_qkv = False
|
|
config.fuse_attention_ffn = False
|
|
return config
|
|
|
|
|
|
def prepare_fuse_config(config):
|
|
config = prepare_default_config(config)
|
|
config = copy.deepcopy(config)
|
|
config.fuse_attention_qkv = True
|
|
config.fuse_attention_ffn = True
|
|
return config
|
|
|
|
|
|
def common_test_load(model_class, model_first, config_second, tempdir):
|
|
model_first.eval()
|
|
with paddle.no_grad():
|
|
first = model_first(input_ids)[0]
|
|
|
|
model_second = model_class.from_pretrained(tempdir, config=config_second)
|
|
model_second.eval()
|
|
with paddle.no_grad():
|
|
second = model_second(input_ids)[0]
|
|
|
|
assert paddle.allclose(paddle.mean(first), paddle.mean(second), atol=1e-5)
|
|
# assert paddle.allclose(first, second, atol=1e-4)
|
|
|
|
files = glob.glob(tempdir + "/*")
|
|
for f in files:
|
|
os.remove(f)
|
|
|
|
|
|
def common_test_save_and_load(config_first, config_second, model_class):
|
|
model_first = model_class.from_config(config_first)
|
|
|
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
# test load pdparams: model.pdparams
|
|
model_first.save_pretrained(save_dir=tempdir)
|
|
common_test_load(model_class, model_first, config_second, tempdir)
|
|
|
|
# test load shard pdparams: model-001-0f-008.pdparams
|
|
model_first.save_pretrained(tempdir, max_shard_size="5MB")
|
|
common_test_load(model_class, model_first, config_second, tempdir)
|
|
|
|
# test save safetensors: model.safetensors
|
|
model_first.save_pretrained(tempdir, safe_serialization=True)
|
|
common_test_load(model_class, model_first, config_second, tempdir)
|
|
|
|
# test load shard safetensors: model-001-0f-008.safetensors
|
|
model_first.save_pretrained(tempdir, max_shard_size="5MB", safe_serialization=True)
|
|
common_test_load(model_class, model_first, config_second, tempdir)
|
|
|
|
|
|
def _test_split_to_fuse(config_class, model_class):
|
|
config = config_class()
|
|
|
|
config_split = prepare_split_config(config)
|
|
config_fuse = prepare_fuse_config(config)
|
|
|
|
# Test from splitted weights to fused weight
|
|
common_test_save_and_load(config_split, config_fuse, model_class)
|
|
|
|
|
|
def _test_fuse_to_split(config_class, model_class):
|
|
config = config_class()
|
|
|
|
config_split = prepare_split_config(config)
|
|
config_fuse = prepare_fuse_config(config)
|
|
|
|
# Test from fused weight to splitted weights
|
|
common_test_save_and_load(config_fuse, config_split, model_class)
|
|
|
|
|
|
def _test_fast_ffn():
|
|
from functools import partial
|
|
|
|
import paddle
|
|
from paddle import nn
|
|
|
|
from paddlenlp.transformers import PretrainedModel
|
|
from paddlenlp.transformers.configuration_utils import PretrainedConfig
|
|
|
|
class TestConfig(PretrainedConfig):
|
|
def __init__(self, fast_ffn_state=False, convert_fast_ffn=False):
|
|
self.fast_ffn_state = fast_ffn_state
|
|
self.convert_fast_ffn = convert_fast_ffn
|
|
super().__init__()
|
|
|
|
class TestMLP(nn.Layer):
|
|
def __init__(self, config):
|
|
super().__init__()
|
|
self.config = config
|
|
self.hidden_size = config.hidden_size
|
|
self.gate_up_fused_proj = nn.Linear(self.hidden_size, self.hidden_size * 2, bias_attr=True)
|
|
|
|
def forward(self, hidden_state):
|
|
hidden_state = self.gate_up_fused_proj(hidden_state)
|
|
if self.config.use_fast_ffn:
|
|
x, y = paddle.chunk(hidden_state, chunks=2, axis=-1)
|
|
else:
|
|
x, y = hidden_state[..., ::2], hidden_state[..., 1::2]
|
|
|
|
return nn.functional.silu(x) * y
|
|
|
|
class TestPretrainedModel(PretrainedModel):
|
|
config_class = TestConfig
|
|
|
|
@classmethod
|
|
def _get_fuse_or_split_param_mappings(cls, config: TestConfig, is_fuse=False):
|
|
|
|
# user defined function to get convert param mappings
|
|
def convert_fast_ffn_fn(fuse_params, convert_fast_ffn=False):
|
|
import numpy as np
|
|
|
|
concat_fn = np.concatenate
|
|
if isinstance(fuse_params[0], paddle.Tensor):
|
|
concat_fn = paddle.concat
|
|
|
|
if convert_fast_ffn:
|
|
# fast_ffn
|
|
first = fuse_params[0][..., ::2]
|
|
second = fuse_params[0][..., 1::2]
|
|
return concat_fn([first, second], axis=-1)
|
|
|
|
fn = convert_fast_ffn_fn
|
|
|
|
convert_fast_ffn_keys = (
|
|
"layers.0.gate_up_fused_proj.weight",
|
|
"layers.0.gate_up_fused_proj.weight",
|
|
)
|
|
convert_fast_ffn_bias_keys = (
|
|
"layers.0.gate_up_fused_proj.bias",
|
|
"layers.0.gate_up_fused_proj.bias",
|
|
)
|
|
fast_ffn_state = getattr(config, "fast_ffn_state", False)
|
|
convert_fast_ffn = getattr(config, "convert_fast_ffn", False)
|
|
convert_fast_ffn &= not fast_ffn_state
|
|
|
|
final_actions = {}
|
|
if is_fuse:
|
|
# for_get_fuse_or_split_param_mappings, is_fuse have two conditions, true and false,
|
|
# to fit partial fuse or split conditions, is_fuse will called twice(True and False).
|
|
# thus, for this func, we only use one condition.
|
|
|
|
# use_fast_ffn only in one condition
|
|
# convert when use_fast_ffn is False
|
|
if convert_fast_ffn:
|
|
for i in range(config.num_hidden_layers):
|
|
for keys in [convert_fast_ffn_keys, convert_fast_ffn_bias_keys]:
|
|
keys = tuple([key.replace("layers.0.", f"layers.{i}.") for key in keys])
|
|
final_actions[keys] = partial(fn, convert_fast_ffn=convert_fast_ffn)
|
|
return final_actions
|
|
|
|
def _init_weights(self, layer):
|
|
if isinstance(layer, (nn.Linear, nn.Embedding)):
|
|
if isinstance(layer.weight, paddle.Tensor):
|
|
layer.weight.set_value(paddle.tensor.normal(mean=0.0, std=1.0, shape=layer.weight.shape))
|
|
if hasattr(layer, "bias") and isinstance(layer.bias, paddle.Tensor):
|
|
layer.bias.set_value(paddle.tensor.normal(mean=0.0, std=1.0, shape=layer.bias.shape))
|
|
|
|
class TestModel(TestPretrainedModel):
|
|
def __init__(self, config):
|
|
super().__init__(config)
|
|
self.layers = nn.LayerList([TestMLP(config=config) for i in range(config.num_hidden_layers)])
|
|
|
|
def forward(self, hidden_state):
|
|
for idx, (decoder_layer) in enumerate(self.layers):
|
|
hidden_state = decoder_layer(hidden_state)
|
|
return hidden_state
|
|
|
|
class TestForCausalLM(TestPretrainedModel):
|
|
def __init__(self, config):
|
|
super().__init__(config)
|
|
self.config = config
|
|
self.embedding_layer = nn.Embedding(65535, self.config.hidden_size)
|
|
self.test = TestModel(config=config)
|
|
|
|
def forward(self, input_ids):
|
|
hidden_state = self.embedding_layer(input_ids)
|
|
return self.test(hidden_state)
|
|
|
|
config = TestConfig()
|
|
config = prepare_default_config(config)
|
|
config_no_fast_ffn = copy.deepcopy(config)
|
|
config_fast_ffn = copy.deepcopy(config)
|
|
|
|
config_no_fast_ffn.use_fast_ffn = False
|
|
|
|
config_fast_ffn.use_fast_ffn = True
|
|
config_fast_ffn.fast_ffn_state = False
|
|
config_fast_ffn.convert_fast_ffn = True
|
|
|
|
common_test_save_and_load(config_no_fast_ffn, config_fast_ffn, TestForCausalLM)
|
|
|
|
|
|
from paddlenlp.transformers import (
|
|
GPTConfig,
|
|
GPTForCausalLM,
|
|
LlamaConfig,
|
|
LlamaForCausalLM,
|
|
OPTConfig,
|
|
OPTForCausalLM,
|
|
)
|
|
|
|
|
|
class TestFuseOrSplit(unittest.TestCase):
|
|
def test_model_split_to_fuse(self):
|
|
_test_split_to_fuse(LlamaConfig, LlamaForCausalLM)
|
|
_test_split_to_fuse(GPTConfig, GPTForCausalLM)
|
|
_test_split_to_fuse(OPTConfig, OPTForCausalLM)
|
|
|
|
def test_model_fuse_to_split(self):
|
|
_test_fuse_to_split(LlamaConfig, LlamaForCausalLM)
|
|
_test_fuse_to_split(GPTConfig, GPTForCausalLM)
|
|
_test_fuse_to_split(OPTConfig, OPTForCausalLM)
|
|
|
|
def test_model_convert_fast_ffn(self):
|
|
_test_fast_ffn()
|