[LLM] Add qwen3 (#10524)

* init qwen3

* first version.

* fisrt version support for qwen3

* add missing files.

* add readme

* refine copyright.

* init version for qwen moe.

* fix networks.

* fix generate.

* refine

* fix set_state_dict to slow for supper large network for network, such as moe model.

* fix

* fix

* fix set_state_dict to slow for supper large network for network, such as moe model.

* fix qwen2 tensor parallel.

---------

Co-authored-by: DrownFish19 <DrownFish19@gmail.com>
This commit is contained in:
Zhong Hui
2025-05-12 10:35:58 +08:00
committed by GitHub
parent 759ae99609
commit c043487a16
19 changed files with 3614 additions and 58 deletions

View File

@@ -33,17 +33,20 @@
## News 📢
* **2025.03.17 《DeepSeek-R1满血版单机部署实测》** 🔥🔥🔥 飞桨框架3.0大模型推理部署全面升级,支持多款主流大模型,DeepSeek-R1满血版实现单机部署,吞吐提升一倍!欢迎广大用户开箱体验~现已开启有奖活动:完成 DeepSeek-R1-MTP 单机部署任务、提交高质量测评 blog,即可实时赢取奖金!💰💰💰
报名[地址](https://www.wjx.top/vm/OlzzmbG.aspx#), 活动详情:https://github.com/PaddlePaddle/PaddleNLP/issues/10166 , 参考文档:https://github.com/PaddlePaddle/PaddleNLP/issues/10157 。
* **2025.04.29 PaddleNLP 现已支持 Qwen3 系列模型**: Qwen3 系列模型支持持两种思考模式,预训练约 36 万亿个 token、119 种语言和方言。包括六个 Dense 模型, Qwen3-32B、Qwen3-14B、Qwen3-8B、Qwen3-4B、Qwen3-1.7B 和 Qwen3-0.6B。两个 MoE 模型的权重:Qwen3-235B-A22B,Qwen3-30B-A3B。
* **2025.03.12 [PaddleNLP v3.0 Beta4](https://github.com/PaddlePaddle/PaddleNLP/releases/tag/v3.0.0-beta4)**:全面支持 DeepSeek V3/R1/R1-Distill, 及 QwQ-32B 等热门思考模型。**DeepSeek V3/R1完整版支持 FP8、INT8、4-bit 量化推理,MTP 投机解码**。单机 FP8推理输出超**1000 tokens/s**; 4-bit 推理输出超**2100 tokens/s**! 发布新版推理部署镜像,热门模型[一键部署](https://paddlenlp.readthedocs.io/zh/latest/llm/server/docs/general_model_inference.html)。推理部署[使用文档](https://paddlenlp.readthedocs.io/zh/latest/llm/docs/predict/index.html)全面更新,体验全面提升!自研下一代通用信息抽取模型 PP-UIE [全新发布](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/llm/application/information_extraction),支持8K 长度信息抽取。新增大模型 Embedding 训练,支持 INF-CL 超大 batch size 训练。新增[MergeKit](https://paddlenlp.readthedocs.io/zh/latest/llm/docs/mergekit.html)模型融合工具,缓解对齐代价。低资源训练全面优化,16G 小显存可以流畅训练。
* **2025.03.06 PaddleNLP 现已支持 Qwen/QwQ-32B 模型**: 其模型参数仅有 32B,但其数学推理、编程能力和通用能力可与具备 671B 参数(其中 37B 被激活)的 DeepSeek-R1 媲美。借助 PaddleNLP 3.0套件,现可实现多种并行策略[微调训练](./llm/README.md)、[高性能推理、低比特量化](./llm/docs/predict/qwen.md)和[服务化部署](./llm/server/README.md)。
* **2025.02.10 PaddleNLP 现已支持 DeepSeek-R1系列模型,[在线使用](https://aistudio.baidu.com/projectdetail/8775758)**:依托全新的 PaddleNLP 3.0套件,DeepSeek-R1系列模型现已全面支持。凭借数据并行、数据分组切分并行、模型并行、流水线并行以及专家并行等一系列先进的分布式训练能力,结合 Paddle 框架独有的列稀疏注意力掩码表示技术——FlashMask 方法,DeepSeek-R1系列模型在训练过程中显著降低了显存消耗,同时取得了卓越的训练性能提升。
<details><summary> <b>点击展开</b> </summary><div>
* **2025.03.17 《DeepSeek-R1满血版单机部署实测》** 🔥🔥🔥 飞桨框架3.0大模型推理部署全面升级,支持多款主流大模型,DeepSeek-R1满血版实现单机部署,吞吐提升一倍!欢迎广大用户开箱体验~现已开启有奖活动:完成 DeepSeek-R1-MTP 单机部署任务、提交高质量测评 blog,即可实时赢取奖金!💰💰💰
报名[地址](https://www.wjx.top/vm/OlzzmbG.aspx#), 活动详情:https://github.com/PaddlePaddle/PaddleNLP/issues/10166 , 参考文档:https://github.com/PaddlePaddle/PaddleNLP/issues/10157 。
* **2025.03.06 PaddleNLP 现已支持 Qwen/QwQ-32B 模型**: 其模型参数仅有 32B,但其数学推理、编程能力和通用能力可与具备 671B 参数(其中 37B 被激活)的 DeepSeek-R1 媲美。借助 PaddleNLP 3.0套件,现可实现多种并行策略[微调训练](./llm/README.md)、[高性能推理、低比特量化](./llm/docs/predict/qwen.md)和[服务化部署](./llm/server/README.md)。
* **2025.02.20 🔥🔥《PP-UIE 信息抽取智能引擎全新升级》** 强化零样本学习能力,支持极少甚至零标注数据实现高效冷启动与迁移学习,显著降低数据标注成本;具备处理长文本能力,支持 8192 个 Token 长度文档信息抽取,实现跨段落识别关键信息,形成完整理解;提供完整可定制化的训练和推理全流程,训练效率相较于 LLama-Factory 实现了1.8倍的提升。
2月26日(周三)19:00为您深度解析全新 PP-UIE 技术方案及在部署方面的功能、优势与技巧。报名链接:https://www.wjx.top/vm/mBKC6pb.aspx?udsid=606418
@@ -119,6 +122,7 @@
| [Qwen2.5](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/llm/config/qwen/) | Qwen/Qwen2.5-0.5B, Qwen/Qwen2.5-0.5B-Instruct, Qwen/Qwen2.5-1.5B, Qwen/Qwen2.5-1.5B-Instruct, Qwen/Qwen2.5-3B, Qwen/Qwen2.5-3B-Instruct, Qwen/Qwen2.5-7B, Qwen/Qwen2.5-7B-Instruct, Qwen/Qwen2.5-7B-Instruct-1M, Qwen/Qwen2.5-14B, Qwen/Qwen2.5-14B-Instruct, Qwen/Qwen2.5-14B-Instruct-1M, Qwen/Qwen2.5-32B, Qwen/Qwen2.5-32B-Instruct, Qwen/Qwen2.5-72B, Qwen/Qwen2.5-72B-Instruct |
| [Qwen2.5-Math](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/llm/config/qwen/) | Qwen/Qwen2.5-Math-1.5B, Qwen/Qwen2.5-Math-1.5B-Instruct, Qwen/Qwen2.5-Math-7B, Qwen/Qwen2.5-Math-7B-Instruct, Qwen/Qwen2.5-Math-72B, Qwen/Qwen2.5-Math-72B-Instruct, Qwen/Qwen2.5-Math-RM-72B |
| [Qwen2.5-Coder](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/llm/config/qwen/) | Qwen/Qwen2.5-Coder-1.5B, Qwen/Qwen2.5-Coder-1.5B-Instruct, Qwen/Qwen2.5-Coder-7B, Qwen/Qwen2.5-Coder-7B-Instruct |
| [Qwen3](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/llm/config/qwen/) | Qwen/Qwen3-0.6B, Qwen/Qwen3-1.7B, Qwen/Qwen3-4B, Qwen/Qwen3-8B, Qwen/Qwen3-14B, Qwen/Qwen3-32B, Qwen/Qwen3-30B-A3B, Qwen/Qwen3-235B-A22B, Qwen/Qwen3-0.6B-Base, Qwen/Qwen3-1.7B-Base, Qwen/Qwen3-4B-Base, Qwen/Qwen3-8B-Base, Qwen/Qwen3-14B-Base, Qwen/Qwen3-30B-A3B-Base |
| [QwQ](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/llm/config/qwen/) | Qwen/QwQ-32B, Qwen/QwQ-32B-Preview |
| [Yuan2](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/llm/config/yuan/) | IEITYuan/Yuan2-2B, IEITYuan/Yuan2-51B, IEITYuan/Yuan2-102B |

View File

@@ -261,6 +261,8 @@ from .prophetnet.tokenizer import *
from .qwen import *
from .qwen2 import *
from .qwen2_moe import *
from .qwen3 import *
from .qwen3_moe import *
from .reformer.configuration import *
from .reformer.modeling import *
from .reformer.tokenizer import *

View File

@@ -99,6 +99,8 @@ CONFIG_MAPPING_NAMES = OrderedDict(
("qwen", "QWenConfig"),
("qwen2", "Qwen2Config"),
("qwen2_moe", "Qwen2MoeConfig"),
("qwen3", "Qwen3Config"),
("qwen3_moe", "Qwen3MoeConfig"),
("reformer", "ReformerConfig"),
("rembert", "RemBertConfig"),
("roberta", "RobertaConfig"),
@@ -190,6 +192,8 @@ MODEL_NAMES_MAPPING = OrderedDict(
("qwen", "QWen"),
("qwen2", "Qwen2"),
("qwen2_moe", "Qwen2Moe"),
("qwen3", "Qwen3"),
("qwen3_moe", "Qwen3Moe"),
("reformer", "Reformer"),
("rembert", "RemBert"),
("roberta", "Roberta"),

View File

@@ -124,7 +124,9 @@ MAPPING_NAMES = OrderedDict(
("Mistral", "mistral"),
("Mixtral", "mixtral"),
("Qwen2", "qwen2"),
("Qwen3", "qwen3"),
("Qwen2Moe", "qwen2_moe"),
("Qwen3Moe", "qwen3_moe"),
("Gemma", "gemma"),
("Yuan", "yuan"),
("Mamba", "mamba"),

View File

@@ -38,7 +38,7 @@ from paddle import Tensor
from paddle.nn import Layer
from paddlenlp.utils.distributed import distributed_allgather, distributed_gather
from paddlenlp.utils.env import CONFIG_NAME, PADDLE_WEIGHTS_NAME, PYTORCH_WEIGHTS_NAME
from paddlenlp.utils.env import CONFIG_NAME, PYTORCH_WEIGHTS_NAME
from paddlenlp.utils.import_utils import (
is_package_available,
is_torch_available,
@@ -1161,7 +1161,7 @@ class ConversionMixin:
file for file in os.listdir(os.path.dirname(weight_file)) if file.startswith("pytorch_model-")
]
state_dict = {}
for file in files:
for file in sorted(files):
sub_state_dict = load_torch(os.path.join(os.path.dirname(weight_file), file))
state_dict.update(sub_state_dict)
else:
@@ -1179,13 +1179,9 @@ class ConversionMixin:
all_layer_names.remove(name_mapping.source_name)
if all_layer_names:
logger.warning(f"there are {len(all_layer_names)} tensors not initialized:")
for layer_name in all_layer_names:
logger.warning(f"--- {layer_name}")
logger.warning(f"There are {len(all_layer_names)} tensors not initialized:")
logger.warning(f"Keys: {all_layer_names}")
model_weight_file = os.path.join(cache_dir, PADDLE_WEIGHTS_NAME)
if not os.path.isfile(model_weight_file):
paddle.save(state_dict, model_weight_file)
return state_dict
@classmethod
@@ -1553,10 +1549,7 @@ class Converter(ConversionMixin, LogitComparer):
all_layer_names.remove(name_mapping.source_name)
if all_layer_names:
logger.warning(f"there are {len(all_layer_names)} tensors not initialized:")
for layer_name in all_layer_names:
logger.warning(f"--- {layer_name}")
logger.warning(f"There are {len(all_layer_names)} tensors not initialized:")
logger.warning(f"Keys: {all_layer_names}")
model_weight_file = os.path.join(input_dir, PADDLE_WEIGHTS_NAME)
paddle.save(state_dict, model_weight_file)
return state_dict

View File

@@ -807,12 +807,14 @@ def load_sharded_checkpoint(model, folder, variant=None, strict=True, prefer_saf
return missing_keys, unexpected_keys
def faster_set_state_dict(model, state_dict, strict_dtype=True):
def faster_set_state_dict(model, state_dict, model_state_dict=None, strict_dtype=True):
if model_state_dict is None:
model_state_dict = model.state_dict()
# the state_dict will be destroyed.
unused_keys = set(state_dict.keys())
unset_keys = set(model.state_dict().keys())
unset_keys = set(model_state_dict.keys())
with paddle.no_grad():
for k, v in model.state_dict().items():
for k, v in model_state_dict.items():
if k in state_dict:
v_new = state_dict.pop(k)
if not isinstance(v_new, paddle.Tensor):
@@ -857,14 +859,14 @@ def faster_set_state_dict(model, state_dict, strict_dtype=True):
return error_msgs
def _load_state_dict_into_model(model_to_load, state_dict, start_prefix):
def _load_state_dict_into_model(model_to_load, state_dict, start_prefix, model_to_load_state_dict):
# torch will cast dtype in load_state_dict, but paddle strictly check dtype
if len(start_prefix) > 0:
for key in list(state_dict.keys()):
if key.startswith(start_prefix):
state_dict[key.replace(start_prefix, "")] = state_dict.pop(key)
_convert_state_dict_dtype_and_shape(state_dict, model_to_load)
_convert_state_dict_dtype_and_shape(state_dict, model_to_load_state_dict)
error_msgs = []
@@ -874,7 +876,15 @@ def _load_state_dict_into_model(model_to_load, state_dict, start_prefix):
# paddlenlp hold missing_keys , just ignore not found warnings.
warnings.filterwarnings("ignore", message=r".*is not found in the provided dict.*")
warnings.filterwarnings("ignore", message=r".*paddle.to_tensor.*")
model_to_load.set_state_dict(state_dict)
if len(model_to_load_state_dict) > 4000 and os.getenv("DISABLE_FASTER_SET_STATE_DICT", None) is None:
logger.warning_once(
"The model contains an excessive number of tensors, so we utilize the faster_set_state_dict method to load tensors into the model efficiently."
" If any issues arise during the loading process, you can disable this feature by setting the environment variable DISABLE_FASTER_SET_STATE_DICT=1."
)
faster_set_state_dict(model_to_load, state_dict, model_to_load_state_dict)
else:
model_to_load.set_state_dict(state_dict)
error_msgs.extend([str(x.message) for x in w])
del state_dict
@@ -882,12 +892,12 @@ def _load_state_dict_into_model(model_to_load, state_dict, start_prefix):
return error_msgs
def _convert_state_dict_dtype_and_shape(state_dict, model_to_load):
def _convert_state_dict_dtype_and_shape(state_dict, model_to_load_state_dict):
# convert the dtype of state dict
def is_0d_or_1d(tensor):
return len(tensor.shape) == 0 or list(tensor.shape) == [1]
for key, value in model_to_load.state_dict().items():
for key, value in model_to_load_state_dict.items():
if key in list(state_dict.keys()):
if isinstance(state_dict[key], np.ndarray):
raise ValueError(
@@ -2034,12 +2044,15 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
start_prefix = cls.base_model_prefix + "."
if len(cls.base_model_prefix) > 0 and hasattr(model, cls.base_model_prefix) and not has_prefix_module:
model_to_load = getattr(model, cls.base_model_prefix)
base_model_expected_keys = list(model_to_load.state_dict().keys())
base_model_expected_keys = list(model_state_dict.keys())
if any(key in expected_keys_not_prefixed and key not in base_model_expected_keys for key in loaded_keys):
raise ValueError(
"The state dictionary of the model you are trying to load is corrupted. Are you sure it was "
"properly saved?"
)
model_to_load_state_dict = model_to_load.state_dict()
else:
model_to_load_state_dict = model_state_dict
def _find_mismatched_keys(
state_dict,
@@ -2143,7 +2156,12 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
keep_in_fp32_modules=keep_in_fp32_modules,
)
else:
error_msgs = _load_state_dict_into_model(model_to_load, state_dict, start_prefix)
error_msgs = _load_state_dict_into_model(
model_to_load,
state_dict,
start_prefix,
model_to_load_state_dict,
)
else:
# Sharded checkpoint or whole but low_cpu_mem_usage==True
@@ -2156,9 +2174,7 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
resume_state_dict = {}
if len(resolved_archive_file) > 1:
resolved_archive_file = tqdm(resolved_archive_file, desc="Loading checkpoint shards")
if low_cpu_mem_usage or quantization_linear_list is not None:
# model.state_dict() takes a long time
model_to_load_state_dict = model_to_load.state_dict()
for shard_file in resolved_archive_file:
pre_tensor_parallel_split = False
if quantization_linear_list is not None:
@@ -2270,7 +2286,9 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
)
error_msgs += new_error_msgs
else:
error_msgs += _load_state_dict_into_model(model_to_load, state_dict, start_prefix)
error_msgs += _load_state_dict_into_model(
model_to_load, state_dict, start_prefix, model_to_load_state_dict
)
# force memory release
del state_dict
@@ -2581,6 +2599,7 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
for key in model.state_dict().keys():
if "quant_weight" in key:
quantization_linear_list.append(key[:-13])
model, missing_keys, unexpected_keys, mismatched_keys = cls._load_pretrained_model(
model=model,
state_dict=state_dict,
@@ -2749,7 +2768,7 @@ class PretrainedModel(Layer, GenerationMixin, ConversionMixin):
# make sure that file to be deleted matches format of sharded file, e.g. paddle_model-00001-of-00005
filename_no_suffix = filename.replace(".pdparams", "").replace(".safetensors", "")
reg = re.compile("(.*?)-\d{5}-of-\d{5}")
reg = re.compile(r"(.*?)-\d{5}-of-\d{5}")
if (
filename.startswith(weights_no_suffix)

View File

@@ -226,7 +226,7 @@ class PretrainedMoEGate(nn.Layer, MoEGateMixin):
chosen_expert = topk_idx.reshape([-1])
# Shape: [seq_len * k, num_experts].
token_priority = F.one_hot(chosen_expert, self.num_experts).cast(paddle.int32)
token_priority = paddle.logical_and(token_priority > 0, token_priority.cumsum(axis=0) < capacity)
token_priority = paddle.logical_and(token_priority > 0, token_priority.cumsum(axis=0) <= capacity)
# Shape: [seq_len, num_experts].
token_priority = token_priority.reshape([-1, k, self.num_experts]).sum(axis=1)
@@ -532,12 +532,14 @@ class PretrainedMoEGate(nn.Layer, MoEGateMixin):
token_priority = self._priority(top_idx, capacity)
# normalize gates
# gates_masked is equal to top_gate.
gates_masked = gates * mask
if self.training:
gates_s = paddle.sum(gates_masked, axis=-1, keepdim=True)
denom_s = paddle.clip(gates_s, min=paddle.finfo(gates_masked.dtype).eps)
if self.norm_topk_prob:
gates_masked = gates_masked / denom_s
# if self.training:
gates_s = paddle.sum(gates_masked, axis=-1, keepdim=True)
denom_s = paddle.clip(gates_s, min=paddle.finfo(gates_masked.dtype).eps)
if self.norm_topk_prob:
gates_masked = gates_masked / denom_s
gates_masked *= self.routed_scaling_factor
return (
capacity,

View File

@@ -101,7 +101,7 @@ class Qwen2Config(PretrainedConfig):
num_key_value_heads=32,
hidden_act="silu",
max_position_embeddings=32768,
seq_length=32768,
# seq_length=32768,
initializer_range=0.02,
rms_norm_eps=1e-6,
use_cache=True,
@@ -113,6 +113,7 @@ class Qwen2Config(PretrainedConfig):
use_sliding_window=False,
sliding_window=4096,
max_window_layers=28,
attention_bias=True,
attention_dropout=0.0,
rope_scaling_factor=1.0,
rope_scaling_type=None,
@@ -122,7 +123,7 @@ class Qwen2Config(PretrainedConfig):
):
self.vocab_size = vocab_size
self.max_position_embeddings = max_position_embeddings
self.seq_length = seq_length
# self.seq_length = seq_length
self.hidden_size = hidden_size
self.intermediate_size = intermediate_size
self.num_hidden_layers = num_hidden_layers
@@ -141,6 +142,7 @@ class Qwen2Config(PretrainedConfig):
self.rms_norm_eps = rms_norm_eps
self.use_cache = use_cache
self.rope_theta = rope_theta
self.attention_bias = attention_bias
self.attention_dropout = attention_dropout
self.use_cache = use_cache

View File

@@ -509,8 +509,9 @@ class Qwen2Attention(nn.Layer):
self.skip_recompute_ops = skip_recompute_ops
self.hidden_size = config.hidden_size
self.num_heads = config.num_attention_heads
self.num_attention_heads = config.num_attention_heads
self.head_dim = self.hidden_size // config.num_attention_heads
self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
self.num_key_value_heads = config.num_key_value_heads
assert config.num_attention_heads // config.num_key_value_heads
@@ -521,9 +522,9 @@ class Qwen2Attention(nn.Layer):
self.is_causal = True
self.attention_dropout = config.attention_dropout
self.seq_length = config.seq_length
# self.seq_length = config.seq_length
self.sequence_parallel = config.sequence_parallel
self.has_bias = config.attention_bias
self.fuse_attention_qkv = config.fuse_attention_qkv
# Note that we will actually perform a recompute only if both enable_recompute and layerwise_recompute are set to True
@@ -576,27 +577,37 @@ class Qwen2Attention(nn.Layer):
if self.fuse_attention_qkv:
self.qkv_proj = ColumnParallelLinear(
self.hidden_size,
self.hidden_size + 2 * self.config.num_key_value_heads * self.head_dim,
has_bias=True,
self.num_attention_heads * self.head_dim + 2 * self.config.num_key_value_heads * self.head_dim,
has_bias=self.has_bias,
gather_output=False,
)
else:
self.q_proj = ColumnParallelLinear(
self.hidden_size, self.hidden_size, has_bias=True, gather_output=False
self.hidden_size,
self.num_attention_heads * self.head_dim,
has_bias=self.has_bias,
gather_output=False,
)
self.k_proj = ColumnParallelLinear(self.hidden_size, self.config.num_key_value_heads * self.head_dim, has_bias=True, gather_output=False) # fmt:skip
self.v_proj = ColumnParallelLinear(self.hidden_size, self.config.num_key_value_heads * self.head_dim, has_bias=True, gather_output=False) # fmt:skip
self.k_proj = ColumnParallelLinear(self.hidden_size, self.config.num_key_value_heads * self.head_dim, has_bias=self.has_bias, gather_output=False) # fmt:skip
self.v_proj = ColumnParallelLinear(self.hidden_size, self.config.num_key_value_heads * self.head_dim, has_bias=self.has_bias, gather_output=False) # fmt:skip
self.o_proj = RowParallelLinear(self.hidden_size, self.hidden_size, has_bias=False, input_is_parallel=True)
else:
if self.fuse_attention_qkv:
self.qkv_proj = Linear(
self.hidden_size, self.hidden_size + 2 * self.config.num_key_value_heads * self.head_dim
self.hidden_size,
self.num_attention_heads * self.head_dim + 2 * self.config.num_key_value_heads * self.head_dim,
)
else:
self.q_proj = Linear(self.hidden_size, self.hidden_size, bias_attr=True)
self.k_proj = Linear(self.hidden_size, self.config.num_key_value_heads * self.head_dim, bias_attr=True)
self.v_proj = Linear(self.hidden_size, self.config.num_key_value_heads * self.head_dim, bias_attr=True)
self.o_proj = Linear(self.hidden_size, self.hidden_size, bias_attr=False)
self.q_proj = Linear(
self.hidden_size, self.num_attention_heads * self.head_dim, bias_attr=self.has_bias
)
self.k_proj = Linear(
self.hidden_size, self.config.num_key_value_heads * self.head_dim, bias_attr=self.has_bias
)
self.v_proj = Linear(
self.hidden_size, self.config.num_key_value_heads * self.head_dim, bias_attr=self.has_bias
)
self.o_proj = Linear(self.num_attention_heads * self.head_dim, self.hidden_size, bias_attr=False)
self.rotary_emb = Qwen2RotaryEmbedding(
self.head_dim,

View File

@@ -779,7 +779,7 @@ class Qwen2MoeGate(PretrainedMoEGate):
return capacity, combine_weights, dispatch_mask, exp_counts, l_aux, l_zloss
class Qwen2MoeSparseMoEBlock(MoELayer):
class Qwen2MoeSparseMoeBlock(MoELayer):
def __init__(self, config: Qwen2MoeConfig):
gate = Qwen2MoeGate(
config,
@@ -822,7 +822,7 @@ class Qwen2MoeDecoderLayer(nn.Layer):
self.self_attn = Qwen2MoeAttention(config, layerwise_recompute)
if config.num_experts > 0:
self.mlp = Qwen2MoeSparseMoEBlock(config)
self.mlp = Qwen2MoeSparseMoeBlock(config)
else:
# num_experts == 0 or this layer is not sparse layer
self.mlp = Qwen2MoeMLP(config)

View File

@@ -0,0 +1,18 @@
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
# Copyright 2024 The Qwen Team and The HuggingFace Inc. team. 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.
from .configuration import *
from .modeling import *
from .modeling_pp import *

View File

@@ -0,0 +1,195 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
# Copyright 2024 The Qwen team, Alibaba Group and the HuggingFace Inc. team. 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.
"""Qwen2 model configuration"""
from ..configuration_utils import PretrainedConfig
class Qwen3Config(PretrainedConfig):
r"""
This is the configuration class to store the configuration of a [`Qwen3Model`]. It is used to instantiate a
Qwen3 model according to the specified arguments, defining the model architecture. Instantiating a configuration
with the defaults will yield a similar configuration to that of
Qwen3-8B [Qwen/Qwen3-8B](https://huggingface.co/Qwen/Qwen3-8B).
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
documentation from [`PretrainedConfig`] for more information.
Args:
vocab_size (`int`, *optional*, defaults to 151936):
Vocabulary size of the Qwen3 model. Defines the number of different tokens that can be represented by the
`inputs_ids` passed when calling [`Qwen3Model`]
hidden_size (`int`, *optional*, defaults to 4096):
Dimension of the hidden representations.
intermediate_size (`int`, *optional*, defaults to 22016):
Dimension of the MLP representations.
num_hidden_layers (`int`, *optional*, defaults to 32):
Number of hidden layers in the Transformer encoder.
num_attention_heads (`int`, *optional*, defaults to 32):
Number of attention heads for each attention layer in the Transformer encoder.
num_key_value_heads (`int`, *optional*, defaults to 32):
This is the number of key_value heads that should be used to implement Grouped Query Attention. If
`num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
`num_key_value_heads=1` the model will use Multi Query Attention (MQA) otherwise GQA is used. When
converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
by meanpooling all the original heads within that group. For more details checkout [this
paper](https://arxiv.org/pdf/2305.13245.pdf). If it is not specified, will default to `32`.
head_dim (`int`, *optional*, defaults to 128):
The attention head dimension.
hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
The non-linear activation function (function or string) in the decoder.
max_position_embeddings (`int`, *optional*, defaults to 32768):
The maximum sequence length that this model might ever be used with.
initializer_range (`float`, *optional*, defaults to 0.02):
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
rms_norm_eps (`float`, *optional*, defaults to 1e-06):
The epsilon used by the rms normalization layers.
use_cache (`bool`, *optional*, defaults to `True`):
Whether or not the model should return the last key/values attentions (not used by all models). Only
relevant if `config.is_decoder=True`.
tie_word_embeddings (`bool`, *optional*, defaults to `False`):
Whether the model's input and output word embeddings should be tied.
rope_theta (`float`, *optional*, defaults to 10000.0):
The base period of the RoPE embeddings.
rope_scaling (`Dict`, *optional*):
Dictionary containing the scaling configuration for the RoPE embeddings. NOTE: if you apply new rope type
and you expect the model to work on longer `max_position_embeddings`, we recommend you to update this value
accordingly.
Expected contents:
`rope_type` (`str`):
The sub-variant of RoPE to use. Can be one of ['default', 'linear', 'dynamic', 'yarn', 'longrope',
'llama3'], with 'default' being the original RoPE implementation.
`factor` (`float`, *optional*):
Used with all rope types except 'default'. The scaling factor to apply to the RoPE embeddings. In
most scaling types, a `factor` of x will enable the model to handle sequences of length x *
original maximum pre-trained length.
`original_max_position_embeddings` (`int`, *optional*):
Used with 'dynamic', 'longrope' and 'llama3'. The original max position embeddings used during
pretraining.
`attention_factor` (`float`, *optional*):
Used with 'yarn' and 'longrope'. The scaling factor to be applied on the attention
computation. If unspecified, it defaults to value recommended by the implementation, using the
`factor` field to infer the suggested value.
`beta_fast` (`float`, *optional*):
Only used with 'yarn'. Parameter to set the boundary for extrapolation (only) in the linear
ramp function. If unspecified, it defaults to 32.
`beta_slow` (`float`, *optional*):
Only used with 'yarn'. Parameter to set the boundary for interpolation (only) in the linear
ramp function. If unspecified, it defaults to 1.
`short_factor` (`List[float]`, *optional*):
Only used with 'longrope'. The scaling factor to be applied to short contexts (<
`original_max_position_embeddings`). Must be a list of numbers with the same length as the hidden
size divided by the number of attention heads divided by 2
`long_factor` (`List[float]`, *optional*):
Only used with 'longrope'. The scaling factor to be applied to long contexts (<
`original_max_position_embeddings`). Must be a list of numbers with the same length as the hidden
size divided by the number of attention heads divided by 2
`low_freq_factor` (`float`, *optional*):
Only used with 'llama3'. Scaling factor applied to low frequency components of the RoPE
`high_freq_factor` (`float`, *optional*):
Only used with 'llama3'. Scaling factor applied to high frequency components of the RoPE
attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`):
Whether to use a bias in the query, key, value and output projection layers during self-attention.
use_sliding_window (`bool`, *optional*, defaults to `False`):
Whether to use sliding window attention.
sliding_window (`int`, *optional*, defaults to 4096):
Sliding window attention (SWA) window size. If not specified, will default to `4096`.
max_window_layers (`int`, *optional*, defaults to 28):
The number of layers that use SWA (Sliding Window Attention). The bottom layers use SWA while the top use full attention.
attention_dropout (`float`, *optional*, defaults to 0.0):
The dropout ratio for the attention probabilities.
```python
>>> from transformers import Qwen3Model, Qwen3Config
>>> # Initializing a Qwen3 style configuration
>>> configuration = Qwen3Config()
>>> # Initializing a model from the Qwen3-8B style configuration
>>> model = Qwen3Model(configuration)
>>> # Accessing the model configuration
>>> configuration = model.config
```"""
model_type = "qwen3"
keys_to_ignore_at_inference = ["past_key_values"]
def __init__(
self,
vocab_size=151936,
hidden_size=4096,
intermediate_size=22016,
num_hidden_layers=32,
num_attention_heads=32,
num_key_value_heads=32,
head_dim=128,
hidden_act="silu",
max_position_embeddings=32768,
initializer_range=0.02,
rms_norm_eps=1e-6,
use_cache=True,
tie_word_embeddings=False,
rope_theta=10000.0,
rope_scaling=None,
attention_bias=False,
use_sliding_window=False,
sliding_window=4096,
max_window_layers=28,
attention_dropout=0.0,
dpo_config=None,
use_fused_head_and_loss_fn=False,
**kwargs,
):
self.vocab_size = vocab_size
self.max_position_embeddings = max_position_embeddings
self.hidden_size = hidden_size
self.intermediate_size = intermediate_size
self.num_hidden_layers = num_hidden_layers
self.num_attention_heads = num_attention_heads
self.use_sliding_window = use_sliding_window
self.sliding_window = sliding_window # we check `use_sliding_window` in the modeling code
self.max_window_layers = max_window_layers
# for backward compatibility
if num_key_value_heads is None:
num_key_value_heads = num_attention_heads
self.num_key_value_heads = num_key_value_heads
self.head_dim = head_dim
self.hidden_act = hidden_act
self.initializer_range = initializer_range
self.rms_norm_eps = rms_norm_eps
self.use_cache = use_cache
self.rope_theta = rope_theta
self.rope_scaling = rope_scaling
self.attention_bias = attention_bias
self.attention_dropout = attention_dropout
# Validate the correctness of rotary position embeddings parameters
# BC: if there is a 'type' field, move it to 'rope_type'.
if self.rope_scaling is not None and "type" in self.rope_scaling:
self.rope_scaling["rope_type"] = self.rope_scaling["type"]
# rope_config_validation(self)
self.dpo_config = dpo_config
self.use_fused_head_and_loss_fn = use_fused_head_and_loss_fn
super().__init__(
tie_word_embeddings=tie_word_embeddings,
**kwargs,
)
__all__ = ["Qwen3Config"]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,364 @@
# Copyright (c) 2025 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.
from typing import OrderedDict
import paddle
import paddle.distributed.fleet as fleet
import paddle.nn as nn
from paddle.distributed.fleet.meta_parallel import (
LayerDesc,
PipelineLayer,
SharedLayerDesc,
)
from paddle.distributed.fleet.recompute.recompute import recompute
from paddlenlp.transformers.refined_recompute import get_skip_recompute_ops
from paddlenlp.transformers.refined_recompute import recompute as rr_recompute
from ...utils.tools import get_env_device
from ..dpo_criterion import DPOCriterion
from ..model_utils import PipelinePretrainedModel
from .modeling import (
Qwen3Config,
Qwen3DecoderLayer,
Qwen3LMHead,
Qwen3Model,
Qwen3PretrainedModel,
Qwen3PretrainingCriterion,
Qwen3RMSNorm,
)
__all__ = [
"Qwen3ForCausalLMPipe",
]
def parse_args(args):
if isinstance(args, tuple):
if len(args) == 4:
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = args
elif len(args) == 3:
hidden_states, attention_mask, attn_mask_startend_row_indices = args
position_ids = None
elif len(args) == 2:
hidden_states, attention_mask = args
attn_mask_startend_row_indices, position_ids = None, None
else:
hidden_states = args
attention_mask, attn_mask_startend_row_indices, position_ids = None, None, None
if position_ids is not None:
position_ids.stop_gradient = True
if attention_mask is not None:
attention_mask.stop_gradient = True
if attn_mask_startend_row_indices is not None:
attn_mask_startend_row_indices.stop_gradient = True
return hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids
def return_args(hidden_states, attention_mask=None, attn_mask_startend_row_indices=None, position_ids=None):
ret = (hidden_states,)
if attention_mask is not None:
ret += (attention_mask.clone(),)
if attn_mask_startend_row_indices is not None:
ret += (attn_mask_startend_row_indices.clone(),)
if position_ids is not None:
ret += (position_ids.clone(),)
if len(ret) == 1:
ret = ret[0]
return ret
def get_attr(layer, name):
if getattr(layer, name, None) is not None:
return getattr(layer, name, None)
else:
return get_attr(layer._layer, name)
class Qwen3EmbeddingPipe(nn.Layer):
"""Extends QWenEmbeddings to forward attention_mask through the pipeline."""
def __init__(self, config: Qwen3Config):
super(Qwen3EmbeddingPipe, self).__init__()
self.config = config
self.sequence_parallel = config.sequence_parallel
self.hidden_size = config.hidden_size
if config.tensor_parallel_degree > 1 and config.vocab_size % config.tensor_parallel_degree == 0:
self.embed_tokens = fleet.meta_parallel.VocabParallelEmbedding(
config.vocab_size,
config.hidden_size,
weight_attr=paddle.ParamAttr(initializer=nn.initializer.XavierNormal()),
)
else:
self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
@property
def embedding_weight(self):
return get_attr(self.embed_tokens, "weight")
def forward(self, args):
"""_summary_
Args:
input (_type_): _description_
Returns:
_type_: _description_
"""
input_ids, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
input_embeds = self.embed_tokens(input_ids)
if self.config.sequence_parallel:
from paddlenlp.transformers import ScatterOp
# [bs, seq_len, num_head * head_dim] -> [bs * seq_len, num_head * head_dim]
bs, seq_len, hidden_size = input_embeds.shape
input_embeds = paddle.reshape_(input_embeds, [bs * seq_len, hidden_size])
# [seq_len * bs / n, num_head * head_dim] (n is mp parallelism)
input_embeds = ScatterOp.apply(input_embeds)
batch_size, seq_length = input_ids.shape
if attention_mask is not None:
assert (
attn_mask_startend_row_indices is None
), "attention_mask and attn_mask_startend_row_indices can not be set at same time"
attention_mask = Qwen3Model._prepare_decoder_attention_mask(
attention_mask, (batch_size, seq_length), 0, input_embeds.dtype
)
attention_mask.stop_gradient = True
if get_env_device() == "npu":
attention_mask = attention_mask.astype("bool")
elif get_env_device() == "npu":
attention_mask = paddle.tril(paddle.ones((seq_length, seq_length), dtype="bool"))
attention_mask.stop_gradient = True
return return_args(input_embeds, attention_mask, attn_mask_startend_row_indices, position_ids)
class Qwen3DecoderLayerPipe(Qwen3DecoderLayer):
def forward(self, args):
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
has_gradient = not hidden_states.stop_gradient
if attention_mask is not None and attention_mask.dtype == paddle.int32:
attention_mask, attn_mask_startend_row_indices, position_ids = (
None,
attention_mask,
attn_mask_startend_row_indices,
)
elif attention_mask is not None and attention_mask.dtype == paddle.int64:
attention_mask, attn_mask_startend_row_indices, position_ids = None, None, attention_mask
elif attn_mask_startend_row_indices is not None and attn_mask_startend_row_indices.dtype == paddle.int64:
attn_mask_startend_row_indices, position_ids = None, attn_mask_startend_row_indices
if self.enable_recompute and self.config.recompute_granularity == "full" and has_gradient:
recompute_fn = rr_recompute if any(self.skip_recompute_ops.values()) else recompute
if attention_mask is not None or attn_mask_startend_row_indices is not None:
hidden_states = recompute_fn(
super().forward,
hidden_states,
position_ids=position_ids,
attention_mask=attention_mask,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
use_reentrant=False,
)
else:
# for pretrain
hidden_states = recompute_fn(
super().forward,
hidden_states,
position_ids=position_ids,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
use_reentrant=self.config.recompute_use_reentrant,
)
else:
hidden_states = super().forward(
hidden_states,
position_ids=position_ids,
attention_mask=attention_mask,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
)
return return_args(hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids)
class Qwen3RMSNormPipe(nn.Layer):
def __init__(self, config):
super().__init__()
self.norm = Qwen3RMSNorm(config)
def forward(self, args):
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
return self.norm(hidden_states)
class Qwen3LMHeadPipe(Qwen3LMHead):
def __init__(self, config, transpose_y=False):
super(Qwen3LMHeadPipe, self).__init__(config, transpose_y=transpose_y)
@property
def embedding_weight(self):
return get_attr(self, "weight")
class Qwen3ForCausalLMPipe(PipelinePretrainedModel, PipelineLayer):
"""QWenForPretraining adapted for pipeline parallelism.
The largest change is flattening the QWenModel class so we can express it as a
sequence of layers including embedding, transformer layers, and output.
"""
config_class = Qwen3Config
_get_tensor_parallel_mappings = Qwen3PretrainedModel._get_tensor_parallel_mappings
_init_weights = Qwen3PretrainedModel._init_weights
_keys_to_ignore_on_load_unexpected = Qwen3PretrainedModel._keys_to_ignore_on_load_unexpected
_get_model_flops = Qwen3PretrainedModel._get_model_flops
_get_hardware_flops = Qwen3PretrainedModel._get_hardware_flops
_tied_weights_keys = ["lm_head.weight"]
# DONOT Add base_model_prefix !!!!
@classmethod
def _prepare_pipeline_inputs_func(cls, inputs):
first_stage_keys = ["input_ids", "attention_mask", "attn_mask_startend_row_indices", "position_ids"]
last_stage_keys = ["labels"]
def get_expected_keys(inputs, keys):
ret = tuple([inputs.pop(k) if k in inputs else None for k in keys])
if len(ret) == 1:
ret = ret[0]
return ret
if type(inputs) is dict or type(inputs) is OrderedDict:
return [
get_expected_keys(inputs, first_stage_keys),
get_expected_keys(inputs, last_stage_keys),
]
keys = list(inputs[0].keys())
inputs_batch = {key: [data.pop(key) for data in inputs] for key in keys}
return [
get_expected_keys(inputs_batch, first_stage_keys),
get_expected_keys(inputs_batch, last_stage_keys),
]
def __init__(self, config: Qwen3Config):
self.config = config
# Note that we will actually perform a recompute only if both enable_recompute and layerwise_recompute are set to True
# Enable_recompute defaults to False and is controlled by Trainer
self.enable_recompute = False
self.recompute_granularity = self.config.recompute_granularity
self.pp_recompute_interval = self.config.pp_recompute_interval
self.no_recompute_layers = config.no_recompute_layers if config.no_recompute_layers is not None else []
if self.recompute_granularity == "full":
assert len(self.no_recompute_layers) == 0, "for pp with full recompute, no_recompute_layers is not support"
virtual_pp_degree = getattr(self.config, "virtual_pp_degree", 1)
def get_hcg():
return fleet.get_hybrid_communicate_group()
hcg = get_hcg()
tensor_parallel_degree = max(hcg.get_model_parallel_world_size(), 1)
tensor_parallel_rank = max(hcg.get_model_parallel_rank(), 0)
# TODO: fix tensor_parallel_degree rewrite in here
config.tensor_parallel_degree = tensor_parallel_degree
config.tensor_parallel_rank = tensor_parallel_rank
if config.tie_word_embeddings:
self.add_sequential_layer(
SharedLayerDesc(
"model_shared_weight", Qwen3EmbeddingPipe, shared_weight_attr="embedding_weight", config=config
),
"model",
)
else:
self.add_sequential_layer(LayerDesc(Qwen3EmbeddingPipe, config=config), "model")
for i in range(config.num_hidden_layers):
self.add_sequential_layer(
LayerDesc(
Qwen3DecoderLayerPipe,
config=config,
layerwise_recompute=i not in self.no_recompute_layers,
skip_recompute_ops=get_skip_recompute_ops(config, i),
),
f"model.layers.{i}",
)
self.add_sequential_layer(LayerDesc(Qwen3RMSNormPipe, config=config), "model")
if config.tie_word_embeddings:
self.add_sequential_layer(
SharedLayerDesc(
"model_shared_weight",
Qwen3LMHeadPipe,
shared_weight_attr="embedding_weight",
config=config,
**{"transpose_y": True},
),
"lm_head",
)
else:
self.add_sequential_layer(LayerDesc(Qwen3LMHeadPipe, config=config), "lm_head")
recompute_interval = 0
if self.enable_recompute and self.recompute_granularity == "full":
assert self.config.pp_recompute_interval <= config.num_hidden_layers // (
virtual_pp_degree * get_hcg().topology().get_dim_size("pipe")
), "pp recompute interval should smaller than num layers of each pp chunk"
recompute_interval = self.config.pp_recompute_interval
seg_method = "layer:Qwen3DecoderLayer"
if config.num_hidden_layers % get_hcg().topology().get_dim_size("pipe") != 0:
seg_method = "uniform"
PipelineLayer.__init__(
self,
layers=self.get_sequential_layers(),
loss_fn=self.get_loss_fn(config),
topology=get_hcg().topology(),
seg_method=seg_method,
recompute_interval=recompute_interval,
recompute_ctx={
"mp_group": get_hcg().get_model_parallel_group(),
"offload": False,
"partition": False,
},
num_virtual_pipeline_stages=virtual_pp_degree,
)
# You should call init here, since there is a diamond inheritance problem
self.apply(self._init_weights)
# DON'T init PipelinePretrainedModel
# PipelinePretrainedModel.__init__(self.super(), config=config)
def get_loss_fn(self, config):
if config.dpo_config is not None:
return DPOCriterion(config, use_infohub=True)
else:
return Qwen3PretrainingCriterion(config)

View File

@@ -0,0 +1,17 @@
# Copyright (c) 2024 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.
from .configuration import *
from .modeling import *
from .modeling_pp import *

View File

@@ -0,0 +1,219 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
# coding=utf-8
# Copyright 2024 The Qwen team, Alibaba Group and the HuggingFace Inc. team. 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.
"""Qwen3MoE model configuration"""
from ..configuration_utils import PretrainedConfig
class Qwen3MoeConfig(PretrainedConfig):
r"""
This is the configuration class to store the configuration of a [`Qwen3MoeModel`]. It is used to instantiate a
Qwen3MoE model according to the specified arguments, defining the model architecture. Instantiating a configuration
with the defaults will yield a similar configuration to that of [Qwen/Qwen3-MoE-15B-A2B](https://huggingface.co/Qwen/Qwen3-15B-A2B).
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
documentation from [`PretrainedConfig`] for more information.
Args:
vocab_size (`int`, *optional*, defaults to 151936):
Vocabulary size of the Qwen3MoE model. Defines the number of different tokens that can be represented by the
`inputs_ids` passed when calling [`Qwen3MoeModel`]
hidden_size (`int`, *optional*, defaults to 2048):
Dimension of the hidden representations.
intermediate_size (`int`, *optional*, defaults to 6144):
Dimension of the MLP representations.
num_hidden_layers (`int`, *optional*, defaults to 24):
Number of hidden layers in the Transformer encoder.
num_attention_heads (`int`, *optional*, defaults to 32):
Number of attention heads for each attention layer in the Transformer encoder.
num_key_value_heads (`int`, *optional*, defaults to 4):
This is the number of key_value heads that should be used to implement Grouped Query Attention. If
`num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
`num_key_value_heads=1` the model will use Multi Query Attention (MQA) otherwise GQA is used. When
converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
by meanpooling all the original heads within that group. For more details checkout [this
paper](https://arxiv.org/pdf/2305.13245.pdf). If it is not specified, will default to `32`.
hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
The non-linear activation function (function or string) in the decoder.
max_position_embeddings (`int`, *optional*, defaults to 32768):
The maximum sequence length that this model might ever be used with.
initializer_range (`float`, *optional*, defaults to 0.02):
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
rms_norm_eps (`float`, *optional*, defaults to 1e-06):
The epsilon used by the rms normalization layers.
use_cache (`bool`, *optional*, defaults to `True`):
Whether or not the model should return the last key/values attentions (not used by all models). Only
relevant if `config.is_decoder=True`.
tie_word_embeddings (`bool`, *optional*, defaults to `False`):
Whether the model's input and output word embeddings should be tied.
rope_theta (`float`, *optional*, defaults to 10000.0):
The base period of the RoPE embeddings.
rope_scaling (`Dict`, *optional*):
Dictionary containing the scaling configuration for the RoPE embeddings. NOTE: if you apply new rope type
and you expect the model to work on longer `max_position_embeddings`, we recommend you to update this value
accordingly.
Expected contents:
`rope_type` (`str`):
The sub-variant of RoPE to use. Can be one of ['default', 'linear', 'dynamic', 'yarn', 'longrope',
'llama3'], with 'default' being the original RoPE implementation.
`factor` (`float`, *optional*):
Used with all rope types except 'default'. The scaling factor to apply to the RoPE embeddings. In
most scaling types, a `factor` of x will enable the model to handle sequences of length x *
original maximum pre-trained length.
`original_max_position_embeddings` (`int`, *optional*):
Used with 'dynamic', 'longrope' and 'llama3'. The original max position embeddings used during
pretraining.
`attention_factor` (`float`, *optional*):
Used with 'yarn' and 'longrope'. The scaling factor to be applied on the attention
computation. If unspecified, it defaults to value recommended by the implementation, using the
`factor` field to infer the suggested value.
`beta_fast` (`float`, *optional*):
Only used with 'yarn'. Parameter to set the boundary for extrapolation (only) in the linear
ramp function. If unspecified, it defaults to 32.
`beta_slow` (`float`, *optional*):
Only used with 'yarn'. Parameter to set the boundary for interpolation (only) in the linear
ramp function. If unspecified, it defaults to 1.
`short_factor` (`List[float]`, *optional*):
Only used with 'longrope'. The scaling factor to be applied to short contexts (<
`original_max_position_embeddings`). Must be a list of numbers with the same length as the hidden
size divided by the number of attention heads divided by 2
`long_factor` (`List[float]`, *optional*):
Only used with 'longrope'. The scaling factor to be applied to long contexts (<
`original_max_position_embeddings`). Must be a list of numbers with the same length as the hidden
size divided by the number of attention heads divided by 2
`low_freq_factor` (`float`, *optional*):
Only used with 'llama3'. Scaling factor applied to low frequency components of the RoPE
`high_freq_factor` (`float`, *optional*):
Only used with 'llama3'. Scaling factor applied to high frequency components of the RoPE
attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`):
Whether to use a bias in the query, key, value and output projection layers during self-attention.
use_sliding_window (`bool`, *optional*, defaults to `False`):
Whether to use sliding window attention.
sliding_window (`int`, *optional*, defaults to 4096):
Sliding window attention (SWA) window size. If not specified, will default to `4096`.
max_window_layers (`int`, *optional*, defaults to 28):
The number of layers that use SWA (Sliding Window Attention). The bottom layers use SWA while the top use full attention.
attention_dropout (`float`, *optional*, defaults to 0.0):
The dropout ratio for the attention probabilities.
decoder_sparse_step (`int`, *optional*, defaults to 1):
The frequency of the MoE layer.
moe_intermediate_size (`int`, *optional*, defaults to 768):
Intermediate size of the routed expert.
num_experts_per_tok (`int`, *optional*, defaults to 8):
Number of selected experts.
num_experts (`int`, *optional*, defaults to 128):
Number of routed experts.
norm_topk_prob (`bool`, *optional*, defaults to `False`):
Whether to normalize the topk probabilities.
output_router_logits (`bool`, *optional*, defaults to `False`):
Whether or not the router logits should be returned by the model. Enabling this will also
allow the model to output the auxiliary loss, including load balancing loss and router z-loss.
router_aux_loss_coef (`float`, *optional*, defaults to 0.001):
The aux loss factor for the total loss.
mlp_only_layers (`List[int]`, *optional*, defaults to `[]`):
Indicate which layers use Qwen3MoeMLP rather than Qwen3MoeSparseMoeBlock
The list contains layer index, from 0 to num_layers-1 if we have num_layers layers
If `mlp_only_layers` is empty, `decoder_sparse_step` is used to determine the sparsity.
```python
>>> from transformers import Qwen3MoeModel, Qwen3MoeConfig
>>> # Initializing a Qwen3MoE style configuration
>>> configuration = Qwen3MoeConfig()
>>> # Initializing a model from the Qwen3-15B-A2B" style configuration
>>> model = Qwen3MoeModel(configuration)
>>> # Accessing the model configuration
>>> configuration = model.config
```"""
model_type = "qwen3_moe"
keys_to_ignore_at_inference = ["past_key_values"]
def __init__(
self,
vocab_size=151936,
hidden_size=2048,
intermediate_size=6144,
num_hidden_layers=24,
num_attention_heads=32,
num_key_value_heads=4,
hidden_act="silu",
max_position_embeddings=32768,
initializer_range=0.02,
rms_norm_eps=1e-6,
use_cache=True,
tie_word_embeddings=False,
rope_theta=10000.0,
rope_scaling=None,
attention_bias=False,
use_sliding_window=False,
sliding_window=4096,
max_window_layers=28,
attention_dropout=0.0,
decoder_sparse_step=1,
moe_intermediate_size=768,
num_experts_per_tok=8,
num_experts=128,
norm_topk_prob=False,
output_router_logits=False,
router_aux_loss_coef=0.001,
mlp_only_layers=None,
**kwargs,
):
self.vocab_size = vocab_size
self.max_position_embeddings = max_position_embeddings
self.hidden_size = hidden_size
self.intermediate_size = intermediate_size
self.num_hidden_layers = num_hidden_layers
self.num_attention_heads = num_attention_heads
self.use_sliding_window = use_sliding_window
self.sliding_window = sliding_window if use_sliding_window else None
self.max_window_layers = max_window_layers
self.num_key_value_heads = num_key_value_heads
self.hidden_act = hidden_act
self.initializer_range = initializer_range
self.rms_norm_eps = rms_norm_eps
self.use_cache = use_cache
self.rope_theta = rope_theta
self.rope_scaling = rope_scaling
self.attention_bias = attention_bias
self.attention_dropout = attention_dropout
# Validate the correctness of rotary position embeddings parameters
# BC: if there is a 'type' field, move it to 'rope_type'.
if self.rope_scaling is not None and "type" in self.rope_scaling:
self.rope_scaling["rope_type"] = self.rope_scaling["type"]
# MoE arguments
self.decoder_sparse_step = decoder_sparse_step
self.moe_intermediate_size = moe_intermediate_size
self.num_experts_per_tok = num_experts_per_tok
self.num_experts = num_experts
self.norm_topk_prob = norm_topk_prob
self.output_router_logits = output_router_logits
self.router_aux_loss_coef = router_aux_loss_coef
self.mlp_only_layers = [] if mlp_only_layers is None else mlp_only_layers
super().__init__(
tie_word_embeddings=tie_word_embeddings,
**kwargs,
)
__all__ = ["Qwen3MoeConfig"]

View File

@@ -0,0 +1,996 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
# Copyright 2025 The Qwen team, Alibaba Group and the HuggingFace Inc. team. 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.
"""Paddle Qwen3Moe model."""
from __future__ import annotations
import math
from functools import partial
from typing import List, Optional, Tuple, Union
import paddle
import paddle.distributed.fleet.meta_parallel as mpu
import paddle.nn.functional as F
from paddle import Tensor, nn
from paddle.distributed.fleet.meta_parallel import get_rng_state_tracker
from paddle.distributed.fleet.utils import recompute
from .. import linear_utils
from ..conversion_utils import StateDictNameMapping, init_name_mappings
from ..llama.modeling import get_use_casual_mask
from ..model_outputs import MoECausalLMOutputWithPast, MoEModelOutputWithPast
from ..model_utils import PretrainedModel, register_base_model
from ..moe_layer import MoELayer
from ..utils import logger
from .configuration import Qwen3MoeConfig
try:
from paddle.distributed.fleet.utils.sequence_parallel_utils import ScatterOp
except ImportError:
pass
__all__ = [
"Qwen3MoeModel",
"Qwen3MoePretrainedModel",
"Qwen3MoeForCausalLM",
"Qwen3MoePretrainingCriterion",
]
from ..qwen2_moe.modeling import Qwen2MoeGate, Qwen2MoeMLP, load_balancing_loss_func
from ..qwen3.modeling import (
Qwen3Attention,
Qwen3LMHead,
Qwen3PretrainingCriterion,
Qwen3RMSNorm,
_expand_2d_mask,
_make_causal_mask,
is_casual_mask,
)
class Qwen3MoeRMSNorm(Qwen3RMSNorm):
pass
class Qwen3MoeMLP(Qwen2MoeMLP):
pass
class Qwen3MoeAttention(Qwen3Attention):
pass
class Qwen3MoeGate(Qwen2MoeGate):
pass
class ExpertParallelQwen3MoeSparseMoeBlock(MoELayer):
def __init__(self, config: Qwen3MoeConfig):
gate = Qwen3MoeGate(
config,
config.num_experts,
config.hidden_size,
top_k=config.num_experts_per_tok,
drop_tokens=False,
norm_topk_prob=config.norm_topk_prob,
)
super().__init__(
config,
moe_num_experts=config.num_experts,
expert_class=Qwen3MoeMLP,
expert_kwargs={"config": config},
gate=gate,
capacity=2.0,
)
self.top_k = config.num_experts_per_tok
self.norm_topk_prob = config.norm_topk_prob
def forward(self, hidden_states):
final_hidden_states, l_aux, l_zloss = super().forward(hidden_states)
return final_hidden_states, l_aux
class Qwen3MoeSparseMoeBlock(nn.Layer):
def __init__(self, config):
super().__init__()
self.num_experts = config.num_experts
self.top_k = config.num_experts_per_tok
self.norm_topk_prob = config.norm_topk_prob
# gating
self.gate = nn.Linear(config.hidden_size, config.num_experts, bias_attr=False)
self.experts = nn.LayerList([Qwen3MoeMLP(config) for _ in range(self.num_experts)])
def forward(self, hidden_states: paddle.Tensor) -> paddle.Tensor:
""" """
batch_size, sequence_length, hidden_dim = hidden_states.shape
hidden_states = hidden_states.view([-1, hidden_dim])
# router_logits: (batch * sequence_length, n_experts)
router_logits = self.gate(hidden_states)
routing_weights = F.softmax(router_logits, axis=1, dtype=paddle.float32)
# (batch * sequence_length, topk)
routing_weights, selected_experts = paddle.topk(routing_weights, self.top_k, axis=-1)
if self.norm_topk_prob: # only diff with mixtral sparse moe block!
routing_weights /= routing_weights.sum(axis=-1, keepdim=True)
# we cast back to the input dtype
routing_weights = routing_weights.to(hidden_states.dtype)
final_hidden_states = paddle.zeros((batch_size * sequence_length, hidden_dim), dtype=hidden_states.dtype)
# One hot encode the selected experts to create an expert mask
# this will be used to easily index which expert is going to be sollicitated
expert_mask = paddle.nn.functional.one_hot(selected_experts, num_classes=self.num_experts).transpose([2, 1, 0])
# [num_experts, topk, bs*seq]
tokens_per_expert = expert_mask.reshape([expert_mask.shape[0], -1]).sum(axis=-1)
# Loop over all available experts in the model and perform the computation on each expert
for expert_idx in range(self.num_experts):
if tokens_per_expert[expert_idx] <= 0.1:
continue
expert_layer = self.experts[expert_idx]
top_x, idx = paddle.where(expert_mask[expert_idx])
# Index the correct hidden states and compute the expert hidden state for
# the current expert. We need to make sure to multiply the output hidden
# states by `routing_weights` on the corresponding tokens (top-1 and top-2)
current_state = hidden_states[idx, None].reshape([-1, hidden_dim])
current_hidden_states = expert_layer(current_state) * routing_weights[idx, top_x].unsqueeze(-1)
final_hidden_states.index_add_(
index=idx.reshape([-1]), axis=0, value=current_hidden_states.to(hidden_states.dtype)
)
final_hidden_states = final_hidden_states.reshape([batch_size, sequence_length, hidden_dim])
return final_hidden_states, router_logits
class Qwen3MoeDecoderLayer(nn.Layer):
def __init__(self, config: Qwen3MoeConfig, layerwise_recompute: bool = False):
super().__init__()
self.config = config
self.self_attn = Qwen3MoeAttention(config, layerwise_recompute)
if config.num_experts > 0:
self.mlp = ExpertParallelQwen3MoeSparseMoeBlock(config)
else:
# num_experts == 0 or this layer is not sparse layer
self.mlp = Qwen3MoeMLP(config)
self.input_layernorm = Qwen3MoeRMSNorm(config)
self.post_attention_layernorm = Qwen3MoeRMSNorm(config)
self.sequence_parallel = config.sequence_parallel
# Note that we will actually perform a recompute only if both enable_recompute and layerwise_recompute are set to True
# Enable_recompute defaults to False and is controlled by Trainer
self.enable_recompute = False
self.layerwise_recompute = layerwise_recompute
self.recompute_granularity = config.recompute_granularity
def forward(
self,
hidden_states: paddle.Tensor,
position_ids: Optional[paddle.Tensor] = None,
attention_mask: Optional[paddle.Tensor] = None,
output_attentions: Optional[bool] = False,
output_router_logits: Optional[bool] = False,
past_key_value: Optional[Tuple[paddle.Tensor]] = None,
use_cache: Optional[bool] = False,
attn_mask_startend_row_indices: Optional[paddle.Tensor] = None,
**kwargs,
) -> Tuple[paddle.Tensor, Optional[Tuple[paddle.Tensor, paddle.Tensor]]]:
"""
Args:
hidden_states (`paddle.Tensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
attention_mask (`paddle.Tensor`, *optional*): attention mask of size
`(batch, sequence_length)` where padding elements are indicated by 0.
output_attentions (`bool`, *optional*):
Whether or not to return the attentions tensors of all attention layers. See `attentions` under
returned tensors for more detail.
output_router_logits (`bool`, *optional*):
Whether or not to return the logits of all the routers. They are useful for computing the router loss, and
should not be returned during inference.
use_cache (`bool`, *optional*):
If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding
(see `past_key_values`).
past_key_value (`Tuple(paddle.Tensor)`, *optional*): cached past key and value projection states
"""
# [bs * seq_len, embed_dim] -> [seq_len * bs / n, embed_dim] (sequence_parallel)
residual = hidden_states
hidden_states = self.input_layernorm(hidden_states)
# Self Attention
has_gradient = not hidden_states.stop_gradient
if (
self.enable_recompute
and self.layerwise_recompute
and has_gradient
and self.recompute_granularity == "full_attn"
):
outputs = recompute(
self.self_attn,
hidden_states,
position_ids,
past_key_value,
attention_mask,
output_attentions,
use_cache,
attn_mask_startend_row_indices,
use_reentrant=self.config.recompute_use_reentrant,
)
else:
outputs = self.self_attn(
hidden_states,
position_ids,
past_key_value,
attention_mask,
output_attentions,
use_cache,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
)
if type(outputs) is tuple:
hidden_states = outputs[0]
else:
hidden_states = outputs
if output_attentions:
self_attn_weights = outputs[1]
if use_cache:
present_key_value = outputs[2 if output_attentions else 1]
hidden_states = residual + hidden_states
# Fully Connected
residual = hidden_states
hidden_states = self.post_attention_layernorm(hidden_states)
hidden_states = self.mlp(hidden_states)
if isinstance(hidden_states, tuple):
hidden_states, router_logits = hidden_states
else:
router_logits = None
hidden_states = residual + hidden_states
outputs = (hidden_states,)
if output_attentions:
outputs += (self_attn_weights,)
if use_cache:
outputs += (present_key_value,)
if output_router_logits:
outputs += (router_logits,)
if type(outputs) is tuple and len(outputs) == 1:
outputs = outputs[0]
return outputs
class Qwen3MoePretrainedModel(PretrainedModel):
config_class = Qwen3MoeConfig
base_model_prefix = "model"
_keys_to_ignore_on_load_unexpected = [r"self_attn.rotary_emb.inv_freq"]
@classmethod
def _get_name_mappings(cls, config: Qwen3MoeConfig) -> list[StateDictNameMapping]:
mappings: list[StateDictNameMapping] = []
model_mappings = [
["embed_tokens.weight"],
["norm.weight"],
]
for layer_index in range(config.num_hidden_layers):
layer_mappings = [
[f"layers.{layer_index}.self_attn.q_proj.weight", None, "transpose"],
[f"layers.{layer_index}.self_attn.k_proj.weight", None, "transpose"],
[f"layers.{layer_index}.self_attn.v_proj.weight", None, "transpose"],
[f"layers.{layer_index}.self_attn.o_proj.weight", None, "transpose"],
[f"layers.{layer_index}.self_attn.rotary_emb.inv_freq"],
[f"layers.{layer_index}.input_layernorm.weight"],
[f"layers.{layer_index}.post_attention_layernorm.weight"],
[f"layers.{layer_index}.self_attn.q_norm.weight"],
[f"layers.{layer_index}.self_attn.k_norm.weight"],
]
model_mappings.extend(layer_mappings)
for expert_idx in range(config.num_experts):
expert_mappings = [
[f"layers.{layer_index}.mlp.experts.{expert_idx}.gate_proj.weight", None, "transpose"],
[f"layers.{layer_index}.mlp.experts.{expert_idx}.down_proj.weight", None, "transpose"],
[f"layers.{layer_index}.mlp.experts.{expert_idx}.up_proj.weight", None, "transpose"],
]
model_mappings.extend(expert_mappings)
model_mappings.append([f"layers.{layer_index}.mlp.gate.weight", None, "transpose"])
init_name_mappings(mappings=model_mappings)
# base-model prefix "Qwen3MoeModel"
if "Qwen3MoeModel" not in config.architectures:
for mapping in model_mappings:
mapping[0] = "model." + mapping[0]
mapping[1] = "model." + mapping[1]
model_mappings.append(["lm_head.weight", "lm_head.weight", "transpose"])
mappings = [StateDictNameMapping(*mapping, index=index) for index, mapping in enumerate(model_mappings)]
return mappings
@classmethod
def _get_tensor_parallel_mappings(cls, config: Qwen3MoeConfig, is_split=True):
from paddlenlp.transformers.conversion_utils import split_or_merge_func
fn = split_or_merge_func(
is_split=is_split,
tensor_parallel_degree=config.tensor_parallel_degree,
tensor_parallel_rank=config.tensor_parallel_rank,
num_attention_heads=config.num_attention_heads,
)
def get_tensor_parallel_split_mappings(num_layers, num_experts):
final_actions = {}
base_actions = {
"lm_head.weight": partial(fn, is_column=True),
# Row Linear
"embed_tokens.weight": partial(fn, is_column=False),
"layers.0.self_attn.o_proj.weight": partial(fn, is_column=False),
}
if not config.vocab_size % config.tensor_parallel_degree == 0:
base_actions.pop("lm_head.weight")
base_actions.pop("embed_tokens.weight")
# Column Linear
base_actions["layers.0.self_attn.q_proj.weight"] = partial(fn, is_column=True)
# if we have enough num_key_value_heads to split, then split it.
if config.num_key_value_heads % config.tensor_parallel_degree == 0:
base_actions["layers.0.self_attn.k_proj.weight"] = partial(fn, is_column=True)
base_actions["layers.0.self_attn.v_proj.weight"] = partial(fn, is_column=True)
for key, action in base_actions.items():
if "layers.0." in key:
for i in range(num_layers):
final_actions[key.replace("layers.0.", f"layers.{i}.")] = action
final_actions[key] = action
# Add tp split for expert params.
base_actions = {
"layers.0.mlp.experts.0.gate_proj.weight": partial(fn, is_column=True),
"layers.0.mlp.experts.0.down_proj.weight": partial(fn, is_column=False),
"layers.0.mlp.experts.0.up_proj.weight": partial(fn, is_column=True),
}
for key, action in base_actions.items():
for i in range(num_layers):
newkey = key.replace("layers.0.", f"layers.{i}.")
for j in range(num_experts):
newkey2 = newkey.replace("experts.0.", f"experts.{j}.")
final_actions[newkey2] = action
# Add tp split for shared expert params.
base_actions = {}
for key, action in base_actions.items():
if "layers.0." in key:
for i in range(num_layers):
final_actions[key.replace("layers.0.", f"layers.{i}.")] = action
final_actions[key] = action
return final_actions
mappings = get_tensor_parallel_split_mappings(config.num_hidden_layers, config.num_experts)
return mappings
@classmethod
def _get_fuse_or_split_param_mappings(cls, config: Qwen3MoeConfig, is_fuse=False):
# return parameter fuse utils
from paddlenlp.transformers.conversion_utils import split_or_fuse_func
fn = split_or_fuse_func(is_fuse=is_fuse)
# last key is fused key, other keys are to be fused.
fuse_qkv_keys = [
(
"layers.0.self_attn.q_proj.weight",
"layers.0.self_attn.k_proj.weight",
"layers.0.self_attn.v_proj.weight",
"layers.0.self_attn.qkv_proj.weight",
),
]
fuse_gate_up_keys = (
"layers.0.mlp.gate_proj.weight",
"layers.0.mlp.up_proj.weight",
"layers.0.mlp.gate_up_fused_proj.weight",
)
num_heads = config.num_attention_heads
num_key_value_heads = getattr(config, "num_key_value_heads", num_heads)
fuse_attention_qkv = getattr(config, "fuse_attention_qkv", False)
fuse_attention_ffn = getattr(config, "fuse_attention_ffn", False)
final_actions = {}
if is_fuse:
if fuse_attention_qkv:
for i in range(config.num_hidden_layers):
for fuse_keys in fuse_qkv_keys:
keys = tuple([key.replace("layers.0.", f"layers.{i}.") for key in fuse_keys])
final_actions[keys] = partial(
fn, is_qkv=True, num_heads=num_heads, num_key_value_heads=num_key_value_heads
)
if fuse_attention_ffn:
for i in range(config.num_hidden_layers):
keys = tuple([key.replace("layers.0.", f"layers.{i}.") for key in fuse_gate_up_keys])
final_actions[keys] = fn
else:
if not fuse_attention_qkv:
for i in range(config.num_hidden_layers):
for fuse_keys in fuse_qkv_keys:
keys = tuple([key.replace("layers.0.", f"layers.{i}.") for key in fuse_keys])
final_actions[keys] = partial(
fn, split_nums=3, is_qkv=True, num_heads=num_heads, num_key_value_heads=num_key_value_heads
)
if not fuse_attention_ffn:
for i in range(config.num_hidden_layers):
keys = tuple([key.replace("layers.0.", f"layers.{i}.") for key in fuse_gate_up_keys])
final_actions[keys] = partial(fn, split_nums=2)
return final_actions
def _init_weights(self, layer):
"""Initialization hook"""
return None
if self.config.tensor_parallel_degree > 1:
rng_tracker = get_rng_state_tracker().rng_state
if isinstance(
layer,
(
nn.Linear,
nn.Embedding,
mpu.VocabParallelEmbedding,
mpu.RowParallelLinear,
mpu.ColumnParallelLinear,
linear_utils.RowSequenceParallelLinear,
linear_utils.ColumnSequenceParallelLinear,
Qwen3MoeLMHead,
),
):
# In the dygraph mode, use the `set_value` to reset the parameter directly,
# and reset the `state_dict` to update parameter in static mode.
if isinstance(layer.weight, paddle.Tensor):
if layer.weight.is_distributed:
with rng_tracker():
layer.weight.set_value(
paddle.tensor.normal(
mean=0.0,
std=self.config.initializer_range
if hasattr(self.config, "initializer_range")
else self.model.config.initializer_range,
shape=layer.weight.shape,
)
)
else:
layer.weight.set_value(
paddle.tensor.normal(
mean=0.0,
std=self.config.initializer_range
if hasattr(self.config, "initializer_range")
else self.model.config.initializer_range,
shape=layer.weight.shape,
)
)
if hasattr(layer, "bias") and isinstance(layer.bias, paddle.Tensor):
layer.bias.set_value(paddle.zeros_like(layer.bias))
# Layer.apply is DFS https://github.com/PaddlePaddle/Paddle/blob/a6f5021fcc58b21f4414bae6bf4731ef6971582c/python/paddle/nn/layer/layers.py#L527-L530
# sublayer is init first
# scale RowParallelLinear weight
with paddle.no_grad():
if isinstance(layer, Qwen3MoeMLP):
factor = 1 / math.sqrt(2 * self.config.num_hidden_layers)
layer.down_proj.weight.scale_(factor)
if isinstance(layer, Qwen3MoeAttention):
factor = 1 / math.sqrt(2 * self.config.num_hidden_layers)
layer.o_proj.weight.scale_(factor)
@register_base_model
class Qwen3MoeModel(Qwen3MoePretrainedModel):
"""
Transformer decoder consisting of *config.num_hidden_layers* layers. Each layer is a [`Qwen3MoeDecoderLayer`]
Args:
config: Qwen3MoeConfig
"""
def __init__(self, config: Qwen3MoeConfig):
super().__init__(config)
self.padding_idx = config.pad_token_id
self.vocab_size = config.vocab_size
self.hidden_size = config.hidden_size
self.sequence_parallel = config.sequence_parallel
self.recompute_granularity = config.recompute_granularity
self.no_recompute_layers = config.no_recompute_layers if config.no_recompute_layers is not None else []
# Recompute defaults to False and is controlled by Trainer
self.enable_recompute = False
if config.tensor_parallel_degree > 1 and config.vocab_size % config.tensor_parallel_degree == 0:
self.embed_tokens = mpu.VocabParallelEmbedding(
self.vocab_size,
self.hidden_size,
weight_attr=paddle.ParamAttr(initializer=nn.initializer.XavierNormal()),
)
else:
self.embed_tokens = nn.Embedding(
self.vocab_size,
self.hidden_size,
)
self.layers = nn.LayerList(
[
Qwen3MoeDecoderLayer(
config=config,
layerwise_recompute=layer_idx not in self.no_recompute_layers,
)
for layer_idx in range(config.num_hidden_layers)
]
)
self.norm = Qwen3MoeRMSNorm(config)
def get_input_embeddings(self):
return self.embed_tokens
def set_input_embeddings(self, value):
self.embed_tokens = value
@staticmethod
def _prepare_decoder_attention_mask(attention_mask, input_shape, past_key_values_length, dtype):
if attention_mask is not None:
# [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
if len(attention_mask.shape) == 2:
expanded_attn_mask = _expand_2d_mask(attention_mask, dtype, tgt_length=input_shape[-1])
# For decoding phase in generation, seq_length = 1, we don't need to add causal mask
if input_shape[-1] > 1:
combined_attention_mask = _make_causal_mask(
input_shape,
past_key_values_length=past_key_values_length,
)
expanded_attn_mask = expanded_attn_mask & combined_attention_mask
# [bsz, seq_len, seq_len] -> [bsz, 1, seq_len, seq_len]
elif len(attention_mask.shape) == 3:
expanded_attn_mask = attention_mask.unsqueeze(1).astype("bool")
# if attention_mask is already 4-D, do nothing
else:
expanded_attn_mask = attention_mask
else:
expanded_attn_mask = _make_causal_mask(
input_shape,
past_key_values_length=past_key_values_length,
)
# Convert bool attention_mask to float attention mask, which will be added to attention_scores later
expanded_attn_mask = paddle.where(expanded_attn_mask.to("bool"), 0.0, paddle.finfo(dtype).min).astype(dtype)
return expanded_attn_mask
@paddle.jit.not_to_static
def recompute_training_full(
self,
layer_module: nn.Layer,
hidden_states: Tensor,
position_ids: Optional[Tensor],
attention_mask: Tensor,
output_attentions: bool,
output_router_logits: bool,
past_key_value: Tensor,
use_cache: bool,
attn_mask_startend_row_indices=None,
):
def create_custom_forward(module):
def custom_forward(*inputs):
return module(*inputs)
return custom_forward
hidden_states = recompute(
create_custom_forward(layer_module),
hidden_states,
position_ids,
attention_mask,
output_attentions,
output_router_logits,
past_key_value,
use_cache,
attn_mask_startend_row_indices,
use_reentrant=self.config.recompute_use_reentrant,
)
return hidden_states
def forward(
self,
input_ids: paddle.Tensor = None,
position_ids: Optional[paddle.Tensor] = None,
attention_mask: Optional[paddle.Tensor] = None,
inputs_embeds: Optional[paddle.Tensor] = None,
use_cache: Optional[bool] = None,
past_key_values: Optional[List[paddle.Tensor]] = None,
output_attentions: Optional[bool] = None,
output_hidden_states: Optional[bool] = None,
output_router_logits: Optional[bool] = None,
return_dict: Optional[bool] = None,
attn_mask_startend_row_indices=None,
**kwargs,
) -> Union[Tuple, MoEModelOutputWithPast]:
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
output_router_logits = (
output_router_logits if output_router_logits is not None else self.config.output_router_logits
)
output_hidden_states = (
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
)
use_cache = use_cache if use_cache is not None else self.config.use_cache
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
# retrieve input_ids and inputs_embeds
if input_ids is not None and inputs_embeds is not None:
raise ValueError("You cannot specify both decoder_input_ids and decoder_inputs_embeds at the same time")
elif input_ids is not None:
batch_size, seq_length = input_ids.shape
elif inputs_embeds is not None:
batch_size, seq_length, _ = inputs_embeds.shape
else:
raise ValueError("You have to specify either decoder_input_ids or decoder_inputs_embeds")
if past_key_values is None:
past_key_values = tuple([None] * len(self.layers))
# NOTE: to make cache can be clear in-time
past_key_values = list(past_key_values)
seq_length_with_past = seq_length
cache_length = 0
if past_key_values[0] is not None:
cache_length = past_key_values[0][0].shape[1]
seq_length_with_past += cache_length
if inputs_embeds is None:
# [bs, seq_len, dim]
inputs_embeds = self.embed_tokens(input_ids)
if self.sequence_parallel:
# [bs, seq_len, num_head * head_dim] -> [bs * seq_len, num_head * head_dim]
bs, seq_len, hidden_size = inputs_embeds.shape
inputs_embeds = paddle.reshape_(inputs_embeds, [bs * seq_len, hidden_size])
# [seq_len * bs / n, num_head * head_dim] (n is mp parallelism)
inputs_embeds = ScatterOp.apply(inputs_embeds)
# embed positions
if attn_mask_startend_row_indices is not None or get_use_casual_mask():
attention_mask = None
else:
# [bs, seq_len]
attention_mask = (
paddle.ones((batch_size, seq_length_with_past), dtype=paddle.bool)
if attention_mask is None
else attention_mask
)
attention_mask = self._prepare_decoder_attention_mask(
attention_mask, (batch_size, seq_length), cache_length, inputs_embeds.dtype
) # [bs, 1, seq_len, seq_len]
if self.config.use_flash_attention:
attention_mask = None if is_casual_mask(attention_mask) else attention_mask
if position_ids is None:
position_ids = paddle.arange(seq_length, dtype="int64").expand((batch_size, seq_length))
hidden_states = inputs_embeds
# decoder layers
all_hidden_states = () if output_hidden_states else None
all_self_attns = () if output_attentions else None
all_router_logits = () if output_router_logits else None
next_decoder_cache = () if use_cache else None
for idx, (decoder_layer) in enumerate(self.layers):
if output_hidden_states:
all_hidden_states += (hidden_states,)
past_key_value = past_key_values[idx] if past_key_values is not None else None
has_gradient = not hidden_states.stop_gradient
if (
self.enable_recompute
and idx not in self.no_recompute_layers
and has_gradient
and self.recompute_granularity == "full"
):
layer_outputs = self.recompute_training_full(
decoder_layer,
hidden_states,
position_ids,
attention_mask,
output_attentions,
output_router_logits,
past_key_value,
use_cache,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
)
else:
layer_outputs = decoder_layer(
hidden_states,
position_ids,
attention_mask,
output_attentions,
output_router_logits,
past_key_value,
use_cache,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
)
# NOTE: clear outdate cache after it has been used for memory saving
past_key_value = past_key_values[idx] = None
if type(layer_outputs) is tuple:
hidden_states = layer_outputs[0]
else:
hidden_states = layer_outputs
if output_attentions:
all_self_attns += (layer_outputs[1],)
if use_cache:
next_decoder_cache += (layer_outputs[2 if output_attentions else 1],)
if output_router_logits:
all_router_logits += (layer_outputs[-1],)
hidden_states = self.norm(hidden_states)
# add hidden states from the last decoder layer
if output_hidden_states:
all_hidden_states += (hidden_states,)
next_cache = next_decoder_cache if use_cache else None
if not return_dict:
return tuple(
v
for v in [hidden_states, next_cache, all_hidden_states, all_self_attns, all_router_logits]
if v is not None
)
return MoEModelOutputWithPast(
last_hidden_state=hidden_states,
past_key_values=next_cache,
hidden_states=all_hidden_states,
attentions=all_self_attns,
router_logits=all_router_logits,
)
class Qwen3MoePretrainingCriterion(Qwen3PretrainingCriterion):
pass
class Qwen3MoeLMHead(Qwen3LMHead):
pass
class Qwen3MoeForCausalLM(Qwen3MoePretrainedModel):
enable_to_static_method = True
_tied_weights_keys = ["lm_head.weight"]
def __init__(self, config: Qwen3MoeConfig):
super().__init__(config)
self.config = config
self.model = Qwen3MoeModel(config)
self.lm_head = Qwen3MoeLMHead(config)
self.criterion = Qwen3MoePretrainingCriterion(config)
self.router_aux_loss_coef = config.router_aux_loss_coef
self.num_experts = config.num_experts
self.num_experts_per_tok = config.num_experts_per_tok
# Initialize weights and apply final processing
if config.sliding_window:
self.config.sliding_window = False
logger.warning("We do not support sliding window attention for now.")
def get_input_embeddings(self):
return self.model.embed_tokens
def set_input_embeddings(self, value):
self.model.embed_tokens = value
def get_output_embeddings(self):
return self.lm_head
def set_output_embeddings(self, new_embeddings):
self.lm_head = new_embeddings
def set_decoder(self, decoder):
self.model = decoder
def get_decoder(self):
return self.model
def prepare_inputs_for_generation(
self,
input_ids,
use_cache=False,
past_key_values=None,
attention_mask=None,
inputs_embeds=None,
output_router_logits=False,
**kwargs,
):
batch_size, seq_length = input_ids.shape
position_ids = kwargs.get("position_ids", paddle.arange(seq_length).expand((batch_size, seq_length)))
if past_key_values:
input_ids = input_ids[:, -1].unsqueeze(axis=-1)
position_ids = position_ids[:, -1].unsqueeze(-1)
# if `inputs_embeds` are passed, we only want to use them in the 1st generation step
if inputs_embeds is not None and past_key_values is None:
model_inputs = {"inputs_embeds": inputs_embeds}
else:
model_inputs = {"input_ids": input_ids}
model_inputs.update(
{
"position_ids": position_ids,
"past_key_values": past_key_values,
"use_cache": use_cache,
"attention_mask": attention_mask,
"output_router_logits": output_router_logits,
}
)
return model_inputs
def _get_model_inputs_spec(self, dtype: str):
return {
"input_ids": paddle.static.InputSpec(shape=[None, None], dtype="int64"),
"attention_mask": paddle.static.InputSpec(shape=[None, None], dtype="int64"),
"position_ids": paddle.static.InputSpec(shape=[None, None], dtype="int64"),
}
@staticmethod
def update_model_kwargs_for_generation(outputs, model_kwargs, is_encoder_decoder=False):
# update cache
if isinstance(outputs, tuple) and len(outputs) > 1 and not isinstance(outputs[1], paddle.Tensor):
model_kwargs["past_key_values"] = outputs[1]
if isinstance(outputs, MoECausalLMOutputWithPast) and "past_key_values" in outputs:
model_kwargs["past_key_values"] = outputs.past_key_values
# update position_ids
if "position_ids" in model_kwargs and model_kwargs["position_ids"] is not None:
position_ids = model_kwargs["position_ids"]
model_kwargs["position_ids"] = paddle.concat([position_ids, position_ids[..., -1:] + 1], axis=-1)
if not is_encoder_decoder and "attention_mask" in model_kwargs:
# TODO: support attention mask for other models
attention_mask = model_kwargs["attention_mask"]
if len(attention_mask.shape) == 2:
model_kwargs["attention_mask"] = paddle.concat(
[attention_mask, paddle.ones([attention_mask.shape[0], 1], dtype=attention_mask.dtype)],
axis=-1,
)
elif len(attention_mask.shape) == 4:
model_kwargs["attention_mask"] = paddle.concat(
[attention_mask, paddle.ones([*attention_mask.shape[:3], 1], dtype=attention_mask.dtype)],
axis=-1,
)[:, :, -1:, :]
return model_kwargs
def forward(
self,
input_ids: paddle.Tensor = None,
position_ids: Optional[paddle.Tensor] = None,
attention_mask: Optional[paddle.Tensor] = None,
inputs_embeds: Optional[paddle.Tensor] = None,
labels: Optional[paddle.Tensor] = None,
use_cache: Optional[bool] = None,
past_key_values: Optional[List[paddle.Tensor]] = None,
output_attentions: Optional[bool] = None,
output_hidden_states: Optional[bool] = None,
output_router_logits: Optional[bool] = None,
return_dict: Optional[bool] = None,
attn_mask_startend_row_indices=None,
):
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
output_hidden_states = (
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
)
output_router_logits = (
output_router_logits if output_router_logits is not None else self.config.output_router_logits
)
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
if attn_mask_startend_row_indices is not None and attention_mask is not None:
logger.warning(
"You have provided both attn_mask_startend_row_indices and attention_mask. "
"The attn_mask_startend_row_indices will be used."
)
attention_mask = None
# decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
outputs = self.model(
input_ids=input_ids, # [bs, seq_len]
position_ids=position_ids,
attention_mask=attention_mask,
inputs_embeds=inputs_embeds,
use_cache=use_cache,
past_key_values=past_key_values,
output_attentions=output_attentions,
output_hidden_states=output_hidden_states,
output_router_logits=output_router_logits,
return_dict=return_dict,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
)
hidden_states = outputs[0] # [bs, seq_len, dim]
# if labels is None,means we need full output, instead of tensor_parallel_output
# tensor_parallel_output is together with ParallelCrossEntropy
tensor_parallel_output = self.config.tensor_parallel_output and self.config.tensor_parallel_degree > 1
if labels is not None and self.config.use_fused_linear_cross_entropy:
from paddlenlp_kernel.triton.cut_cross_entropy import linear_cross_entropy
assert (
self.config.tensor_parallel_degree <= 1
), "The argument `use_fused_linear_cross_entropy` is imcompatiable with tensor parallel "
masked_lm_loss = linear_cross_entropy(hidden_states, self.lm_head.weight, targets=labels)
binary_sequence = paddle.where(
masked_lm_loss > 0, paddle.ones_like(masked_lm_loss), paddle.zeros_like(masked_lm_loss)
)
count = paddle.sum(binary_sequence)
if count == 0:
loss = paddle.sum(masked_lm_loss * binary_sequence)
else:
loss = paddle.sum(masked_lm_loss * binary_sequence) / count
logits = None
else:
logits = self.lm_head(hidden_states, tensor_parallel_output=tensor_parallel_output)
loss = None
if labels is not None:
loss = self.criterion(logits, labels)
aux_loss = None
if output_router_logits:
aux_loss = load_balancing_loss_func(
outputs.router_logits if return_dict else outputs[-1],
self.num_experts,
self.num_experts_per_tok,
attention_mask,
)
if labels is not None:
loss += self.router_aux_loss_coef * aux_loss
if not return_dict:
output = (logits,) + outputs[1:]
if output_router_logits:
output = (aux_loss,) + output
return (loss,) + output if loss is not None else output
return MoECausalLMOutputWithPast(
loss=loss,
aux_loss=aux_loss,
logits=logits,
past_key_values=outputs.past_key_values,
hidden_states=outputs.hidden_states,
attentions=outputs.attentions,
router_logits=outputs.router_logits,
)

View File

@@ -0,0 +1,254 @@
# Copyright (c) 2025 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.
from typing import OrderedDict
import paddle
import paddle.distributed.fleet as fleet
import paddle.nn as nn
from paddle.distributed.fleet.meta_parallel import (
LayerDesc,
PipelineLayer,
SharedLayerDesc,
)
from paddle.distributed.fleet.recompute.recompute import recompute
from ..model_utils import PipelinePretrainedModel
from .modeling import (
Qwen3MoeConfig,
Qwen3MoeDecoderLayer,
Qwen3MoeLMHead,
Qwen3MoePretrainedModel,
Qwen3MoePretrainingCriterion,
Qwen3MoeRMSNorm,
)
__all__ = [
"Qwen3MoeForCausalLMPipe",
]
from ..qwen2_moe.modeling_pp import (
Qwen2MoeEmbeddingPipe,
get_attr,
parse_args,
return_args,
)
class Qwen3MoeEmbeddingPipe(Qwen2MoeEmbeddingPipe):
pass
class Qwen3MoeDecoderLayerPipe(Qwen3MoeDecoderLayer):
def forward(self, args):
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
has_gradient = not hidden_states.stop_gradient
if attention_mask is not None and attention_mask.dtype == paddle.int32:
attention_mask, attn_mask_startend_row_indices, position_ids = (
None,
attention_mask,
attn_mask_startend_row_indices,
)
elif attention_mask is not None and attention_mask.dtype == paddle.int64:
attention_mask, attn_mask_startend_row_indices, position_ids = None, None, attention_mask
elif attn_mask_startend_row_indices is not None and attn_mask_startend_row_indices.dtype == paddle.int64:
attn_mask_startend_row_indices, position_ids = None, attn_mask_startend_row_indices
if self.enable_recompute and self.config.recompute_granularity == "full" and has_gradient:
if attention_mask is not None or attn_mask_startend_row_indices is not None:
hidden_states = recompute(
super().forward,
hidden_states,
position_ids=position_ids,
attention_mask=attention_mask,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
use_reentrant=False,
)
else:
# for pretrain
hidden_states = recompute(
super().forward,
hidden_states,
position_ids=position_ids,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
use_reentrant=self.config.recompute_use_reentrant,
)
else:
hidden_states = super().forward(
hidden_states,
position_ids=position_ids,
attention_mask=attention_mask,
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
)
return return_args(hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids)
class Qwen3MoeRMSNormPipe(nn.Layer):
def __init__(self, config):
super().__init__()
self.norm = Qwen3MoeRMSNorm(config)
def forward(self, args):
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
return self.norm(hidden_states)
class Qwen3MoeLMHeadPipe(Qwen3MoeLMHead):
def __init__(self, config, transpose_y=False):
super(Qwen3MoeLMHeadPipe, self).__init__(config)
@property
def embedding_weight(self):
return get_attr(self, "weight")
class Qwen3MoeForCausalLMPipe(PipelinePretrainedModel, PipelineLayer):
"""QWenForPretraining adapted for pipeline parallelism.
The largest change is flattening the QWenModel class so we can express it as a
sequence of layers including embedding, transformer layers, and output.
"""
config_class = Qwen3MoeConfig
_get_tensor_parallel_mappings = Qwen3MoePretrainedModel._get_tensor_parallel_mappings
_init_weights = Qwen3MoePretrainedModel._init_weights
_keys_to_ignore_on_load_unexpected = Qwen3MoePretrainedModel._keys_to_ignore_on_load_unexpected
_tied_weights_keys = ["lm_head.weight"]
# DONOT Add base_model_prefix !!!!
@classmethod
def _prepare_pipeline_inputs_func(cls, inputs):
first_stage_keys = ["input_ids", "attention_mask", "attn_mask_startend_row_indices", "position_ids"]
last_stage_keys = ["labels"]
def get_expected_keys(inputs, keys):
ret = tuple([inputs.pop(k) if k in inputs else None for k in keys])
if len(ret) == 1:
ret = ret[0]
return ret
if type(inputs) is dict or type(inputs) is OrderedDict:
return [
get_expected_keys(inputs, first_stage_keys),
get_expected_keys(inputs, last_stage_keys),
]
keys = list(inputs[0].keys())
inputs_batch = {key: [data.pop(key) for data in inputs] for key in keys}
return [
get_expected_keys(inputs_batch, first_stage_keys),
get_expected_keys(inputs_batch, last_stage_keys),
]
def __init__(self, config: Qwen3MoeConfig):
self.config = config
# Note that we will actually perform a recompute only if both enable_recompute and layerwise_recompute are set to True
# Enable_recompute defaults to False and is controlled by Trainer
self.enable_recompute = False
self.recompute_granularity = self.config.recompute_granularity
self.pp_recompute_interval = self.config.pp_recompute_interval
self.no_recompute_layers = config.no_recompute_layers if config.no_recompute_layers is not None else []
if self.recompute_granularity == "full":
assert len(self.no_recompute_layers) == 0, "for pp with full recompute, no_recompute_layers is not support"
virtual_pp_degree = getattr(self.config, "virtual_pp_degree", 1)
def get_hcg():
return fleet.get_hybrid_communicate_group()
hcg = get_hcg()
tensor_parallel_degree = max(hcg.get_model_parallel_world_size(), 1)
tensor_parallel_rank = max(hcg.get_model_parallel_rank(), 0)
# TODO: fix tensor_parallel_degree rewrite in here
config.tensor_parallel_degree = tensor_parallel_degree
config.tensor_parallel_rank = tensor_parallel_rank
if config.tie_word_embeddings:
self.add_sequential_layer(
SharedLayerDesc(
"model_moe_shared_weight",
Qwen3MoeEmbeddingPipe,
shared_weight_attr="embedding_weight",
config=config,
),
"model",
)
else:
self.add_sequential_layer(LayerDesc(Qwen3MoeEmbeddingPipe, config=config), "model")
for i in range(config.num_hidden_layers):
self.add_sequential_layer(
LayerDesc(
Qwen3MoeDecoderLayerPipe,
config=config,
layerwise_recompute=i not in self.no_recompute_layers,
),
f"model.layers.{i}",
)
self.add_sequential_layer(LayerDesc(Qwen3MoeRMSNormPipe, config=config), "model")
if config.tie_word_embeddings:
self.add_sequential_layer(
SharedLayerDesc(
"model_moe_shared_weight",
Qwen3MoeLMHeadPipe,
shared_weight_attr="embedding_weight",
config=config,
**{"transpose_y": True},
),
"lm_head",
)
else:
self.add_sequential_layer(LayerDesc(Qwen3MoeLMHeadPipe, config=config), "lm_head")
recompute_interval = 0
if self.enable_recompute and self.recompute_granularity == "full":
assert self.config.pp_recompute_interval <= config.num_hidden_layers // (
virtual_pp_degree * get_hcg().topology().get_dim_size("pipe")
), "pp recompute interval should smaller than num layers of each pp chunk"
recompute_interval = self.config.pp_recompute_interval
seg_method = "layer:Qwen3MoeDecoderLayer"
if config.num_hidden_layers % get_hcg().topology().get_dim_size("pipe") != 0:
seg_method = "uniform"
PipelineLayer.__init__(
self,
layers=self.get_sequential_layers(),
loss_fn=self.get_loss_fn(config),
topology=get_hcg().topology(),
seg_method=seg_method,
recompute_interval=recompute_interval,
recompute_ctx={
"mp_group": get_hcg().get_model_parallel_group(),
"offload": False,
"partition": False,
},
num_virtual_pipeline_stages=virtual_pp_degree,
)
# You should call init here, since there is a diamond inheritance problem
self.apply(self._init_weights)
# DON'T init PipelinePretrainedModel
# PipelinePretrainedModel.__init__(self.super(), config=config)
def get_loss_fn(self, config):
return Qwen3MoePretrainingCriterion(config)

View File

@@ -25,6 +25,7 @@ import paddle
from _io import BufferedReader
from safetensors import deserialize
from paddlenlp.transformers.utils import device_guard
from paddlenlp.utils.env import PYTORCH_WEIGHTS_NAME, SAFE_WEIGHTS_NAME
MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30
@@ -226,10 +227,14 @@ def load_torch(path: str, **pickle_load_args):
state_dict = {}
for k, v in flat:
dtype = _TYPES[v["dtype"]]
if v["dtype"] == "BF16":
arr = paddle.to_tensor(np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]), dtype="bfloat16")
else:
arr = paddle.to_tensor(np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]))
with device_guard("cpu"):
if v["dtype"] == "BF16":
arr = paddle.Tensor(
np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]), dtype="bfloat16", zero_copy=True
)
else:
arr = paddle.Tensor(np.frombuffer(v["data"], dtype=dtype).reshape(v["shape"]), zero_copy=True)
state_dict[k] = arr
return state_dict