[TeleChat]: add TeleChat causal language model support - #4768
[TeleChat]: add TeleChat causal language model support#4768cf-icehzgzh wants to merge 4 commits into
Conversation
d6cd78c to
2e4da63
Compare
liuhao2638
left a comment
There was a problem hiding this comment.
已完成初审,发现了需要修复后再合入的问题,具体细节已放在行级评论中。CI 当前显示通过;本地环境缺少 paddle,我未能复跑新增模型单测。
优先级:P3 非行级:PR 标题和描述目前只说明 TeleChat,但 diff 同时新增并公开导出 DiffTransformer 模型、tokenizer 和测试,范围与描述不一致。处理要求:请针对该评论进行回复(同意并已修改请回复 Done,不同意请说明理由)。 建议将 DiffTransformer 相关变更拆到独立 PR,或在本 PR 标题/描述/测试计划中明确说明这部分范围和验证结果。
| [ | ||
| ("deepseek_v2", "DeepseekV2"), | ||
| ("deepseek_v3", "DeepseekV3"), | ||
| ("diff_transformer", "DiffTransformer"), |
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit。
这里把 diff_transformer 的基础模型名注册成了 DiffTransformer,但新增的 paddleformers.transformers.diff_transformer 只导出了 DiffTransformerModel / DiffTransformerForCausalLM,没有 DiffTransformer 这个符号。AutoModel.from_config(DiffTransformerConfig()) 走 _LazyAutoMapping 时会按这个名字加载属性,导致新注册的 AutoModel 路由不可用。请把映射改成实际导出的基础模型类,或补充等价别名并加上 AutoModel 路由测试。
| ("diff_transformer", "DiffTransformer"), | |
| ("diff_transformer", "DiffTransformerModel"), |
| self.num_heads = config.n_head | ||
| self.head_dim = config.hidden_size // config.n_head | ||
| if self.head_dim * self.num_heads != config.hidden_size: | ||
| raise ValueError("hidden_size must be divisible by n_head") |
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit。
query / key_value 使用 GeneralLinear.create(..., tp_plan="colwise") 后,在 tensor_model_parallel_size > 1 时每个 rank 只会拿到分片后的 hidden 维度;但这里的 self.num_heads 仍是全局 config.n_head,下面 reshape 到 [batch, seq, self.num_heads, self.head_dim] 会直接形状不匹配。其它 LLM 实现会先把本地 head 数除以 TP degree。请按本地 head 数 reshape,并补充一个 tensor_model_parallel_size > 1 的构造/前向覆盖。
| self.num_heads = config.n_head | |
| self.head_dim = config.hidden_size // config.n_head | |
| if self.head_dim * self.num_heads != config.hidden_size: | |
| raise ValueError("hidden_size must be divisible by n_head") | |
| self.num_heads = config.n_head | |
| self.head_dim = config.hidden_size // config.n_head | |
| if self.head_dim * config.n_head != config.hidden_size: | |
| raise ValueError("hidden_size must be divisible by n_head") | |
| if config.tensor_model_parallel_size > 1: | |
| if config.n_head % config.tensor_model_parallel_size != 0: | |
| raise ValueError("n_head must be divisible by tensor_model_parallel_size") | |
| self.num_heads = config.n_head // config.tensor_model_parallel_size |
| def __init__(self, config): | ||
| super().__init__(config) | ||
| self.transformer = TelechatModel(config) | ||
| self.lm_head = GeneralLinear.create(config.hidden_size, config.vocab_size, has_bias=False, config=config, tp_plan="colwise") |
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit。
这里同样走了 colwise TP,但默认 gather_output=False 会让 logits 只包含本 rank 的 vocab_size / tensor_model_parallel_size 分片。后面的 F.log_softmax、take_along_axis 以及 generation 都按完整词表使用 logits.shape[-1],TP 下会得到错误 loss/采样分布,甚至 labels 超出本地分片时索引失败。请确保 causal LM 输出完整词表 logits,或改用仓库里已有的 GeneralLMHead/分布式 criterion 路径并同步调整权重转换。
| self.lm_head = GeneralLinear.create(config.hidden_size, config.vocab_size, has_bias=False, config=config, tp_plan="colwise") | |
| self.lm_head = GeneralLinear.create( | |
| config.hidden_size, | |
| config.vocab_size, | |
| has_bias=False, | |
| config=config, | |
| tp_plan="colwise", | |
| gather_output=True, | |
| ) |
| def forward(self, x, position_ids): | ||
| freqs = position_ids.astype("float32").unsqueeze(-1) * self.inv_freq.reshape([1, 1, -1]) | ||
| emb = paddle.concat((freqs, freqs), axis=-1) | ||
| return emb.cos().astype("float16"), emb.sin().astype("float16") |
There was a problem hiding this comment.
优先级:P2
处理要求:请针对该评论进行回复(同意并已修改请回复 Done,不同意请说明理由)。
这里把 RoPE 的 cos/sin 固定成 float16,会让 FP32 前向/精度对齐路径也先经过半精度位置编码;如果用户用 BF16,位置编码还会先量化到 FP16 再参与计算。仓库里同类 RoPE 通常按输入 hidden states 的 dtype 返回,请避免在这里硬编码为 FP16。
| return emb.cos().astype("float16"), emb.sin().astype("float16") | |
| return emb.cos().astype(x.dtype), emb.sin().astype(x.dtype) |
4dfb614 to
bdaf729
Compare
risemeup1111
left a comment
There was a problem hiding this comment.
复查当前 head 后,之前的 DiffTransformer 范围已不在当前 PR diff 中;CI 显示通过。本地环境缺少 paddle,未能复跑新增 TeleChat 单测。
仍有需要修复后再合入的 TeleChat TP 路径问题,细节见行级评论。
| self.num_heads = config.n_head | ||
| self.head_dim = config.hidden_size // config.n_head | ||
| if self.head_dim * self.num_heads != config.hidden_size: | ||
| raise ValueError("hidden_size must be divisible by n_head") |
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit。
query / key_value 使用 tp_plan="colwise" 后,在 tensor_model_parallel_size > 1 时每个 rank 只会拿到分片后的 hidden 维度;但这里仍用全局 config.n_head 做 reshape,self.query(hidden_states) 的最后一维会是 hidden_size / tp_size,无法 reshape 到 config.n_head * head_dim == hidden_size。请按本地 head 数 reshape,并补充一个 tensor_model_parallel_size > 1 的构造或前向覆盖。
| self.num_heads = config.n_head | |
| self.head_dim = config.hidden_size // config.n_head | |
| if self.head_dim * self.num_heads != config.hidden_size: | |
| raise ValueError("hidden_size must be divisible by n_head") | |
| self.num_heads = config.n_head | |
| self.head_dim = config.hidden_size // config.n_head | |
| if self.head_dim * config.n_head != config.hidden_size: | |
| raise ValueError("hidden_size must be divisible by n_head") | |
| if config.tensor_model_parallel_size > 1: | |
| if config.n_head % config.tensor_model_parallel_size != 0: | |
| raise ValueError("n_head must be divisible by tensor_model_parallel_size") | |
| self.num_heads = config.n_head // config.tensor_model_parallel_size |
| def __init__(self, config): | ||
| super().__init__(config) | ||
| self.transformer = TelechatModel(config) | ||
| self.lm_head = GeneralLinear.create(config.hidden_size, config.vocab_size, has_bias=False, config=config, tp_plan="colwise") |
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit。
lm_head 同样走 colwise TP,但默认不会聚合输出,TP 下 logits 只包含本 rank 的词表分片。后面的 F.log_softmax、take_along_axis 和 generation 都按完整词表使用 logits;一旦 label 落在其它 rank 的词表分片上会索引失败,采样分布也会错误。请确保 causal LM 输出完整词表 logits,或改为仓库已有的分布式 loss/generation 路径并同步补测试。
| self.lm_head = GeneralLinear.create(config.hidden_size, config.vocab_size, has_bias=False, config=config, tp_plan="colwise") | |
| self.lm_head = GeneralLinear.create( | |
| config.hidden_size, | |
| config.vocab_size, | |
| has_bias=False, | |
| config=config, | |
| tp_plan="colwise", | |
| gather_output=True, | |
| ) |
| def forward(self, x, position_ids): | ||
| freqs = position_ids.astype("float32").unsqueeze(-1) * self.inv_freq.reshape([1, 1, -1]) | ||
| emb = paddle.concat((freqs, freqs), axis=-1) | ||
| return emb.cos().astype("float16"), emb.sin().astype("float16") |
There was a problem hiding this comment.
优先级:P2
处理要求:请针对该评论进行回复(同意并已修改请回复 Done,不同意请说明理由)。
这里把 RoPE 的 cos/sin 固定成 float16,会让 FP32 前向也先经过半精度位置编码;BF16 路径还会先量化到 FP16 再参与计算。仓库同类 RoPE 通常按输入 hidden states 的 dtype 返回,请避免在这里硬编码为 FP16。
| return emb.cos().astype("float16"), emb.sin().astype("float16") | |
| return emb.cos().astype(x.dtype), emb.sin().astype(x.dtype) |
risemeup1111
left a comment
There was a problem hiding this comment.
已复查新 commit。两个此前阻塞的 TP 路径问题已通过代码修改和新增测试覆盖修复;RoPE dtype 也已改为按输入 dtype 返回。CI 当前显示通过;本地环境仍缺少 paddle,未能复跑新增单测。
当前没有新的行级问题。按评审约定,剩余的低优先级线程还需要作者在对应评论下回复 Done 或说明理由后,我再提交批准。
3 similar comments
|
Thanks for your contribution! |
a184700 to
ed18579
Compare
risemeup1111
left a comment
There was a problem hiding this comment.
复查当前 head,代码树相对上次复查没有变化;此前 TeleChat TP 和 RoPE dtype 的代码修复仍然在。新的阻塞点是当前 CI 的 Lint 失败,能定位到具体行的部分已放在行级评论中。
优先级:P0 非行级:CI
Lint中black还报告会重写paddleformers/transformers/telechat/modeling.py,这部分格式化结果通常是多处自动换行,无法可靠附到单个 diff 行。处理要求:请修复并提交新的 commit。 建议本地运行:
make lint| import math | ||
|
|
||
| import paddle | ||
| from paddle import nn | ||
| import paddle.nn.functional as F | ||
|
|
||
| from ...nn.attention.interface import ALL_ATTENTION_FUNCTIONS | ||
| from ...nn.embedding import Embedding as GeneralEmbedding | ||
| from ...nn.linear import Linear as GeneralLinear | ||
| from ..cache_utils import Cache, DynamicCache |
There was a problem hiding this comment.
优先级:P0
处理要求:请针对该评论修复并提交新的 commit。
当前 Lint 检查失败,日志里 flake8 明确报出 math 和 Cache 是未使用导入,同时 isort 也会改写这个文件。这个 import block 会阻塞 CI 合入;请删除未使用导入并按 isort 顺序整理,之后重新运行 lint。
| import math | |
| import paddle | |
| from paddle import nn | |
| import paddle.nn.functional as F | |
| from ...nn.attention.interface import ALL_ATTENTION_FUNCTIONS | |
| from ...nn.embedding import Embedding as GeneralEmbedding | |
| from ...nn.linear import Linear as GeneralLinear | |
| from ..cache_utils import Cache, DynamicCache | |
| import paddle | |
| import paddle.nn.functional as F | |
| from paddle import nn | |
| from ...nn.attention.interface import ALL_ATTENTION_FUNCTIONS | |
| from ...nn.embedding import Embedding as GeneralEmbedding | |
| from ...nn.linear import Linear as GeneralLinear | |
| from ..cache_utils import DynamicCache |
| import unittest | ||
| from unittest.mock import patch | ||
|
|
||
| import paddle | ||
|
|
||
| import paddle.nn.functional as F |
There was a problem hiding this comment.
优先级:P0
处理要求:请针对该评论修复并提交新的 commit。
Lint 日志显示 copyright_checker 和 isort 都会修改新增的测试文件。这里缺少仓库要求的版权头,且 paddle 相关 import 被多余空行拆开,会导致 lint 持续失败。请补齐版权头并整理 import。
| import unittest | |
| from unittest.mock import patch | |
| import paddle | |
| import paddle.nn.functional as F | |
| # Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import unittest | |
| from unittest.mock import patch | |
| import paddle | |
| import paddle.nn.functional as F |
risemeup1111
left a comment
There was a problem hiding this comment.
已复查最新 commit。此前 P0 lint 问题已经通过本次提交修复,TeleChat TP 路径和 RoPE dtype 的代码修复也仍然保留;当前没有发现新的行级问题。
本地环境仍缺少 paddle,未能复跑新增单测。当前还有部分 CI 任务在运行中;另外此前 P2 线程仍需要作者在对应评论下回复 Done 或说明理由后,我再提交批准。
PaddleFormers Log Analysis
日志分析报告
失败的测试case: 根本原因分析: 修复建议:
🔄 每次 Re-run 后自动更新 |
| "gpt_oss.configuration": ["GptOssConfig"], | ||
| "gpt_oss.modeling": ["GptOssModel", "GptOssForCausalLM", "GptOssForCausalLMPipe"], | ||
|
|
||
| "telechat.configuration": ["TelechatConfig"], |
| def __init__(self, config): | ||
| super().__init__() | ||
| self.head_dim = config.hidden_size // config.n_head | ||
| inv_freq = 1.0 / (10000.0 ** (paddle.arange(0, self.head_dim, 2, dtype="float32") / self.head_dim)) |
| ) | ||
| residual = hidden_states | ||
| hidden_states = self.post_attention_layernorm(hidden_states) | ||
| return residual + self.mlp(hidden_states) |
| position_embeddings=None, | ||
| past_key_values=None, | ||
| ): | ||
| residual = hidden_states |
| query_states, key_states = apply_rotary_pos_emb(query_states, key_states, *position_embeddings) | ||
| if past_key_values is not None: | ||
| key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx) | ||
| attention_interface = ALL_ATTENTION_FUNCTIONS["sdpa"] |
There was a problem hiding this comment.
Paddle-Bot Review Board (review完成)
| 序号 | 位置 | 优先级 | 规则来源 | 状态 |
|---|---|---|---|---|
| 1 | Lint 格式检查 | 默认规则 | ✅ | |
| 2 | 长上下文 RoPE | 默认规则 仓库规则:基础评审规则 |
✅ | |
| 3 | Hidden Dropout | 默认规则 仓库规则:基础评审规则 |
✅ | |
| 4 | 残差连接配置 | 默认规则 仓库规则:基础评审规则 |
✅ | |
| 5 | 注意力后端路由 | 默认规则 仓库规则:基础评审规则 |
✅ |
af44885 to
844d7cf
Compare
… residual option, attn backend)
Failed CI看板
日志分析报告失败的测试 case: 根本原因分析: 首个真实错误发生在 失败发生在 checkout 完成前,仓库代码未被执行,PR 新增的 TeleChat 模型、自动注册逻辑和测试用例均未进入测试流程;coverage 上传也未执行。清理阶段的重复报错属于同一环境故障的级联错误,不是独立根因。 PR diff 未修改 workflow、runner 镜像、系统依赖或 Node runtime,因此本次失败应归类为【与当前改动无关】的 CI 基础设施问题,而不是 TeleChat 代码问题。 修复建议:
Powered by Nyanpasu with gpt-5.6-luna 默认推理级别, please check the suggestions carefully. |
本 PR 完成 Tele-AI/TeleChat-1B 从 Transformers 到 PaddleFormers 的迁移,包含组网、权重转换、前向精度、生成、LoRA 训练 loss 对齐、LoRA 合并及单测验证。
1. 前向精度对齐
模型:
Tele-AI/TeleChat-1B通过官方 PyTorch 权重转换为 PaddleFormers 权重进行验证
AutoConfig、AutoModel、AutoModelForCausalLM本地加载验证通过PaddleFormers / Transformers logits:
1.5991124655556632e-062.6702880859375e-052. 模型生成
相同转换权重与 greedy 解码下验证通过,连续生成 8 个 token 与官方 PyTorch 实现逐 token 完全一致。
<_user>你好<_bot>3. 训练 loss 对齐
Tele-AI/TeleChat-1B8,alpha=16,dropout=0query、key_value、dense、gate_proj、up_proj、down_proj96首步 loss:
25.86615371704101625.866155624389651.9073486328125e-06logits checksum:
476130.65625,两端完全一致;所有位置 argmax 完全一致。4. LoRA 训练、合并与确定性
get_merge_state_dict导出纯合并权重:163lora_A/lora_B残留[10.734102249145508, 8.075804710388184, 3.176398754119873]192eaf30ec683e4d5fe75a9c9fc3c9bf4f6a274cc1964d6c03089ff50f2bfbba45e5. 模型单测
覆盖:
loss_mask与空监督 maskgit diff --check已通过。不包含转换权重、训练配置、训练 checkpoint、LoRA adapter、合并模型或本地验证脚本。