Files
wehub-resource-sync 2aaeece67c
Codestyle Check / Lint (push) Has been cancelled
Codestyle Check / Check bypass (push) Has been cancelled
Pipelines-Test / Pipelines-Test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:14 +08:00

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()