Skip to content

Add newloader core framework#1164

Open
Oneydauh wants to merge 25 commits into
alibaba:mainfrom
Oneydauh:develop/hengcang/newloader_core
Open

Add newloader core framework#1164
Oneydauh wants to merge 25 commits into
alibaba:mainfrom
Oneydauh:develop/hengcang/newloader_core

Conversation

@Oneydauh

@Oneydauh Oneydauh commented Jul 7, 2026

Copy link
Copy Markdown

This PR introduces the core framework for the new Python weight loader.

Included:

  • NewModelLoader and LoadConfig
  • RtpModule streaming weight dispatch
  • weight_mapper checkpoint discovery and tensor streaming
  • reusable newloader layers
  • quant_methods dispatch framework
  • FP8/GPTQ/AWQ/weight-only quant method implementations
  • empty lazy registry framework for follow-up model adaptation PRs

Not included:

  • model-specific newloader adaptations
  • runtime input contract changes
  • multimodal/VIT changes
  • smoke/golden updates
  • scheduler/profiler/metrics changes

Validation:

  • Python compile check passed for newloader core files
  • Diff is limited to rtp_llm/models_py

@Oneydauh Oneydauh requested a review from LLLLKKKK as a code owner July 7, 2026 09:06
@CLAassistant

CLAassistant commented Jul 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/6 · P3/2

Blocking Issues

P1

  • verify_phase1_dispatch 自检 py_test 与实现矛盾会失败,且暴露 ignore 匹配对 re: 正则前缀失效 @ rtp_llm/models_py/quant_methods/test/verify_phase1_dispatch.py:70
    • 建议:二选一并对齐:若实现符合预期(段边界匹配、拒绝子串),修正测试输入(如改成 "vision.encoder")并显式解析 re: 前缀走 re.fullmatch;若期望 re: 生效,则在 is_layer_ignored 中识别 re: 前缀并用正则匹配、同时补一条针对 re:.*lm_head.* 命中 model.lm_head 的断言。无论哪种,都应保证该 py_test 在 CI 上可通过,并覆盖 re: 与非 re: 两类模式。

Non-blocking Suggestions

P2

  • FP8 per-tensor MoE 融合刻意复刻旧 loader 的 shard 偏移 bug,up_s>gate_s 时精度错误 @ rtp_llm/models_py/quant_methods/fp8_moe.py:326
    • 建议:改为对 up/gate 两半区分别按各自 shard_s→max_s 正确 rescale(gate 应作用于 w13[e, M_tp:2*M_tp]),或直接采用内置版本的 dequant→cat→单标度 requant 方案(数值上等价于在线 per-tensor)。若确需与旧 golden 位对齐,请在注释/commit 中明确该 quirk 的适用前提(如仅 up_s<=gate_s 时正确)并补一个 up_s>gate_s 的用例证明可接受,同时修正 docstring 的"逐字一致"表述。
  • BaseMoEExperts._copy_per_channel_scale 用 tp_rank 而非 moe_expert_tp_rank,EP 模式潜在越界 @ rtp_llm/models_py/layers/moe_experts.py:526
    • 建议:将 _copy_per_channel_scaleself.tp_rank 改为 self.moe_expert_tp_rank,与 _copy_gate_or_up/_copy_block_scale/Fp8MoEMethod 对齐。更根本地,鉴于内置 fp8 分支与 Fp8MoEMethod 大段重复且已出现分叉,建议删除 BaseMoEExperts 内已被注册方法覆盖的内置 fp8 逻辑(保留 unquantized/委托),消除双份维护。
  • NewModelLoader.load() 主路径残留大量临时调试脚手架 @ rtp_llm/models_py/model_loader.py:421
    • 建议:将 dump 逻辑抽到独立的可选调试模块(如 debug_dump.py)并在 load() 末尾以单行调用挂钩,或直接移除。函数内局部 import 提到模块顶部或封装函数内。保留时也应精简为最小钩子,避免污染主加载路径。
  • LazyRegistryDict 迭代协议不一致,@register_model 直接注册的模型对 keys/items/len 不可见 @ rtp_llm/models_py/registry.py:15
    • 建议:让迭代类方法合并两来源:keys() 返回 _LAZY_MODEL_REGISTRY 键与底层 dict 已注册键的并集,__len__/__iter__/items() 相应对齐;或统一让 register_model 也在 _LAZY_MODEL_REGISTRY 记录条目。补一个"装饰器注册后可被 keys()/items() 枚举"的单测。
  • 最复杂的 MoE 加载路径缺少单元测试 @ rtp_llm/models_py/layers/moe_experts.py:327
    • 建议:为 MoE 加载补 CPU 可跑的单测:至少覆盖 EP remap(含 num_experts % ep_size != 0 的边界)、gate/up 半区顺序与 TP 切分、per-channel/per-block scale 的 offset 拼接、_fuse_per_tensor 在 up_s>gate_s 与 up_s<gate_s 两种输入下的融合结果。这些逻辑不依赖 GPU kernel,可用小尺寸张量验证 buffer 布局。
  • GPTQ / WeightOnly 方法以 empty(0) 建参数,无法通过 Linear 加载路径 @ rtp_llm/models_py/quant_methods/gptq.py:15
    • 建议:若本 PR 阶段暂不支持 GPTQ/WeightOnly,建议先不注册(从装饰器移除对应 key)或标注 NotImplementedError,避免它们看似可用实则崩溃;若要支持,则按 AWQ 的方式在 create_weights 分配正确形状的参数,并补最小加载单测。

P3

  • new_weight_loader py_library 未声明任何 deps @ rtp_llm/models_py/BUILD:16
    • 建议:在 new_weight_loaderdeps 中显式列出实际用到的库目标(utils/modules/kernels/model_loader/device/distributed 等),使该 library 自洽可独立复用;移除 new_models/** 空 glob 或在引入相应目录后再添加。
  • Column/RowParallelLinear.load_weights 中 "scales" 分支重复导致死代码 @ rtp_llm/models_py/layers/linear.py:136
    • 建议:去掉后一分支中的 "scales",仅保留 ("qweight", "qzeros");或将 AWQ 三件套统一到一处显式处理,并加注释说明 scales 不按 pack_factor 压缩。

Checklist Violations (11 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue new_weight_loader py_library 未声明任何 deps
    新增 py_library(name="new_weight_loader") 仅 glob 源文件、deps 为空,但其源码 import 了 rtp_llm.devicertp_llm.ops.compute_opsrtp_llm.models_py.modulesrtp_llm.utils.model_weightrtp_llm.model_loader.*rtp_llm.models_py.distributed.* 等外部包。当前仅因 models 目标额外依赖 :utils/:modules/:kernels///rtp_llm/model_loader:loader 才能间接满足;任何直接依赖 :new_weight_loader 的目标都会缺依赖。另 glob 了并不存在的 new_models/**
  • [6.1] Architecture — 兼容性:公开 API/持久数据/配置/环境迁移安全 → issue verify_phase1_dispatch 自检 py_test 与实现矛盾会失败,且暴露 ignore 匹配对 re: 正则前缀失效
    该文件被 BUILD 注册为 py_test,main() 中断言 qc_src.is_layer_ignored("model.visionxxx") is True(ignored_layers=["re:.*lm_head.*", "vision"])。但 base.QuantizationConfig.is_layer_ignored 用点边界分段前缀匹配:"vision" 无通配符 → 比较 prefix_parts[:1]==["model"] != ["vision"] → False;"re:.*lm_head.*" 走 fnmatch,要求以字面 re: 开头 → 也不匹配。故返回 False,断言失败 → ok=Falseraise SystemExit(1),测试必然失败。同时说明:compressed-tensors 常用的 re: 前缀正则 ignore(如 re:.*lm_head.*)在 is_layer_ignored 里被当 fnmatch 处理、永不命中,导致本应保持高精度的层被错误量化。
  • [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue NewModelLoader.load() 主路径残留大量临时调试脚手架
    load() 中 line 421 起有约 110 行 DUMP_WEIGHTS / DUMP_TENSOR_SLICES 调试代码(注释标注 [DUMP_WEIGHTS] temporary debug hook),内含函数内局部 import(import os as _oimport hashlib as _himport json as _j)、md5、逐张量切片采样等。虽由环境变量门控、非默认执行,但把大量一次性调试逻辑内联进核心加载入口,显著增加噪声与维护成本,也与"新概念在正确层级、不泄漏内部"相悖。
  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue FP8 per-tensor MoE 融合刻意复刻旧 loader 的 shard 偏移 bug,up_s>gate_s 时精度错误
    _fuse_per_tensorfor shard_s in (up_s, gate_s): if shard_s != max_s: half = w13[e, :M_tp]*shard_s; w13[e,:M_tp]=(half/max_s)。两次迭代都只改写 w13[e, :M_tp](up 半区),注释亦承认"start never advances"。当 up_s > gate_s(max=up_s):gate 分支会把已经正确的 up 半区按 gate_s/max_s 再缩放,破坏 up;而 gate 半区([M_tp:])从不被 rescale,其 fp8 值仍以 gate_s 表示却共享 max_s 标度 → 解码放大、静默精度损失。此外模块 docstring 声称与 BaseMoEExperts 内置路径"逐字一致",但内置 _fuse_fp8_per_tensor_scales 实为 dequant+requant,二者行为并不一致。
  • [6.1] Quality — 无 per-forward 调试日志 / 噪声热路径输出 → issue NewModelLoader.load() 主路径残留大量临时调试脚手架
    load() 中 line 421 起有约 110 行 DUMP_WEIGHTS / DUMP_TENSOR_SLICES 调试代码(注释标注 [DUMP_WEIGHTS] temporary debug hook),内含函数内局部 import(import os as _oimport hashlib as _himport json as _j)、md5、逐张量切片采样等。虽由环境变量门控、非默认执行,但把大量一次性调试逻辑内联进核心加载入口,显著增加噪声与维护成本,也与"新概念在正确层级、不泄漏内部"相悖。
  • [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue Column/RowParallelLinear.load_weights 中 "scales" 分支重复导致死代码
    ColumnParallelLinear.load_weights 的 elif 链先有 elif param_name in ("scales", "zeros"),随后又有 elif param_name in ("qweight", "qzeros", "scales")。由于 "scales" 已被前一分支捕获,后一 AWQ 分支对 "scales" 永不可达(RowParallelLinear 同样)。虽两处对 AWQ scales 的切分维度恰好等价、不产生错误结果,但重复键使意图混乱、易在后续修改时踩坑。
  • [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue GPTQ / WeightOnly 方法以 empty(0) 建参数,无法通过 Linear 加载路径
    GPTQLinearMethod.create_weights / WeightOnlyLinearMethod.create_weightsqweight/qzeros/scales/weight 建为 torch.empty(0) 占位。但 ColumnParallelLinear/RowParallelLinear.load_weights 在 TP 切分后有 if tensor.shape != param.shape: 强校验,对非 weight 参数直接 raise ValueError。占位形状 (0,) 与真实 ckpt tensor 必然不符 → 一旦这两个方法被选中即在加载期崩溃。二者已经 @register_quant_method("gptq"/"weight_only_*") 注册,属会崩的半成品脚手架(对比 AWQ 的 create_weights 分配了正确形状,可正常工作)。
  • [6.1] Software Engineering — LSP:子类/重写保持基类契约 → issue LazyRegistryDict 迭代协议不一致,@register_model 直接注册的模型对 keys/items/len 不可见
    LazyRegistryDictkeys()/__iter__/__len__/items() 只读取 _LAZY_MODEL_REGISTRY,而 register_modeldict.__setitem__(MODEL_REGISTRY, ...) 直接写入底层 dict、不写 _LAZY_MODEL_REGISTRY。结果:经装饰器注册的模型可被 MODEL_REGISTRY[key] / in 查到,却不出现在 keys()/items()/len()list_models() 中。model_loader._create_model 报错信息里的 list(MODEL_REGISTRY.keys()) 会遗漏这些模型,任何遍历注册表的代码同样漏项。这是对 dict 契约(LSP)的违反。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue BaseMoEExperts._copy_per_channel_scale 用 tp_rank 而非 moe_expert_tp_rank,EP 模式潜在越界
    _copy_per_channel_scale 对 gate/up 用 start = self.tp_rank * self.moe_inter_tp 切分;而同类的 _copy_gate_or_up_copy_block_scale 以及迁出版 Fp8MoEMethod._copy_per_channel_scale 均用 moe_expert_tp_rank。当 EP 开启(moe_expert_tp_size==1moe_expert_tp_rank==0moe_inter_tp==full)但 tp_rank>0 时,start=tp_rank*moe_inter 会使 narrow(0, start, moe_inter) 越界或切错。该内置路径当前因 fp8_per_channel 已注册 Fp8MoEMethod(quant_method != None)而不会走到,属潜伏态;但两份"应一致"的实现在此行分叉,是明显的 DRY 破坏与未来接模型/回退内置时的隐患。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue GPTQ / WeightOnly 方法以 empty(0) 建参数,无法通过 Linear 加载路径
    GPTQLinearMethod.create_weights / WeightOnlyLinearMethod.create_weightsqweight/qzeros/scales/weight 建为 torch.empty(0) 占位。但 ColumnParallelLinear/RowParallelLinear.load_weights 在 TP 切分后有 if tensor.shape != param.shape: 强校验,对非 weight 参数直接 raise ValueError。占位形状 (0,) 与真实 ckpt tensor 必然不符 → 一旦这两个方法被选中即在加载期崩溃。二者已经 @register_quant_method("gptq"/"weight_only_*") 注册,属会崩的半成品脚手架(对比 AWQ 的 create_weights 分配了正确形状,可正常工作)。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 最复杂的 MoE 加载路径缺少单元测试
    新增单测仅覆盖 Linear 的 FP8 加载/前向(test_fp8_already_quantized.py)。而体量最大(~940 行)且正确性最关键的 BaseMoEExperts(专家 TP/EP 切分、stacked-experts 拆分、per-tensor/channel/block scale 融合、在线量化)、Fp8MoEMethodW4A8Int4MoEMethod 全无单测,仅以 docstring 声明"per_tensor 已端到端验证;per_channel/per_block/online 为忠实搬移,各需对应 ckpt 验证"。上文 fp8_moe 融合的精度隐患与 moe_experts 的 rank 分叉正说明此处易错。

Strengths

  • 流式逐张量加载(RtpModule.load_weights + NewModelLoader)显著降低加载峰值显存(模型参数 + 单张 ckpt tensor),fastsafetensors 容量预检与失败回退 scratch 的设计稳健。
  • 量化抽象分层清晰:Linear / MoE 两套注册表按层类型派发,QuantizationConfig 通过 source_config 透传旧富配置而不重复解析 ckpt,扩展点合理。
  • 安全实践到位:torch.load(..., weights_only=True) 防止不可信 pickle 执行;无 eval/shell=True
  • Linear FP8 已量化/在线各路径(per-tensor/per-channel/per-block、Merged/QKV shard 合并、TP 切分)有较完整的 CPU 加载 + CUDA 前向单测。
  • 关键处 fail-fast 形状校验与详尽错误信息,便于定位 ckpt 与配置不匹配。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from 3d61c8c to fb4f93a Compare July 7, 2026 09:28
@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/2 · P3/2

Blocking Issues

P1

  • 1D per-channel FP8 weight_scale 加载直接崩溃 @ rtp_llm/models_py/layers/linear.py:156
    • 建议:在 weight_scale/input_scale 分支内,copy 前把 1D scale 归一化到目标 param 形状(如 if tensor.dim()==1 and param.dim()==2: tensor = tensor.view(-1,1)),或放宽 shape 校验对 per-channel scale 做 reshape,使 1D 与 2D ckpt 都能加载;并确认单测在 CI GPU 上真实执行以防回归。
  • Merged gate/up FP8 per-tensor scale 仅 max 合并未 rescale,静默损坏权重 @ rtp_llm/models_py/layers/linear.py:452
    • 建议:复用 QKV 的做法,在 per-tensor 合并时对非最大 scale 分片按 ratio=own/max rescale 对应 weight 切片后再 fill_(max);或抽出统一的 per-tensor scale 合并工具供 Merged 与 QKV 共用(R.I.1),避免两处逻辑分叉。至少在 phase-5 前对该配置(fp8 per-tensor 已量化 + 融合 gate_up)显式拒绝加载而非静默产出错误权重。

Non-blocking Suggestions

P2

  • 未匹配权重仅告警丢弃,缺少加载完整性校验 @ rtp_llm/models_py/module_base.py:138
    • 建议:增加加载后完整性校验:统计已赋值 param 数 / 期望 param 数,或标记每个 param 的 loaded 状态,发现未加载 param 时抛错(或至少 error 级日志 + 显式开关允许放行)。dropped 非空时默认应视为致命,除非显式配置允许丢弃已知无关权重。
  • GQA 在 tp_size > num_kv_heads 时 QKV 分片越界崩溃 @ rtp_llm/models_py/layers/linear.py:702
    • 建议:对 tp_size > num_kv_heads 显式走 KV 复制路径(所有 rank 加载同一份 kv head),与旧 loader 的 GQA 复制语义对齐;或在构造/加载时对该组合 fail-fast 并给出明确不支持提示,避免越界崩溃。

P3

  • fp8_moe 文件头“逐字一致”描述与实际实现不符 @ rtp_llm/models_py/quant_methods/fp8_moe.py:3
    • 建议:修正文件头描述:说明 _fuse_per_tensor 为保持与旧 loader / smoke golden 的位级兼容而有意采用 max+rescale(含只 rescale up 半的兼容行为),与 base 的 dequant+requant 不同;或统一两处实现并注明选择理由。
  • **LoadConfig 通过 kwargs + setattr 挂载任意属性 @ rtp_llm/models_py/model_loader.py:41
    • 建议:将这些确实需要的配置项显式声明为具名参数(带类型标注与默认值),或用 dataclass/TypedDict 固化 LoadConfig 字段集合;确需扩展点时收敛到单一显式命名的容器字段,减少 **kwargs 透传带来的隐式耦合。

Checklist Violations (8 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue 未匹配权重仅告警丢弃,缺少加载完整性校验
    RtpModule.load_weights 对找不到对应子模块/参数的权重加入 dropped 列表,循环结束仅 logger.warning 打印前 10 条即继续(module_base.py:138)。各层权重 buffer 多以 torch.empty(...) 分配(未初始化内存),NewModelLoader._run_post_load_hooks 也没有「所有 param 均已被赋值」的完整性检查。一旦 ckpt 命名与新 loader 的模块树/weight_mapper 不一致,受影响权重将保留 torch.empty 垃圾值,模型能启动但输出静默错误,排障困难。对于全新加载框架,fail-soft 的风险较高。
  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue Merged gate/up FP8 per-tensor scale 仅 max 合并未 rescale,静默损坏权重
    MergedColumnParallelLinear.load_weights 对已量化 FP8 per-tensor(param.numel()==1)收集各 shard 标量 scale,最后 linear.py:452 param.data.fill_(max(vals)) 只填最大值,不对较小 scale 分片的 fp8 权重做 rescale(linear.py:436 TODO(phase-5) 亦承认)。已量化 fp8 权重是以「各自 ckpt scale」表示的:gate/up 独立标定通常 scale 不同,合并成 max 后,较小 scale 分片反量化值被放大 max/own 倍 → 输出严重偏差,而非「小精度损失」。同文件 QKVParallelLinear._merge_qkv_per_tensor_scales(linear.py:679-686)正是对此做了 weight * (scale/max) 的 rescale,二者对同一问题实现不一致。虽有 warning,但权重仍是错的。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue 未匹配权重仅告警丢弃,缺少加载完整性校验
    RtpModule.load_weights 对找不到对应子模块/参数的权重加入 dropped 列表,循环结束仅 logger.warning 打印前 10 条即继续(module_base.py:138)。各层权重 buffer 多以 torch.empty(...) 分配(未初始化内存),NewModelLoader._run_post_load_hooks 也没有「所有 param 均已被赋值」的完整性检查。一旦 ckpt 命名与新 loader 的模块树/weight_mapper 不一致,受影响权重将保留 torch.empty 垃圾值,模型能启动但输出静默错误,排障困难。对于全新加载框架,fail-soft 的风险较高。
  • [6.1] Quality — Commit 原子、message 与行为匹配 → issue fp8_moe 文件头“逐字一致”描述与实际实现不符
    fp8_moe.py 文件头声称其逻辑与 BaseMoEExperts 内置 fp8 路径「逐字一致(只把 self. 换成 layer.)」。但 Fp8MoEMethod._fuse_per_tensor 采用 max+rescale(且刻意复刻旧 loader StaticFp8QuantWeight 只 rescale 第一半 up 的 bug 以对齐 golden),而 BaseMoEExperts._fuse_fp8_per_tensor_scales 采用 dequant+requant,并在注释中明确说该做法是「为避免 max+rescale 的精度损失」。二者并非逐字一致,描述会误导后续维护者以为可在两条实现间自由切换。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 1D per-channel FP8 weight_scale 加载直接崩溃
    Fp8PerChannelLinearMethod.create_weightsweight_scale 建成 2D [output_size, 1]。而 ColumnParallelLinear.load_weightsweight_scale 分支(linear.py:127)在 split 后仍是 1D [N](tp=1 时 _split_weight 直接原样返回),随后 linear.py:156 if tensor.shape != param.shape[N] != [N,1] 成立,且 param_name != "weight" → 走 else 抛 ValueError。同 PR 单测 test_load_into_column_parallel_normalize_1d_scale(传入 1D scale_1d,期望成功并断言最终 (N,1))必然报错。RowParallelLinear(linear.py:237)同理:per-channel scale 在输出维,`shape[-1]
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue GQA 在 tp_size > num_kv_heads 时 QKV 分片越界崩溃
    QKVParallelLinear._split_qkv(linear.py:702)对 k/v 分片按 heads_per_partition = max(1, num_kv_heads // tp_size)start = tp_rank * heads_per_partition * head_dimnarrow。当 tp_size > num_kv_heads(如 num_kv_heads=2、tp_size=4/8,常见于多卡/多机 GQA)时,num_kv_heads // tp_size == 0max(1,0)=1,而 ckpt k/v 张量仅 num_kv_heads*head_dim 行;starttp_rank 增大会超出该长度,narrow 抛越界 RuntimeError。此场景需 KV head 复制(replicate)而非切分,当前实现未处理。

RTP-LLM Checklist

  • [I] 代码质量 — 同一功能用统一工具函数 → issue Merged gate/up FP8 per-tensor scale 仅 max 合并未 rescale,静默损坏权重
    MergedColumnParallelLinear.load_weights 对已量化 FP8 per-tensor(param.numel()==1)收集各 shard 标量 scale,最后 linear.py:452 param.data.fill_(max(vals)) 只填最大值,不对较小 scale 分片的 fp8 权重做 rescale(linear.py:436 TODO(phase-5) 亦承认)。已量化 fp8 权重是以「各自 ckpt scale」表示的:gate/up 独立标定通常 scale 不同,合并成 max 后,较小 scale 分片反量化值被放大 max/own 倍 → 输出严重偏差,而非「小精度损失」。同文件 QKVParallelLinear._merge_qkv_per_tensor_scales(linear.py:679-686)正是对此做了 weight * (scale/max) 的 rescale,二者对同一问题实现不一致。虽有 warning,但权重仍是错的。

Python Static-First Checklist

  • [P.A] 静态结构与类型纪律 — 公开 API 禁止 **kwargs 透传 → issue LoadConfig 通过 **kwargs + setattr 挂载任意属性
    LoadConfig.__init__**kwargs 收尾并 for k, v in kwargs.items(): setattr(self, k, v)(model_loader.py:41),把 quant_source_configforce_cpu_load_weights 等隐式字段动态挂到实例上。这些字段随后在多处以 getattr(self.load_config, "xxx", default) 读取,属性契约不显式、无类型约束,IDE/pyright 无法校验,拼写错误会静默退化为默认值。

Strengths

  • 量化抽象分层合理:Linear 与 MoE 各自独立注册表 + 三段式钩子(create_weights/process_weights_after_loading/apply),并支持 ignore 列表回退未量化,扩展新量化方法无需改中心逻辑。
  • 安全性到位:torch.load(..., weights_only=True) 防止不可信 .pt/.bin 反序列化任意代码执行。
  • 性能细节考究:kernel import 提到模块级并配 lazy 兜底;EP 过滤用单次 re.search + 边界判断替换 O(num_experts) 的 N 分支正则。
  • fail-fast 校验清晰:vocab_size % tp_size、AWQ input_size % group_size 等均带可操作的报错信息。
  • load 路由单测覆盖较全(per-channel cat、per-block TP 切分、QKV 分片偏移),便于回归。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from fb4f93a to dfee9dc Compare July 7, 2026 09:42
@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/0 · P2/5 · P3/1

Non-blocking Suggestions

P2

  • FP8 MoE 量化逻辑在 BaseMoEExperts 内置路径与 Fp8MoEMethod 中重复,且 per_tensor 融合算法并不一致,内置分支已成死代码 @ rtp_llm/models_py/quant_methods/fp8_moe.py:314
    • 建议:删除 BaseMoEExperts 中已被 Fp8MoEMethod 覆盖的内置 fp8 分支(仅保留 quant_method is None 的 bf16/none 路径),或明确将内置分支标注为"仅当对应 method 未注册时的回退"并加断言防止双实现发散;同时修正 fp8_moe.py 文件头"逐字一致"的描述,指明 per_tensor 采用 max+rescale 与内置 dequant+requant 的差异。
  • 未知/未注册的量化类型静默回退为未量化,GPTQ/WeightOnly 已导出却未注册,存在静默产出错误权重的风险 @ rtp_llm/models_py/quant_methods/base.py:247
    • 建议:对未知/未注册的 quant_type 采用 fail-fast(抛出明确异常,列出已注册 key),而非静默回退未量化;或至少对"非空白且非 none 的 quant_type 未命中注册表"打 error 级日志。若 GPTQ/WeightOnly 暂不启用,建议先从 __init__ 的导出中移除或加 NotImplemented 说明,避免误导调用方以为可用。
  • QKV 的 FP8 per-block weight_scale_inv 在 num_kv_heads < tp_size (GQA/MQA) 时 TP 切分与权重复制逻辑不一致,会越界崩溃 @ rtp_llm/models_py/layers/linear.py:636
    • 建议:让 weight_scale_inv 的 TP 切分复用与权重相同的 GQA 复制语义(num_heads < tp_size 时复制第 0 个 block 而非按 tp_rank 偏移切分),并补一个 num_kv_heads < tp_size 的 fp8-block QKV 单测覆盖此边界。
  • 大量新增核心组件缺少单测覆盖 @ rtp_llm/models_py/quant_methods/test/BUILD:1
    • 建议:至少为 AWQ dequant apply、W4A8 MoE dispatch/repack、FP8 online MoE 融合、RtpModule.load_weights 的 redirect/ModuleList 分发、NewModelLoader 的 EP filter 与 fastsafetensors→scratch 回退补 CPU 单测;把不需要 GPU 的用例去掉 H20 标签以节省 GPU runner;将 verify_phase1_dispatch.py 改写为标准 unittest 断言。
  • new_weight_loader py_library 缺少 deps 声明 @ rtp_llm/models_py/BUILD:104
    • 建议:为 new_weight_loader 显式补齐 deps(modules/utils/kernels/configs/distributed/ops/model_loader/lora 等实际 import 到的目标),使该 target 自洽可被独立依赖,避免依赖 :models 的传递闭包。

P3

  • fastsafetensors 全局 monkeypatch 每次调用重复包裹 SafeTensorsMetadata.init @ rtp_llm/models_py/model_loader.py:262
    • 建议:加幂等保护(如在 SafeTensorsMetadata 上设置 _rtp_patched 标志,已 patch 则跳过),或将该 monkeypatch 提到模块级只执行一次;_clean_and_replace 的宽泛 except Exception 也建议缩窄并记录被丢弃的键。

Checklist Violations (7 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue new_weight_loader py_library 缺少 deps 声明
    新增的 py_library(name="new_weight_loader", ...) 只声明了 srcs(__init__.pymodule_base.pyquant_methods/*.pylayers/*.py 等),没有任何 deps,且 visibility=public。而这些源文件跨包 import 众多:rtp_llm.models_py.modulesrtp_llm.models_py.distributed.collective_torchrtp_llm.ops.compute_opsrtp_llm.devicertp_llm.utils.model_weightrtp_llm.model_loader.*rtp_llm.lora.lora_weights 等。目前依赖 :models 目标间接带入部分依赖,但直接依赖 new_weight_loader 或在严格依赖/隔离构建下会缺失 runfiles 导致 ImportError。
  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue fastsafetensors 全局 monkeypatch 每次调用重复包裹 SafeTensorsMetadata.__init__
    _get_fastsafetensors_weights 每次执行都 orig_init = fastsafetensors.common.SafeTensorsMetadata.__init__ 再赋值 patched_init。第二次调用时捕获到的 orig_init 已是上一次的 patched_init,导致包裹层层叠加。虽然进程内通常只加载一次模型,但该全局可变状态无幂等保护,一旦发生多次加载(多模型/热加载)会累积包裹并放大清洗逻辑的开销与风险。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue 未知/未注册的量化类型静默回退为未量化,GPTQ/WeightOnly 已导出却未注册,存在静默产出错误权重的风险
    get_quant_method 的 Linear 分支在 _LINEAR_METHOD_REGISTRY.get(self.quant_type) 未命中时直接返回 UnquantizedLinearMethod();MoE 分支未命中则返回 None,使 BaseMoEExperts 走 _quant_family="none" 的 bf16 分支。而 gptq.py GPTQLinearMethod、weight_only.py WeightOnlyLinearMethod 均无 @register_quant_method 装饰器,却在 quant_methods/__init__.py 中被导入并列入 __all__。若 quant_type 为 "gptq"/weight-only 或拼写错误:Linear 侧 ckpt 的 qweight/qzeros/scales 在 load_weights 中因 getattr 未命中被丢弃、weight 保持 torch.empty 未初始化;MoE 侧 fp8 权重被 copy 进 bf_
  • [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue FP8 MoE 量化逻辑在 BaseMoEExperts 内置路径与 Fp8MoEMethod 中重复,且 per_tensor 融合算法并不一致,内置分支已成死代码
    fp8_moe.py 文件头声称与 BaseMoEExperts 内置 fp8 路径"逐字一致",但两者的 per_tensor 融合实现不同:内置 _fuse_fp8_per_tensor_scales(moe_experts.py)走 dequant→cat→单次 re-quant,而 Fp8MoEMethod._fuse_per_tensor 走 max+rescale(且注释承认沿用旧 loader 里"shard 循环 start 不前进、两次 rescale 都作用于 w13 前半"的历史怪癖)。同时 fp8 全系已在 @register_moe_quant_method 注册,get_quant_method 对 MoE 层恒返回 Fp8MoEMethod,使 moe_experts.py 内 _init_buffers 的 fp8 分支、_dispatch_scale/_copy_*_scale/_fuse_fp8_*/_online_quantize_* 均不可达。两份实现并存会随演进悄悄发散。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue QKV 的 FP8 per-block weight_scale_inv 在 num_kv_heads < tp_size (GQA/MQA) 时 TP 切分与权重复制逻辑不一致,会越界崩溃
    _dispatch_qkv_shard 中 weight 走 _split_qkv,对 num_heads < tp_size 的 k/v 采用"按 tp_rank%num_heads 复制 head"的逻辑;但同一 shard 的 weight_scale_inv 分支改用 heads_pp=max(1,num_heads//tp_size)rows=heads_pp*head_dimstart_blk=(tp_rank*rows)//blktensor.narrow(0, start_blk, rows//blk)。当 num_kv_heads < tp_size 时 rows=head_dim(通常 1 个 block),而 ckpt 中该 shard 的 scale dim0 只有 1 个 block,start_blk=tp_rank≥1 → narrow 越界报错。per-channel 分支通过复用 _split_qkv 正确处理了该 GQA 场景,唯独 block scale 路径没有对齐。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 大量新增核心组件缺少单测覆盖
    本 PR 新增 AWQ、GPTQ、WeightOnly、W4A8-INT4-MoE、FP8 online/per-channel/per-block MoE、embedding(Vocab/Hidden/LMHead)、norm(RMSNorm/RMSResNorm/LayerNorm)、attention、RtpModule 流式派发、NewModelLoader(scratch/fastsafetensors/EP filter)、LoRA 加载等大量组件,但测试仅覆盖 fp8 already-quantized Linear 的 load 路由(test_fp8_already_quantized.py)与 fp8-block forward 对照。AWQ/GPTQ/W4A8/online-MoE/加载器/LoRA 为零覆盖;verify_phase1_dispatch.py 为纯脚本式自检(非 unittest/parametrize),且这两个 CPU 可跑的用例被打上 H20 GPU 标签,占用稀缺 GPU 资源。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue QKV 的 FP8 per-block weight_scale_inv 在 num_kv_heads < tp_size (GQA/MQA) 时 TP 切分与权重复制逻辑不一致,会越界崩溃
    _dispatch_qkv_shard 中 weight 走 _split_qkv,对 num_heads < tp_size 的 k/v 采用"按 tp_rank%num_heads 复制 head"的逻辑;但同一 shard 的 weight_scale_inv 分支改用 heads_pp=max(1,num_heads//tp_size)rows=heads_pp*head_dimstart_blk=(tp_rank*rows)//blktensor.narrow(0, start_blk, rows//blk)。当 num_kv_heads < tp_size 时 rows=head_dim(通常 1 个 block),而 ckpt 中该 shard 的 scale dim0 只有 1 个 block,start_blk=tp_rank≥1 → narrow 越界报错。per-channel 分支通过复用 _split_qkv 正确处理了该 GQA 场景,唯独 block scale 路径没有对齐。

Strengths

  • 量化抽象分层合理:QuantizationConfig(携带 source_config,不重复解析 ckpt)与 QuantizeMethodBase(create_weights/apply/process_weights_after_loading 三段式)职责清晰,Linear 与 MoE 两套注册表按层类型派发,扩展点明确。
  • 流式加载设计到位:RtpModule.load_weights 逐张量下沉到叶子模块直接 copy,峰值内存控制在 model-params + 单张量;_ExpertRangeFilter 用单一正则 + 边界判断替换 O(num_experts) 的多分支正则,对 256/512 专家 MoE 有实际收益。
  • 安全性细节:torch.load(..., weights_only=True) 避免不可信 pickle RCE;VocabParallelEmbedding/ParallelLMHead 对 vocab_size % tp_size 做 fail-fast assert。
  • 数值一致性用心:融合 kernel 不可用时回退 fp32 eager 并一次性告警;FP8 per-tensor 融合、per-shard rescale 等都对照旧 loader 行为做了注释说明与端到端对照测试(TestFp8BlockForward / TestFp8AlreadyQuantizedForward)。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from dfee9dc to 8b4ef80 Compare July 7, 2026 10:04
@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/4 · P3/2

Blocking Issues

P1

  • LoRA 加载把 q/k/v 三投影映射到同一 engine key,静默丢弃 q/k adapter @ rtp_llm/models_py/model_loader.py:832
    • 建议:为 k_proj、v_proj 使用与引擎打包一致的独立 engine key(如 ...key_weight.kernel...value_weight.kernel);若引擎 QKV 为单一融合张量,则需按引擎打包顺序对 q/k/v 的 lora_A/lora_B 做拼接(参照 legacy loader 的 QKV 合并逻辑),而非三者写入同一槽位。并补充 LoRA 加载后校验(各投影 A/B 均非空)以 fail-fast。

Non-blocking Suggestions

P2

  • FP8 per-tensor MoE 融合恒重缩放首半,up_s>gate_s 时两半皆错且受 import 顺序影响 @ rtp_llm/models_py/quant_methods/fp8_moe.py:320
    • 建议:对 gate 半(w13[e, M_tp:])与 up 半分别按各自 scale/max_s 重缩放,或直接复用基类 _fuse_fp8_per_tensor_scales 的 dequant+requant 实现以消除首半 bug 与精度损失;统一 MoE 与 Linear 的 per-tensor 融合到单一实现。并在新 loader 构造 MoE 前显式 import 一次 quant_methods 以确定性填充注册表,去除 import 顺序依赖;若为匹配 golden 刻意保留旧数值,请用测试固定该行为并删除会误导的另一实现。
  • Row/Column 并行 Linear 的 forward 未执行 TP 集合通信 @ rtp_llm/models_py/layers/linear.py:47
    • 建议:在 RowParallelLinear.forward 中按 reduce_output 追加 all_reduce(..., group=Group.TP)、在 ColumnParallelLinear.forward 中按 gather_output 追加 all_gather(复用 embedding.py 的 distributed.collective_torch);若归约确实由上层/融合 kernel 负责,则移除这两个具误导性的参数并在文档中说明契约,避免调用方误用。
  • 无 EP filter 时权重加载退化为整文件载入,破坏流式内存目标 @ rtp_llm/models_py/model_loader.py:127
    • 建议:让 dense 路径也走 safe_open+get_tensor 的逐张量读取(filter 为空时全量 yield,不整文件 load),或在 weight_mapper._load_safetensors 改用 safe_open 流式读取,使有无 EP 过滤的内存峰值一致;并补充大文件加载峰值内存的校验以防回归。
  • scratch EP 加载路径对 ep_filter 二次 apply,重复过滤且禁用快路径 @ rtp_llm/models_py/model_loader.py:437
    • 建议:去掉 :437-438 的二次 ep_filter.apply(weights_iter)_get_all_weights 已通过 name_filter 完成过滤),或改为只保留一处过滤入口,避免重复正则匹配与语义歧义。

P3

  • **公开配置/层构造广泛使用 kwargs 透传 @ rtp_llm/models_py/model_loader.py:41
    • 建议:将 LoadConfig 关键字段(quant_source_configforce_cpu_load_weightsnum_layers 等)显式声明为构造参数或改用 dataclass;层构造函数如无多态转发需求应去掉 **kwargs,确需转发时限定白名单并对未知键 fail-fast。
  • 纯 CPU 逻辑的 verify_phase1_dispatch 被标记为 H20 GPU 测试 @ rtp_llm/models_py/quant_methods/test/BUILD:12
    • 建议:去掉 verify_phase1_dispatchtags/exec_properties/env 让其在 CPU 运行;并改写为标准 unittest/assert(或 pytest.mark.parametrize),使其成为可稳定断言的 CI 单测而非人工脚本。

Checklist ✅ (56 items passed)

Strengths

  • RtpModule.load_weights 采用逐张量流式派发,峰值内存维持在「模型参数 + 单个 ckpt 张量」,对大 MoE ckpt 友好。
  • torch.load(..., weights_only=True) 显式防御 .pt/.bin 的 pickle 任意代码执行,安全意识好。
  • VocabParallelEmbedding / ParallelLMHeadvocab_size % tp_size == 0 做 fail-fast 断言并给出可操作报错,避免静默切分错位。
  • 量化侧采用「QuantizationConfig 携带旧富对象 source_config + 三段式 method + 双注册表(Linear/MoE)」的分层抽象,扩展新量化类型时无需改中心逻辑,OCP 良好。
  • 覆盖 fp8(per-tensor/channel/block、在线/已量化)、awq、gptq、w4a8 多条路径,并提供大量 CPU 可运行的加载布局单测(TP 切分 / shard 合并 / QKV 拼接)。
  • LazyRegistryDict 在避免包初始化期 eager import 全部模型的同时,保持对 MODEL_REGISTRY 直接访问/遍历的向后兼容。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch 2 times, most recently from cfce460 to 43f4f27 Compare July 7, 2026 11:14
@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/2 · P3/1

Blocking Issues

P1

  • MergedColumnParallelLinear 的 FP8 per-tensor scale 融合在流式派发下静默损坏权重 @ rtp_llm/models_py/layers/linear.py:468
    • 建议:将 per-tensor scale 的收集从局部 per_tensor_collect 改为实例属性(跨流式调用累积),并把 max-merge 与 fp8 分片重标定移到 process_weights_after_loading(重写该方法,参照 QKVParallelLinear._merge_qkv_per_tensor_scales)。同时补一个「逐张量喂入」的单测(模拟 RtpModule 流式派发)覆盖 gate/up scale 不同的场景,防止回归。

Non-blocking Suggestions

P2

  • BaseMoEExperts 内置 FP8 _quant_family 逻辑已被 Fp8MoEMethod 取代,属死代码/重复且已出现漂移 @ rtp_llm/models_py/layers/moe_experts.py:185
    • 建议:删除 BaseMoEExperts 内已被 Fp8MoEMethod 覆盖的 FP8 死分支(或明确保留为唯一实现、由 method 委托调用),并更新过时注释。若确需保留兜底,应消除与 method 的重复实现(抽公共 helper),避免两份逻辑长期漂移导致后续误用其中带 bug 的一份。
  • gptq.py / weight_only.py 未注册且 create_weights 与加载路径不兼容 @ rtp_llm/models_py/quant_methods/gptq.py:9
    • 建议:从本 PR 移除 gptq.py / weight_only.py(待后续对应 loader 打通再引入),或补齐注册、导出与可用的 create_weights/load_weights 契约并加单测。当前状态既增加维护面又易误导读者以为已支持 GPTQ/weight-only。

P3

  • **LoadConfig 采用 kwargs + setattr 透传,形成无类型配置袋 @ rtp_llm/models_py/model_loader.py:42
    • 建议:将实际使用的字段(quant_source_configforce_cpu_load_weightsnum_layers 等)显式列为带类型标注的构造参数(或改用 dataclass),仅在确有需要时保留有限的扩展入口,减少 getattr 默认值散落。

Checklist Violations (5 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue MergedColumnParallelLinear 的 FP8 per-tensor scale 融合在流式派发下静默损坏权重
    MergedColumnParallelLinear.load_weights局部变量 per_tensor_collect 收集 gate/up 的标量 weight_scale,并在函数末尾做 max-merge + 对非 max 分片的 fp8 权重按 own/max 重标定。但 RtpModule.load_weights 的流式契约是「每次只喂一个张量」(layer.load_weights({f"{prefix}.{rest}": tensor}))。因此每次调用 per_tensor_collect 只含单个分片:max_scale 退化为该分片自身,重标定循环空转,且 param.data.fill_(max_scale)最后到达的分片覆盖。当 gate/up 的 ckpt scale 不同(compressed-tensors FP8 per-tensor 融合 gate_up)时,gate 的 fp8 值被按 up 的 scale 解释,process_weights_after_loading 再据此
  • [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue BaseMoEExperts 内置 FP8 _quant_family 逻辑已被 Fp8MoEMethod 取代,属死代码/重复且已出现漂移
    BaseMoEExperts 里注释称「当前 fp8/unquantized 尚未注册 MoE 方法 → 走内置 _quant_family 逻辑」,但 Fp8MoEMethod 已通过 @register_moe_quant_method 注册了 fp8/FP8_PER_*/*_online 等全部键;get_quant_method 对这些键必返回非 None 方法,_dispatch_scale/process_weights_after_loading 首行即 if self.quant_method is not None: ...; return。因此 _init_buffers/_dispatch_scale 的 fp8 分支、_copy_per_channel_scale_copy_block_scale_fuse_fp8_*_online_quantize_*(约 200+ 行)在当前注册下均不可达,且与 Fp8MoEMethod 逐字重复。更值得注意的是二者已开始漂移:`BaseMoEExpert
  • [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue gptq.py / weight_only.py 未注册且 create_weights 与加载路径不兼容
    GPTQLinearMethodWeightOnlyLinearMethod 均无 @register_quant_method 装饰器,也未在 quant_methods/__init__.py 导入/导出,因此 QuantizationConfig.get_quant_methodgptq/weight-only 只会走到 raise ValueError("Unsupported linear quant_type ..."),不可达。此外两者的 create_weightstorch.empty(0, ...) 注册 0 尺寸参数,而 ColumnParallelLinear/RowParallelLinear.load_weightsqweight/scales/zeros 会在 tensor.shape != param.shape 时进入既非 view_as(要求 1D→2D 且元素数相等)也非 .t()(仅限 weight)的分支,最终 raise ValueError shape 不匹
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue MergedColumnParallelLinear 的 FP8 per-tensor scale 融合在流式派发下静默损坏权重
    MergedColumnParallelLinear.load_weights局部变量 per_tensor_collect 收集 gate/up 的标量 weight_scale,并在函数末尾做 max-merge + 对非 max 分片的 fp8 权重按 own/max 重标定。但 RtpModule.load_weights 的流式契约是「每次只喂一个张量」(layer.load_weights({f"{prefix}.{rest}": tensor}))。因此每次调用 per_tensor_collect 只含单个分片:max_scale 退化为该分片自身,重标定循环空转,且 param.data.fill_(max_scale)最后到达的分片覆盖。当 gate/up 的 ckpt scale 不同(compressed-tensors FP8 per-tensor 融合 gate_up)时,gate 的 fp8 值被按 up 的 scale 解释,process_weights_after_loading 再据此

Python Static-First Checklist

  • [P.A] 静态结构与类型纪律 — 公开 API 禁止 **kwargs 透传 → issue LoadConfig 采用 **kwargs + setattr 透传,形成无类型配置袋
    LoadConfig.__init__(..., **kwargs) 末尾 for k, v in kwargs.items(): setattr(self, k, v)quant_source_configforce_cpu_load_weights 等关键字段仅经 kwargs 注入、下游到处 getattr(self.load_config, "xxx", default) 读取。作为对外加载入口,这使字段来源与类型不可见,IDE/pyright 无法校验拼写,违反「公开 API 禁止 **kwargs 透传」。

Strengths

  • 量化抽象分层清晰:QuantizationConfig(携带上游富配置 source_config,不重复解析 ckpt)与 QuantizeMethodBase 三段式钩子,Linear/MoE 双注册表按层类型派发,扩展点良好(OCP)。
  • 流式逐张量加载设计好:RtpModule.load_weights 每个张量直达 leaf 拷贝,峰值内存控制在「模型参数 + 单个 ckpt 张量」,注释充分。
  • 安全实践到位:torch.load(..., weights_only=True) 明确规避不可信 pickle RCE。
  • FP8 各子族(per-tensor / per-channel / per-block、已量化 / 在线)覆盖较全并带单测;QKVParallelLinear 的 per-tensor scale 采用实例状态跨调用累积 + post-load 融合,是流式场景下的正确写法,可作为修复参照。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch 2 times, most recently from 2110267 to 1b8034e Compare July 7, 2026 12:46
@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/3 · P3/2

Blocking Issues

P1

  • FP8 per-channel weight_scale 形状 [1,N] 与单测断言 [N,1] 不一致,H20 测试必失败 @ rtp_llm/models_py/quant_methods/fp8.py:49
    • 建议:将上述四处断言统一改为 (1, N)(与 apply()scale_b 约定一致);若确希望保持 [N, 1] 语义,则需同时调整 apply()scale_b。二者取一,保证实现与测试一致,使 test_fp8_already_quantized 在 H20 通过。

Non-blocking Suggestions

P2

  • QKVParallelLinear per-channel 1D scale 未归一,与 MergedColumn 不一致可致 load 崩溃 @ rtp_llm/models_py/layers/linear.py:713
    • 建议:在 QKV per-channel 写入前补 1D→2D 归一(与 MergedColumnParallelLinear 一致),或抽取统一的 scale 归一/写入 helper 供两处复用,避免 1D per-channel weight_scale 的 ckpt 加载崩溃。
  • RtpModule 静默丢弃无法匹配的权重,存在未初始化参数静默上线风险 @ rtp_llm/models_py/module_base.py:138
    • 建议:区分「预期可丢弃」(如 rotary_emb.inv_freq)白名单与「意外丢弃」;对意外丢弃提供严格模式报错,或在加载后校验是否存在未被任何 ckpt tensor 覆盖的参数并报错/计数,避免未初始化权重静默进入服务。
  • AWQLinearMethod 每次 forward 全量反量化,decode 热路径性能退化 @ rtp_llm/models_py/quant_methods/awq.py:100
    • 建议:后续接入融合 w4a16 gemm;若短期无法融合,评估「首次反量化后缓存 bf16 权重」与显存占用的权衡。当前作为首版可接受,但建议在注释/PR 描述中明确标注该性能限制及后续计划。

P3

  • LazyRegistryDict 未重写 values(),与 keys/items 视图不一致 @ rtp_llm/models_py/registry.py:19
    • 建议:补充 values() 重写(遍历 _registry_keys() 触发惰性加载),或在文档中明确 values() 语义,保证 keys/items/values 三者一致。
  • **LoadConfig 以 kwargs + setattr 承接配置字段,降低可见性易拼写错 @ rtp_llm/models_py/model_loader.py:41
    • 建议:将确实使用的字段(quant_source_configforce_cpu_load_weightsload_method 等)显式声明为构造参数,或改用 dataclass,避免 **kwargs 静默接收未知/拼错的配置项。

Checklist Violations (6 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue RtpModule 静默丢弃无法匹配的权重,存在未初始化参数静默上线风险
    RtpModule.load_weights(module_base.py:86-140)对无法匹配到子模块的权重仅 dropped.append(...),结尾以 logger.warning("... had no matching submodule and were dropped ...") 告警,不报错。新 loader 经 to_empty/torch.empty 分配未初始化参数后再流式赋值,一旦 ckpt 命名与模块树不符(weight_mapper 遗漏、rename),对应参数将保持未初始化垃圾值而加载「成功」,产生静默错误输出而非 fail-fast。
  • [6.1] Software Engineering — LSP:子类/重写保持基类契约 → issue LazyRegistryDict 未重写 values(),与 keys/items 视图不一致
    LazyRegistryDict(registry.py:19)重写了 keys/__iter__/__len__/items 使其覆盖 _LAZY_MODEL_REGISTRY 中尚未物化的模型,但未重写 values()MODEL_REGISTRY.values() 只返回已物化(已 import)的类,与 keys()/items() 的视图不一致,外部若按 values() 迭代会遗漏惰性注册的模型,违反字典契约。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue FP8 per-channel weight_scale 形状 [1,N] 与单测断言 [N,1] 不一致,H20 测试必失败
    _requant_per_channel_to_runtime_fp8(fp8.py:47-49、61-62)两个分支都 return ..., scale_rows.view(1, -1),即 weight_scale 形状为 [1, N]。而 test_fp8_already_quantized.pytest_load_into_column_parallel_normalize_1d_scale(:96)、row_parallel(:130)、merged per-channel(:166)、qkv per-channel(:289)均 assertEqual(layer.weight_scale.shape, (N, 1))[1, N] != [N, 1],该 py_test(tags=["H20"])目标必然失败。[1, N] 恰是 apply()torch._scaled_mmscale_b(rowwise)所需形状,故实现正确、断言不符。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue QKVParallelLinear per-channel 1D scale 未归一,与 MergedColumn 不一致可致 load 崩溃
    QKVParallelLinear._dispatch_qkv_shard 的 per-channel 分支(linear.py:706-713)直接 param.data[offset:offset+size].copy_(split)weight_scale param 为 2D [total, 1](Fp8PerChannelLinearMethod.create_weights: torch.ones(output_size, 1)),而 split 由 1D ckpt scale 经 _split_qkv 得到仍是 1D [size];[size] copy[size, 1] 不可广播会抛错。对照 MergedColumnParallelLinear 同类分支有 if tensor.dim()==1 and param.dim()==2: tensor = tensor.view(-1, 1) 归一,QKV 缺失。test_qkv_per_channel_scale_cat 用 1D scale 即触发。_

RTP-LLM Checklist

  • [I] 代码质量 — 同一功能用统一工具函数 → issue QKVParallelLinear per-channel 1D scale 未归一,与 MergedColumn 不一致可致 load 崩溃
    QKVParallelLinear._dispatch_qkv_shard 的 per-channel 分支(linear.py:706-713)直接 param.data[offset:offset+size].copy_(split)weight_scale param 为 2D [total, 1](Fp8PerChannelLinearMethod.create_weights: torch.ones(output_size, 1)),而 split 由 1D ckpt scale 经 _split_qkv 得到仍是 1D [size];[size] copy[size, 1] 不可广播会抛错。对照 MergedColumnParallelLinear 同类分支有 if tensor.dim()==1 and param.dim()==2: tensor = tensor.view(-1, 1) 归一,QKV 缺失。test_qkv_per_channel_scale_cat 用 1D scale 即触发。_

Python Static-First Checklist

  • [P.A] 静态结构与类型纪律 — 公开 API 禁止 **kwargs 透传 → issue LoadConfig 以 **kwargs + setattr 承接配置字段,降低可见性易拼写错
    LoadConfig.__init__(model_loader.py:21-41)以 **kwargs 收尾并 for k, v in kwargs.items(): setattr(self, k, v),后续多处以 getattr(self, "quant_source_config"/"force_cpu_load_weights", ...) 读取这些动态注入的属性。公开构造函数的 **kwargs 透传 + 动态 setattr 使配置字段不可见,拼写错误的配置项会被静默吞入而不报错。

Strengths

  • 量化抽象分层清晰:QuantizationConfig(携带旧 loader 解析出的 source_config,不重复解析 ckpt)+ QuantizeMethodBase 三段式钩子 + Linear/MoE 双注册表,新增量化方法只需注册,扩展性好。
  • 流式加载(RtpModule.load_weights)将峰值显存控制在「模型参数 + 单个 ckpt tensor」,避免中间 dict 缓冲,process_weights_after_loading 在所有 shard 落位后统一后处理的时机设计正确。
  • FP8 per-tensor 的 gate/up、q/k/v scale 融合正确处理了「各 shard 以各自 scale 量化」的精度陷阱(dequant→restack→requant 或 max-rescale),并附详尽注释说明为何不能简单 max-merge。
  • .pt/.bin 加载统一使用 torch.load(..., weights_only=True),规避不可信 pickle 的 RCE 风险。
  • 单测覆盖了 Col/Row/Merged/QKV 在 per-tensor / per-channel / per-block 下的多种加载路由与 TP 切分。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from 1b8034e to 325cd21 Compare July 7, 2026 13:50
@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/0 · P2/4 · P3/3

Non-blocking Suggestions

P2

  • BaseMoEExperts 内置 FP8 死代码与已注册的 Fp8MoEMethod 算法分歧 @ rtp_llm/models_py/layers/moe_experts.py:1498
    • 建议:删除 BaseMoEExperts 中已被 Fp8MoEMethod 取代的内置 FP8 分支,仅保留 quant_method 委托与 unquantized 兜底;同步修正 line 1496 误导性注释。若确需保留兜底,应让两条路径共用同一融合/在线量化工具函数(R.I.1),避免算法漂移。
  • MoE 权重缓冲用 torch.empty 且无加载完整性校验,命名不匹配会静默产生垃圾权重 @ rtp_llm/models_py/layers/moe_experts.py:1660
    • 建议:在 process_weights_after_loading 起始处断言 self._loaded_count == self._expected_count(不等时报出缺失的 expert/proj 明细);或将 w13/w2 改为 torch.zeros 并在流式派发中记录已写入的 (expert, proj) 集合做完整性校验,使不完整加载 fail-fast 而非静默出错。
  • embedding.py 顶层硬依赖编译 op,破坏 layers 包的可移植回退承诺 @ rtp_llm/models_py/layers/embedding.py:6
    • 建议:将 embedding.pyrtp_llm_ops 的导入移入使用它的方法内部(如 HiddenParallelEmbedding.forward),与其余层保持一致的惰性导入 + 兜底策略;或在 layers/__init__.py 改为惰性/按需导出,避免导入整个 layers 包即触发编译 op 依赖。
  • 大型 PR 关键新逻辑缺单测(MoE 量化 / 流式派发 / EP 过滤 / fastsafetensors) @ rtp_llm/models_py/quant_methods/test/BUILD:1
    • 建议:至少补充:BaseMoEExperts 在 tp/ep 各组合下 gate/up/down 分片落位与 scale 融合的 CPU 单测;RtpModule redirect 与 dropped-weight raise 行为的单测;_create_ep_filter/_ExpertRangeFilter 的专家区间边界单测。GPU 相关可沿用 torch.cuda.is_available() 门控。

P3

  • model_loader 引用未定义的 _IMPORT_ERRORS,诊断信息恒为空 @ rtp_llm/models_py/model_loader.py:629
    • 建议:要么在 __init__.py 中实际维护 _IMPORT_ERRORS(在模型 lazy import 失败处记录),要么删除该已失效的诊断分支,避免给出「有诊断实则无」的错觉。
  • WeightsMapper.apply 的 None 判空为死分支且语义含糊 @ rtp_llm/models_py/weight_mapper.py:38
    • 建议:明确 map_name 的契约:若不允许返回 None 则把返回类型改为 str 并删除 apply 中的判空;若确需支持「丢弃未映射」则让 map_name 在该情形返回 None 并补测该分支。
  • 线性层权重按转置形状匹配自动转置,方阵场景可能掩盖错误朝向 @ rtp_llm/models_py/layers/linear.py:728
    • 建议:自动转置分支加更强前置条件(如仅在明确来源约定需要转置时启用,或对方阵额外校验),或将转置行为记一条 warning/debug 日志便于事后排查。

Checklist Violations (10 fail / 60 total)

General Principles Checklist

  • [6.1] Architecture — 兼容性:公开 API/持久数据/配置/环境迁移安全 → issue embedding.py 顶层硬依赖编译 op,破坏 layers 包的可移植回退承诺
    embedding.py 在模块顶层 from rtp_llm.ops.compute_ops import rtp_llm_ops,而 layers/__init__.py 又 eager 导入 embeddingfrom ...embedding import ParallelLMHead, VocabParallelEmbedding)。因此任何 from rtp_llm.models_py.layers import ...(含只用到 LinearBase/RMSNorm 的场景)都会强制加载编译 op 的 .so;在未编译该 op 的 CPU/可移植构建上会直接 ImportError。这与同目录 activation.py/norm.py/linear.py 刻意采用「函数内惰性 import + is_cuda 判定 + fp32 eager 兜底」以保证可移植的设计明显不一致。
  • [6.1] Architecture — 可观测性:日志/指标/超时可操作、非噪声 → issue model_loader 引用未定义的 _IMPORT_ERRORS,诊断信息恒为空
    _create_model 在注册表为空时尝试 from rtp_llm.models_py import _IMPORT_ERRORS 以拼接模型导入错误诊断,但新增的 rtp_llm/models_py/__init__.py 从未定义 _IMPORT_ERRORS。该 import 必抛 ImportError,被 except (ImportError, AttributeError) 吞掉,diag 恒为空。结果是「registry 为空导致模型找不到」这一最需要根因信息的失败场景,诊断反而失效。
  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue 线性层权重按转置形状匹配自动转置,方阵场景可能掩盖错误朝向
    ColumnParallelLinear/RowParallelLinear.load_weightstensor.shape != param.shape 时,对 param_name == "weight"tensor.t().shape == param.shape 自动做 tensor.t().contiguous()。当权重为方阵(N == K)时 t().shape 恒等于原 shape,一个真正朝向错误的 ckpt 张量会被静默转置接受而非报错,可能引入难以察觉的数值错误。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue WeightsMapper.apply 的 None 判空为死分支且语义含糊
    WeightsMapper.map_name 在无任何映射命中时返回原始 name(永不返回 None),而 apply 却写成 if mapped_name is not None: yield ...。该判空恒为真,是死代码;若原意是「未映射的权重应被丢弃」,当前实现反而会静默保留所有未映射张量,与函数签名 -> Optional[str] 暗示的语义相互矛盾。
  • [6.1] Software Engineering — DRY:重复非平凡逻辑被抽取或显式复用 → issue BaseMoEExperts 内置 FP8 死代码与已注册的 Fp8MoEMethod 算法分歧
    moe_experts.py_quant_family 分支(_init_buffers 的 fp8 分支、_fuse_fp8_per_tensor_scales/_online_quantize_*/_copy_block_scale/_dispatch_scale fp8 分支、_build_weights_dict 的 fp8 elif,约 300 行)仅在 self.quant_method is None 时执行;而 quant_methods/__init__.py 已导入并注册 Fp8MoEMethod(覆盖 fp8/FP8_PER_BLOCK/... 全部 key),get_quant_method 对 MoE 层必返回 Fp8MoEMethod 非 None,故内置路径不可达。line 1496 注释仍称「当前 fp8/unquantized 尚未注册 MoE 方法」,与事实不符。更关键的是两套实现算法分歧:内置 _fuse_fp8_per_tensor_scales 用「反量化+重量化」(注释自称更精确)
  • [6.1] Software Engineering — KISS/YAGNI:无投机性抽象 → issue BaseMoEExperts 内置 FP8 死代码与已注册的 Fp8MoEMethod 算法分歧
    moe_experts.py_quant_family 分支(_init_buffers 的 fp8 分支、_fuse_fp8_per_tensor_scales/_online_quantize_*/_copy_block_scale/_dispatch_scale fp8 分支、_build_weights_dict 的 fp8 elif,约 300 行)仅在 self.quant_method is None 时执行;而 quant_methods/__init__.py 已导入并注册 Fp8MoEMethod(覆盖 fp8/FP8_PER_BLOCK/... 全部 key),get_quant_method 对 MoE 层必返回 Fp8MoEMethod 非 None,故内置路径不可达。line 1496 注释仍称「当前 fp8/unquantized 尚未注册 MoE 方法」,与事实不符。更关键的是两套实现算法分歧:内置 _fuse_fp8_per_tensor_scales 用「反量化+重量化」(注释自称更精确)
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 大型 PR 关键新逻辑缺单测(MoE 量化 / 流式派发 / EP 过滤 / fastsafetensors)
    现有单测(test_fp8_already_quantized.py)只覆盖「已量化 FP8 Linear」的 load/TP/QKV/merged 分片与前向;而本 PR 风险最高的部分没有单测:Fp8MoEMethod(per-tensor/channel/block 融合与在线量化)、W4A8Int4MoEMethod(compressed repack + online int4)、module_base.RtpModule 的流式派发/redirect/dropped 校验、model_loader._ExpertRangeFilter 的 EP 切分边界、以及 fastsafetensors 加载与 monkey-patch。large_pr_route 已启用(churn≥3000、risk_tags≥4),这些正是最需要边界覆盖(空/单专家/tp=ep 组合、命名不匹配)的路径。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue MoE 权重缓冲用 torch.empty 且无加载完整性校验,命名不匹配会静默产生垃圾权重
    _init_bufferstorch.empty(...)(未初始化内存)分配 w13/w2__init__ 计算了 self._expected_count = num_local_experts * len(PROJ_NAMES) 并在 load_weights 里累加 _loaded_count,但全程从不比较两者。load_weights 对不满足 {expert_id}.{proj}.{param} 格式的名字直接 continuelen(parts) < 3int(parts[0])ValueError 时静默跳过)。因此若某专家/投影张量缺失或命名规则与预期不符,对应缓冲保持 torch.empty 的随机值被当作权重使用,产生静默错误输出且无任何报错。这与同 PR RtpModule.load_weights 对 dropped 权重显式 raise 的契约不一致。

RTP-LLM Checklist

  • [H] 测试与 CI — 测试覆盖充分:大重构等价覆盖,新功能端到端测试 → issue 大型 PR 关键新逻辑缺单测(MoE 量化 / 流式派发 / EP 过滤 / fastsafetensors)
    现有单测(test_fp8_already_quantized.py)只覆盖「已量化 FP8 Linear」的 load/TP/QKV/merged 分片与前向;而本 PR 风险最高的部分没有单测:Fp8MoEMethod(per-tensor/channel/block 融合与在线量化)、W4A8Int4MoEMethod(compressed repack + online int4)、module_base.RtpModule 的流式派发/redirect/dropped 校验、model_loader._ExpertRangeFilter 的 EP 切分边界、以及 fastsafetensors 加载与 monkey-patch。large_pr_route 已启用(churn≥3000、risk_tags≥4),这些正是最需要边界覆盖(空/单专家/tp=ep 组合、命名不匹配)的路径。
  • [I] 代码质量 — 同一功能用统一工具函数 → issue BaseMoEExperts 内置 FP8 死代码与已注册的 Fp8MoEMethod 算法分歧
    moe_experts.py_quant_family 分支(_init_buffers 的 fp8 分支、_fuse_fp8_per_tensor_scales/_online_quantize_*/_copy_block_scale/_dispatch_scale fp8 分支、_build_weights_dict 的 fp8 elif,约 300 行)仅在 self.quant_method is None 时执行;而 quant_methods/__init__.py 已导入并注册 Fp8MoEMethod(覆盖 fp8/FP8_PER_BLOCK/... 全部 key),get_quant_method 对 MoE 层必返回 Fp8MoEMethod 非 None,故内置路径不可达。line 1496 注释仍称「当前 fp8/unquantized 尚未注册 MoE 方法」,与事实不符。更关键的是两套实现算法分歧:内置 _fuse_fp8_per_tensor_scales 用「反量化+重量化」(注释自称更精确)

Strengths

  • 流式派发/后处理钩子的设计与「为何放在 process_weights_after_loading 而非 load_weights」的权衡都有详尽内联注释,显著降低理解成本。
  • torch.load(..., weights_only=True) 显式防止不可信 .pt/.bin 的任意代码执行;VocabParallelEmbedding/ParallelLMHeadvocab_size % tp_size fail-fast。
  • _ExpertRangeFilter 用单次 O(1) 正则 + 边界判断替换旧的 O(num_experts) 交替正则,对 256/512 专家 ckpt 是实打实的加载提速。
  • LazyRegistryDict 保持对 MODEL_REGISTRY 直接迭代/访问的外部代码 100% 向后兼容,同时避免包初始化即 eager import 全部模型。
  • NewModelLoader 对 fastsafetensors 显存 headroom 做了容量预估与降级到 scratch 的兜底;RtpModule.load_weights 对未匹配权重 fail-fast(除白名单 rotary 缓存)。
  • 已量化 FP8 per-tensor / per-channel / per-block 的 Linear 加载与 TP/QKV/merged 分片,均有针对性的 CPU 单测(含 offline vs online 前向一致性)。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from 325cd21 to 0d397be Compare July 7, 2026 15:00
@LLLLKKKK

LLLLKKKK commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/3 · P2/1 · P3/0

Blocking Issues

P1

  • Linear 权重加载缺少完整性校验,缺失 shard 会保留未初始化参数 @ rtp_llm/models_py/layers/linear.py:129
    • 建议:为 Linear/QKV/MergedColumn 维护 required loaded keys,并在 post-load 前校验必需权重和必需 scale;未知 param 不应静默跳过。补充缺失 weight、缺失 weight_scale、缺失 q/k/v shard 的单测。
  • MoE 量化 scale 未纳入完整性校验 @ rtp_llm/models_py/layers/moe_experts.py:433
    • 建议:按 quant family 定义必需辅助 tensor 集合,把 scale/meta 加入 loaded-key 校验;dispatch_scale 应记录加载状态,post-load 前对每个 local expert 的 gate/up/down scale fail-fast。补充缺失 scale 的负向测试。
  • FP8 Linear forward 对非连续输入会在 view 处崩溃 @ rtp_llm/models_py/quant_methods/fp8.py:284
    • 建议:改为 x.reshape(-1, x.shape[-1]),或先 x = x.contiguous()view;为 FP8 per-tensor、per-channel、block 路径增加非连续输入测试。

Non-blocking Suggestions

P2

  • 测试文件使用整文件裸 type ignore @ rtp_llm/models_py/quant_methods/test/test_fp8_already_quantized.py:1
    • 建议:删除整文件 # type: ignore;如确有单处类型问题,改为最小作用域的 # type: ignore[具体错误码] 并保留原因说明。

Checklist Violations (5 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue MoE 量化 scale 未纳入完整性校验
    _check_load_complete 只检查 (expert_id, proj) 权重是否加载;dispatch_scale 不记录 FP8/W4A8 的 weight_scale/weight_scale_inv。缺 scale 时,FP8 会使用初始化为 0 的 scale,W4A8 compressed 会使用 torch.empty pending scale,仍可构建 fused MoE。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue MoE 量化 scale 未纳入完整性校验
    _check_load_complete 只检查 (expert_id, proj) 权重是否加载;dispatch_scale 不记录 FP8/W4A8 的 weight_scale/weight_scale_inv。缺 scale 时,FP8 会使用初始化为 0 的 scale,W4A8 compressed 会使用 torch.empty pending scale,仍可构建 fused MoE。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue FP8 Linear forward 对非连续输入会在 view 处崩溃
    多个 FP8 apply 分支先执行 x.view(-1, x.shape[-1]),再检查 input_2d.is_contiguous()。非连续 hidden states 在 view 处已抛 RuntimeError,与普通 torch.nn.functional.linear 可接受非连续输入的契约不一致。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue FP8 Linear forward 对非连续输入会在 view 处崩溃
    多个 FP8 apply 分支先执行 x.view(-1, x.shape[-1]),再检查 input_2d.is_contiguous()。非连续 hidden states 在 view 处已抛 RuntimeError,与普通 torch.nn.functional.linear 可接受非连续输入的契约不一致。

Python Static-First Checklist

  • [P.H] 类型标注 — # type: ignore 必须带 error code → issue 测试文件使用整文件裸 type ignore
    测试文件首行是裸 # type: ignore,没有 error code,也会屏蔽整个文件的类型问题。该文件覆盖新量化 loader 关键路径,整文件屏蔽会降低后续类型回归可见性。

Strengths

  • 新增 FP8 per-tensor/per-channel/per-block 的分片 scale 合并测试,覆盖了 QKV、MergedColumn 和 TP 场景。
  • MoE 权重路径把 EP/TP remap、量化 method 分发和 fused_moe 构建拆开,职责边界比把所有逻辑堆在 loader 中更清楚。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch 2 times, most recently from 5d391e1 to 09d0cf3 Compare July 8, 2026 07:41
@LLLLKKKK

LLLLKKKK commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/0 · P3/0

Blocking Issues

P1

  • 新 loader 没有任何模型注册来源 @ rtp_llm/models_py/registry.py:11
    • 建议:补齐模型类型到 model_desc 类的 lazy registry,或在对应模型类上显式使用 register_model;同时增加一个最小单测,验证至少一个已有 model_type 能通过 NewModelLoader._create_model() 命中 registry。
  • QKV TP 切分会丢弃非整除 KV heads @ rtp_llm/models_py/layers/linear.py:611
    • 建议:对齐现有 attention config 的 GCD 分组策略,或在 num_heads/num_kv_headstp_size 不满足支持矩阵时显式抛错;补充 kv_head_num % tp_size != 0kv_head_num > tp_size 的加载/shape 单测。

Checklist Violations (3 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue QKV TP 切分会丢弃非整除 KV heads
    num_kv_heads_per_partition = max(1, num_kv_heads // tp_size)_split_qkv() 也用 floor division。若 num_kv_heads=6,tp_size=4,每 rank 只加载 1 个 KV head,总共 4 个,剩余 2 个 checkpoint head 被静默丢弃;现有 attention config 支持 GCD 分组语义,新路径未对齐也未 fail-fast。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 新 loader 没有任何模型注册来源
    _LAZY_MODEL_REGISTRY 初始化为空,diff 内也没有任何模型类调用 register_model;而 NewModelLoader._create_modelmodel_type not in MODEL_REGISTRY 时直接抛错。因此当前 NewModelLoader.load() 对任意现有模型都会在建模阶段失败。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue QKV TP 切分会丢弃非整除 KV heads
    num_kv_heads_per_partition = max(1, num_kv_heads // tp_size)_split_qkv() 也用 floor division。若 num_kv_heads=6,tp_size=4,每 rank 只加载 1 个 KV head,总共 4 个,剩余 2 个 checkpoint head 被静默丢弃;现有 attention config 支持 GCD 分组语义,新路径未对齐也未 fail-fast。

Strengths

  • 新增的 streaming load 完整性检查能在缺失权重时 fail-fast,避免继续使用 torch.empty 未初始化参数。
  • FP8 已量化路径补了多类 shard merge 与缺失 scale 的单测,覆盖了部分高风险加载分支。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from 09d0cf3 to 6096d1b Compare July 8, 2026 09:26
@LLLLKKKK

LLLLKKKK commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/3 · P2/0 · P3/0

Blocking Issues

P1

  • scratch 路径会跳过带 state_dict/model 容器的 .bin/.pt 权重 @ rtp_llm/models_py/model_loader.py:135
    • 建议:把 .bin/.ptstate_dict/model 解包逻辑统一放到 weight_mapper._load_pytorch(),或让 _get_all_weights() 不在无 filter 时绕过本地解包实现;补一个 ep_size=1 的 wrapped .pt/.bin 回归测试。
  • BERT new loader 丢失 gamma/beta LayerNorm 命名兼容 @ rtp_llm/models_py/new_models/bert/language.py:74
    • 建议:在 embedding 和 layer mapping 中同时支持 LayerNorm.gamma/LayerNorm.beta,并加一个使用 gamma/beta 命名的 BERT/Roberta load 回归测试,确保迁移不破坏旧 checkpoint。
  • AWQ 注册路径每次 forward 都全量反量化权重 @ rtp_llm/models_py/quant_methods/awq.py:99
    • 建议:不要把 reference AWQ path 作为可用生产 quant method 注册;要么在 post-load 阶段一次性反量化并明确这是 fallback,要么接入 fused/cache-backed w4a16 GEMM。补 AWQ forward 性能/行为测试,防止 decode 热路径误用。

Checklist Violations (6 fail / 94 total)

General Principles Checklist

  • [6.1] Architecture — 兼容性:公开 API/持久数据/配置/环境迁移安全 → issue BERT new loader 丢失 gamma/beta LayerNorm 命名兼容
    新 BERT 映射只接受 LayerNorm.weight/LayerNorm.bias。现有 BERT/Roberta 兼容逻辑支持 checkpoint 使用 LayerNorm.gamma/LayerNorm.beta;这些权重在新 loader 中会被归入 unexpected dropped tensors,导致旧兼容 checkpoint 加载失败。
  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue scratch 路径会跳过带 state_dict/model 容器的 .bin/.pt 权重
    非 EP scratch 加载时 name_filter is None,这里优先走 weight_mapper.get_all_weights()return。但 weight_mapper._load_pytorch() 只 yield 顶层 tensor;对 {"state_dict": {...}}{"model": {...}}.bin/.pt 会跳过嵌套 dict,流出 0 个权重。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue AWQ 注册路径每次 forward 都全量反量化权重
    AWQLinearMethod 已注册为 "awq",但 apply() 每次 forward 都调用 awq_dequantize_triton() 生成完整 dense weight,再 torch.matmul。该 Triton helper 每次分配 result,注释也说明 fused/cache-backed GEMM 应在进入 decode 热路径前替换。

RTP-LLM Checklist

  • [B] 正确性与逻辑 — 逻辑错误、off-by-one、null/zero 检查 → issue scratch 路径会跳过带 state_dict/model 容器的 .bin/.pt 权重
    非 EP scratch 加载时 name_filter is None,这里优先走 weight_mapper.get_all_weights()return。但 weight_mapper._load_pytorch() 只 yield 顶层 tensor;对 {"state_dict": {...}}{"model": {...}}.bin/.pt 会跳过嵌套 dict,流出 0 个权重。
  • [D] 性能 — GPU hot path 避免隐式 workspace/memset/default-stream 同步的 torch/第三方算子 → issue AWQ 注册路径每次 forward 都全量反量化权重
    AWQLinearMethod 已注册为 "awq",但 apply() 每次 forward 都调用 awq_dequantize_triton() 生成完整 dense weight,再 torch.matmul。该 Triton helper 每次分配 result,注释也说明 fused/cache-backed GEMM 应在进入 decode 热路径前替换。
  • [D] 性能 — hot path per-forward 内存分配 → issue AWQ 注册路径每次 forward 都全量反量化权重
    AWQLinearMethod 已注册为 "awq",但 apply() 每次 forward 都调用 awq_dequantize_triton() 生成完整 dense weight,再 torch.matmul。该 Triton helper 每次分配 result,注释也说明 fused/cache-backed GEMM 应在进入 decode 热路径前替换。

Strengths

  • FP8 linear/MoE 路径补了较多加载完整性检查,能在缺少 scale 或 shard 时 fail fast。
  • QKV/MergedColumn 的 per-tensor scale 合并考虑了 streaming dispatch 顺序,避免了单 shard 到达时提前后处理。
  • 新增测试覆盖了 FP8 already-quantized、block scale、QKV/merged scale 拼接和基础 load integrity。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from 6096d1b to 2612b43 Compare July 8, 2026 10:04
@LLLLKKKK

LLLLKKKK commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/0 · P3/0

Blocking Issues

P1

  • 通用 Linear TP 分片对非整除维度会静默丢权重 @ rtp_llm/models_py/layers/linear.py:129
    • 建议:在 Column/Row 构造和 _split_weight() 中对参与 TP 切分的维度做整除校验并 fail-fast,或实现明确的 padding/ceil 分片语义;补充非整除维度的负向单测。
  • FP8 MoE 已量化路径没有转换 ROCm runtime FP8 dtype @ rtp_llm/models_py/quant_methods/fp8_moe.py:116
    • 建议:为 MoE per-tensor/per-channel 已量化路径增加与 Linear 一致的 runtime dtype 转换和 scale 修正;至少在 ROCm 上增加一个 FP8 MoE load/postprocess 单测,校验 w13/w2 dtype 与 scale 语义。

Checklist Violations (4 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue FP8 MoE 已量化路径没有转换 ROCm runtime FP8 dtype
    Fp8MoEMethod.create_weights() 对已量化 MoE 权重固定分配 torch.float8_e4m3fn_fuse_per_tensor/_fuse_per_channel 只合并 scale,没有像 Linear FP8 一样 requant 到 _runtime_fp8_dtype()。ROCm executor 会把 quant dtype 设为 get_rocm_fp8_dtype(),因此会收到 dtype/scale 语义不匹配的 MoE 权重。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue 通用 Linear TP 分片对非整除维度会静默丢权重
    ColumnParallelLinearoutput_size // tp_size 建本地参数,RowParallelLinearinput_size // tp_size_split_weight() 也用 floor division。若 output/input 维度不能整除 TP,例如 10/3,各 rank 合计只加载 9 行或列,尾部权重静默丢失且 forward 仍继续。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue FP8 MoE 已量化路径没有转换 ROCm runtime FP8 dtype
    Fp8MoEMethod.create_weights() 对已量化 MoE 权重固定分配 torch.float8_e4m3fn_fuse_per_tensor/_fuse_per_channel 只合并 scale,没有像 Linear FP8 一样 requant 到 _runtime_fp8_dtype()。ROCm executor 会把 quant dtype 设为 get_rocm_fp8_dtype(),因此会收到 dtype/scale 语义不匹配的 MoE 权重。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 通用 Linear TP 分片对非整除维度会静默丢权重
    ColumnParallelLinearoutput_size // tp_size 建本地参数,RowParallelLinearinput_size // tp_size_split_weight() 也用 floor division。若 output/input 维度不能整除 TP,例如 10/3,各 rank 合计只加载 9 行或列,尾部权重静默丢失且 forward 仍继续。

Strengths

  • 新 loader 的 streaming load + post-load hook 设计能降低加载峰值内存,并避免 fused shard 在未收齐前提前后处理。
  • BERT/Roberta lazy registry、gamma/beta LayerNorm 兼容和 wrapped .pt/.bin 解包已有针对性测试覆盖。
  • FP8 Linear 的缺失 scale、非连续输入、QKV/Merged shard 完整性测试比早期版本更完整。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from 2612b43 to 6c95e72 Compare July 8, 2026 10:55
@LLLLKKKK

LLLLKKKK commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/0 · P3/0

Blocking Issues

P1

  • fastsafetensors stacked MoE 拆分后的名称无法被专家层识别 @ rtp_llm/models_py/model_loader.py:297
    • 建议:让 fastsafetensors stacked 路径输出 BaseMoEExperts 接受的 per-expert 名称:gate_up_proj 需要在拆分 expert 后继续拆成 gate_proj.weightup_proj.weightdown_proj 需要补齐 .weight;同时补一个 fastsafetensors/PerExpertParallelLoader stacked MoE 回归测试,覆盖 post-load completeness。

Checklist Violations (2 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue fastsafetensors stacked MoE 拆分后的名称无法被专家层识别
    PerExpertParallelLoader 模板把 experts.gate_up_proj 改成 experts.{expert_id}.gate_up_proj;但 BaseMoEExperts.load_weights 只处理 3D stacked gate_up_proj,或 per-expert 的 {id}.gate_proj.weight / {id}.up_proj.weight / {id}.down_proj.weight。拆分后 tensor 已是 2D,gate_up_proj 分支不会标记 loaded。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue fastsafetensors stacked MoE 拆分后的名称无法被专家层识别
    PerExpertParallelLoader 模板把 experts.gate_up_proj 改成 experts.{expert_id}.gate_up_proj;但 BaseMoEExperts.load_weights 只处理 3D stacked gate_up_proj,或 per-expert 的 {id}.gate_proj.weight / {id}.up_proj.weight / {id}.down_proj.weight。拆分后 tensor 已是 2D,gate_up_proj 分支不会标记 loaded。

Strengths

  • 新 loader 对缺失权重做了显式 post-load 校验,避免 torch.empty 未初始化参数直接进入推理。
  • FP8 per-tensor/per-channel/per-block 的 Linear 和 MoE 路径补了较多布局、scale merge、TP 切分回归测试。

@Oneydauh Oneydauh force-pushed the develop/hengcang/newloader_core branch from 6c95e72 to 42091b8 Compare July 8, 2026 11:58
@LLLLKKKK

LLLLKKKK commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/0 · P3/0

Blocking Issues

P1

  • RowParallelLinear 在 TP all_reduce 前加 bias 会重复累加 @ rtp_llm/models_py/layers/linear.py:300
    • 建议:RowParallel 路径应先以 bias=None 计算本地 matmul,完成 all_reduce 后再加一次完整 bias;reduce_output=False 时再按本地语义处理 bias。补一个 tp_size>1 + bias=True 的 RowParallel 前向测试。
  • fastsafetensors stacked MoE 名称归一化没有接入加载路径 @ rtp_llm/models_py/model_loader.py:492
    • 建议:在 fastsafetensors 路径中把 _get_fastsafetensors_weights(...) 的结果先经过 _normalize_fastsafetensors_stacked_moe_weights,再做 EP filter/model.load_weights;同时增加覆盖 _load_via_fastsafetensors 实际接线的测试,避免只测 helper。

Checklist Violations (3 fail / 56 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue fastsafetensors stacked MoE 名称归一化没有接入加载路径
    PerExpertParallelLoader 会产出 ...experts.{id}.gate_up_proj/down_proj 这样的 2D per-expert 名称;_normalize_fastsafetensors_stacked_moe_weights 能把它转成 gate_proj.weight/up_proj.weight/down_proj.weight,但 _load_via_fastsafetensors 直接把原 iterator 传给 model.load_weights,没有调用该 helper。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue RowParallelLinear 在 TP all_reduce 前加 bias 会重复累加
    LinearBase.forward 会把 self.bias 传入量化方法并在本 rank 本地加上 bias;RowParallelLinear.forward 随后对 output 做 all_reduce。当 tp_size > 1bias=True 时,每个 rank 都持有完整 output bias,归约结果会包含 tp_size 份 bias。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue fastsafetensors stacked MoE 名称归一化没有接入加载路径
    PerExpertParallelLoader 会产出 ...experts.{id}.gate_up_proj/down_proj 这样的 2D per-expert 名称;_normalize_fastsafetensors_stacked_moe_weights 能把它转成 gate_proj.weight/up_proj.weight/down_proj.weight,但 _load_via_fastsafetensors 直接把原 iterator 传给 model.load_weights,没有调用该 helper。

Strengths

  • 新 loader 对缺失权重增加了 post-load 完整性检查,能避免 torch.empty 缓冲区静默进入推理。
  • FP8/QKV/MoE scale 合并逻辑有针对性单测,覆盖了部分流式加载边界。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: LGTM

Summary: P0/0 · P1/0 · P2/0 · P3/0

lgtm ready to ci

Checklist ✅ (44 items passed)

Strengths

  • EP 分区校验、stacked tensor 维度校验和缺失权重检查都走 fail-fast,能避免静默丢专家或使用未初始化 buffer。
  • 新增测试覆盖了 scratch 与 fastsafetensors 两条 stacked MoE EP 过滤路径,并验证了拆分后的 tensor 内容。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/0 · P3/0

Blocking Issues

P1

  • 空 quantization 会被当成已量化模型 @ rtp_llm/models_py/model_loader.py:469
    • 建议:改为判断真实量化状态,例如 bool(model_config.quantization),或优先检查 model_config.quant_config / quant_source_config.is_quanted();补充 None、空字符串、未量化默认配置和 FP8 配置的边界单测。
  • ROCm FNUZ FP8 online MoE per-channel 使用了错误量化上限 @ rtp_llm/models_py/quant_methods/fp8_moe.py:466
    • 建议:在 online per-channel MoE 中用 runtime_dtype = _runtime_fp8_dtype()float(torch.finfo(runtime_dtype).max) 计算上限,并让最小 scale 与该 dtype 对齐;增加 mock ROCm FNUZ dtype 的回归测试,验证量化后反量化能还原行级 amax。

Checklist Violations (4 fail / 44 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue 空 quantization 会被当成已量化模型
    _is_quantized_load() 只判断 model_config.quantization is not None;而 ModelConfig 默认 quantization=""。未量化模型也会返回 True,随后 fastsafetensors 显存估算把 rank_share 除以 2,可能在显存不足时仍选择 GPU 直读路径并触发 OOM/加载失败。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue ROCm FNUZ FP8 online MoE per-channel 使用了错误量化上限
    _online_per_channel()_runtime_fp8_dtype() 生成权重,但 fp8_max 固定为 448。ROCm 非 gfx950 会使用 float8_e4m3fnuz,其最大值为 240;scale 被低估后 cast 会饱和,MoE 权重量化/反量化结果错误。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue ROCm FNUZ FP8 online MoE per-channel 使用了错误量化上限
    _online_per_channel()_runtime_fp8_dtype() 生成权重,但 fp8_max 固定为 448。ROCm 非 gfx950 会使用 float8_e4m3fnuz,其最大值为 240;scale 被低估后 cast 会饱和,MoE 权重量化/反量化结果错误。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 空 quantization 会被当成已量化模型
    _is_quantized_load() 只判断 model_config.quantization is not None;而 ModelConfig 默认 quantization=""。未量化模型也会返回 True,随后 fastsafetensors 显存估算把 rank_share 除以 2,可能在显存不足时仍选择 GPU 直读路径并触发 OOM/加载失败。

Strengths

  • 新 loader 在多处加入了 shape、TP/EP 分片和加载完整性校验,能避免部分权重静默缺失。
  • FP8 QKV/MoE 的 per-tensor scale 合并逻辑有明确 post-load 阶段,避免流式加载中提前量化。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/0 · P3/0

Blocking Issues

P1

  • BERT 缺省 token_type_embedding 在 CUDA 加载后仍留在 CPU @ rtp_llm/models_py/new_models/bert/language.py:198
    • 建议:创建 fallback 时使用已加载 embedding 的 device,例如从 word_embeddings_weight.device 或其它 embedding 参数取 device,并补一个 CUDA/目标设备上的“缺少 token_type_embeddings.weight”回归测试。

Checklist Violations (2 fail / 44 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue BERT 缺省 token_type_embedding 在 CUDA 加载后仍留在 CPU
    _create_model_weights() 在 checkpoint 缺少 token_type_embeddings.weight 时直接 torch.zeros(...),没有继承已加载 embedding 的 device;_apply() 重建权重也走同一路径。CUDA/fastsafetensors 加载后,forward() 会把这个 CPU tensor 填入 bert_embedding_inputs.token_type_embedding 并传给 CUDA embedding_bert
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue BERT 缺省 token_type_embedding 在 CUDA 加载后仍留在 CPU
    _create_model_weights() 在 checkpoint 缺少 token_type_embeddings.weight 时直接 torch.zeros(...),没有继承已加载 embedding 的 device;_apply() 重建权重也走同一路径。CUDA/fastsafetensors 加载后,forward() 会把这个 CPU tensor 填入 bert_embedding_inputs.token_type_embedding 并传给 CUDA embedding_bert

Strengths

  • 新 loader 对 Linear/MoE 权重缺失、TP/EP 分片边界、FP8 scale 完整性增加了显式校验,避免了不少静默使用未初始化 buffer 的问题。
  • 新增测试覆盖了 FP8 已量化加载、MoE scale 完整性、fastsafetensors fallback、EP stacked MoE 过滤等关键路径。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: LGTM

Summary: P0/0 · P1/0 · P2/0 · P3/0

lgtm ready to ci

Checklist ✅ (44 items passed)

Strengths

  • 新增 loader 在缺失权重、TP/EP 非整除、FP8 block scale 非对齐、fast path fallback 等边界上有 fail-fast 检查,并补了较多针对性回归测试。
  • QKV、merged gate/up、MoE FP8/W4A8 的 scale 合并与 post-load 时序有显式测试覆盖,降低了流式加载下半初始化或 scale 语义漂移的风险。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/0 · P2/1 · P3/0

Non-blocking Suggestions

P2

  • new_weight_loader 公开目标缺少运行时依赖 @ rtp_llm/models_py/BUILD:116
    • 建议:为 new_weight_loader 补齐直接运行时依赖,或收窄 visibility 并约束调用方通过已有 :models 目标使用;同时增加一个只依赖 //rtp_llm/models_py:new_weight_loader 的 smoke import/py_test,验证 public target 可独立执行。

Checklist Violations (2 fail / 50 total)

General Principles Checklist

  • [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue ``new_weight_loader 公开目标缺少运行时依赖
    `new_weight_loader` 公开导出 `model_loader.py`、`layers/.py`、`new_models/**/.py` 等源码,这些文件直接 import `rtp_llm.models_py.distributed.collective_torch`、`rtp_llm.models_py.modules`、`rtp_llm.device`、`rtp_llm.ops` 和 `rtp_llm.model_loader.*`,但该 `py_library` 只有 `srcs` 没有声明 `deps`。直接依赖该 public target 的测试或二进制可能缺少 runfiles。

RTP-LLM Checklist

  • [H] 测试与 CI — BUILD、py_test、cc_test、genrule 变更必须验证 srcs/data/runfiles/testdata 相对路径、import path 和 target 可执行性;genrule glob 不得捕获无关构建产物 → issue ``new_weight_loader 公开目标缺少运行时依赖
    `new_weight_loader` 公开导出 `model_loader.py`、`layers/.py`、`new_models/**/.py` 等源码,这些文件直接 import `rtp_llm.models_py.distributed.collective_torch`、`rtp_llm.models_py.modules`、`rtp_llm.device`、`rtp_llm.ops` 和 `rtp_llm.model_loader.*`,但该 `py_library` 只有 `srcs` 没有声明 `deps`。直接依赖该 public target 的测试或二进制可能缺少 runfiles。

Strengths

  • 新 loader 在缺失权重、unexpected weight、TP/EP/MoE scale 不完整等路径上有明确失败语义,避免静默加载错误。
  • 测试覆盖了 registry、BERT 兼容映射、fastsafetensors fallback、TP 边界、MoE 分片以及 FP8 已量化加载等核心场景。
  • 本轮增量修改将 CUDA memory probe patch 到 loader 的目标设备辅助函数,能覆盖实际被测调用点。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/0 · P3/0

Blocking Issues

P1

  • 完整融合权重名会被子串匹配误判成分片 @ rtp_llm/models_py/layers/linear.py:649
    • 建议:按路径 segment 做精确匹配,先去掉当前 layer prefix,再只接受 gate_proj/up_projq_proj/k_proj/v_proj 作为独立 segment;同时补充 gate_up_proj.weightgate_up_proj.weight_scale(_inv)qkv_proj.weight 这类已合并权重名的加载测试,确保它们进入 direct merged 分支。

Checklist Violations (3 fail / 44 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue 完整融合权重名会被子串匹配误判成分片
    MergedColumnParallelLinear._get_shard_id()if shard_name in weight_name,完整键 gate_up_proj.weight 会命中 up_proj,绕过已合并权重分支。QKVParallelLinear.load_weights() 同样用子串判断,qkv_proj.weight 会命中 v_proj,导致完整融合 ckpt shape mismatch 或 q/k shard 缺失。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 完整融合权重名会被子串匹配误判成分片
    MergedColumnParallelLinear._get_shard_id()if shard_name in weight_name,完整键 gate_up_proj.weight 会命中 up_proj,绕过已合并权重分支。QKVParallelLinear.load_weights() 同样用子串判断,qkv_proj.weight 会命中 v_proj,导致完整融合 ckpt shape mismatch 或 q/k shard 缺失。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 完整融合权重名会被子串匹配误判成分片
    MergedColumnParallelLinear._get_shard_id()if shard_name in weight_name,完整键 gate_up_proj.weight 会命中 up_proj,绕过已合并权重分支。QKVParallelLinear.load_weights() 同样用子串判断,qkv_proj.weight 会命中 v_proj,导致完整融合 ckpt shape mismatch 或 q/k shard 缺失。

Strengths

  • 新 loader 对缺失权重、EP 专家分区、FP8 block scale 对齐等关键边界做了显式校验,避免继续使用未初始化 torch.empty buffer。
  • 新增测试覆盖了 streaming load、fastsafetensors fallback、MoE stacked expert 展开、FP8 scale 合并和 BERT gamma/beta 映射等多条高风险路径。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/0 · P3/0

Blocking Issues

P1

  • BERT 新 loader 在 TP>1 时未按 rank 分片权重 @ rtp_llm/models_py/new_models/bert/language.py:212
    • 建议:在 _create_model_weights 中按 tp_rank/tp_size 对 embedding hidden 维、QKV/O、FFN 权重和 bias 做与 legacy loader 一致的分片;如果 BERT 新 loader 暂不支持 TP,则在构造时对 tp_size > 1 明确 fail-fast,并补充 TP>1 覆盖。
  • per-tensor FP8 forward 未保证量化输入连续 @ rtp_llm/models_py/quant_methods/fp8.py:326
    • 建议:与 per-channel/block FP8 路径保持一致,在两个 per-tensor apply 中对 input_2dif not input_2d.is_contiguous(): input_2d = input_2d.contiguous(),并增加 stride slice 形式的非连续输入回归测试。

Checklist Violations (3 fail / 44 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue BERT 新 loader 在 TP>1 时未按 rank 分片权重
    load_config.tp_size > 1 时,初始化会把 tp_size/tp_rank 写入 parallelism_config,但 _create_model_weights 仍把完整 embedding、QKV、FFN 权重写入 ModelWeights;随后 BertModel 会按 TP 构造本地 attention 配置并在 embedding 后 all_gather,导致维度翻倍或本地 QKV 形状不匹配。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue BERT 新 loader 在 TP>1 时未按 rank 分片权重
    load_config.tp_size > 1 时,初始化会把 tp_size/tp_rank 写入 parallelism_config,但 _create_model_weights 仍把完整 embedding、QKV、FFN 权重写入 ModelWeights;随后 BertModel 会按 TP 构造本地 attention 配置并在 embedding 后 all_gather,导致维度翻倍或本地 QKV 形状不匹配。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue per-tensor FP8 forward 未保证量化输入连续
    Fp8LinearMethod.applyFp8OnlineLinearMethod.apply 直接把 x.reshape(-1, x.shape[-1]) 传给 scaled_fp8_per_tensor_quant;该 CUDA op 的 CHECK_INPUT 要求 contiguous。对 input[::2] 这类 stride 切片,reshape 后仍是非连续 2D,forward 会在量化前报错;现有测试只覆盖 reshape 产生连续拷贝的 transpose 场景。

Strengths

  • 新 loader 对缺失权重、EP expert 划分、FP8 block 对齐等路径增加了 fail-fast 校验,避免静默使用未初始化 buffer。
  • MoE 量化方法从 BaseMoEExperts 中拆出注册式实现,降低了后续 FP8/W4A8 路径继续分叉的风险。

@LLLLKKKK

LLLLKKKK commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/0 · P3/0

Blocking Issues

P1

  • new_weight_loader 目标缺少 BERT 运行时依赖 @ rtp_llm/models_py/BUILD:116
    • 建议:把 BERT wrapper 的运行时依赖拆成可被 new_weight_loader 依赖的下层 py_library,或把所需 model_desc/bert.py 及其依赖纳入该目标且避免与 models 成环;同时把 test_new_weight_loader_imports 扩展到至少触发一次 _build_inner_model()NewModelLoader.load() 的最小 BERT 用例,验证 public target 的 runfiles 能执行真实加载路径。

Checklist Violations (2 fail / 44 total)

General Principles Checklist

  • [6.1] Architecture — 依赖方向:无循环依赖/跨层惊喜 → issue new_weight_loader 目标缺少 BERT 运行时依赖
    new_weight_loadersrcs/deps 只包含新 loader、layers 和 quant_methods,但 BertForEmbedding._build_inner_model() 会导入 rtp_llm.models_py.model_desc.bert.BertModel;该文件只在 models 目标中。仅依赖 new_weight_loader 的 Bazel runfiles 可通过当前 import smoke,却会在实际 BERT/Roberta load 时缺模块。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue new_weight_loader 目标缺少 BERT 运行时依赖
    new_weight_loadersrcs/deps 只包含新 loader、layers 和 quant_methods,但 BertForEmbedding._build_inner_model() 会导入 rtp_llm.models_py.model_desc.bert.BertModel;该文件只在 models 目标中。仅依赖 new_weight_loader 的 Bazel runfiles 可通过当前 import smoke,却会在实际 BERT/Roberta load 时缺模块。

Strengths

  • 新 loader 对 TP/EP 分片、缺失权重、FP8 block 对齐和 MoE scale 完整性做了多处 fail-fast,降低静默使用未初始化权重的风险。
  • 量化方法注册、BERT 权重映射、fastsafetensors fallback 和 FP8/MoE 边界补充了较多聚焦测试。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/1 · P2/0 · P3/0

Blocking Issues

P1

  • MoE 量化派发未传 prefix,ignored_layers 对 MoE 层失效 @ rtp_llm/models_py/layers/moe_experts.py:175
    • 建议:为 BaseMoEExperts 增加稳定的模块 prefix 并传给 get_quant_method;当命中 ignore 时同步把 _quant_family 和 required aux 语义切到 unquantized;补一个 ignored MoE 层且无 weight_scale 的加载测试。

Checklist Violations (2 fail / 44 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue MoE 量化派发未传 prefix,ignored_layers 对 MoE 层失效
    BaseMoEExperts 调用 quant_config.get_quant_method(self) 时未传 prefix;QuantizationConfig 只有拿到 prefix 才会执行 ignored_layers 匹配。包含 ignored MoE 层的量化配置仍会进入 FP8/W4A8 方法,并要求量化 scale,导致应跳过的未量化权重加载失败或被按量化权重处理。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue MoE 量化派发未传 prefix,ignored_layers 对 MoE 层失效
    BaseMoEExperts 调用 quant_config.get_quant_method(self) 时未传 prefix;QuantizationConfig 只有拿到 prefix 才会执行 ignored_layers 匹配。包含 ignored MoE 层的量化配置仍会进入 FP8/W4A8 方法,并要求量化 scale,导致应跳过的未量化权重加载失败或被按量化权重处理。

Strengths

  • 新 loader 对缺失权重、unexpected weight、TP/EP 切分、FP8 block scale 对齐等路径做了显式校验,失败语义比静默忽略更清晰。
  • BERT/Roberta 通过 lazy registry 接入,默认 import 面较小,并补了 import、load integrity、FP8/MoE 相关单测。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/3 · P2/0 · P3/0

Blocking Issues

P1

  • ignored MoE 仅在加载侧回退未量化,运行时仍选择量化 executor @ rtp_llm/models_py/layers/moe_experts.py:101
    • 建议:将 per-layer effective quant config 贯穿 MoEConfigAdapterMoeConfigResolver,确保 ignored 层选择 no-quant strategy。补充使用真实 config-side quant config 的 process_weights_after_loading -> FusedMoeFactory 集成测试,且不要 mock 构建边界。
  • 融合 QKV 权重在 TP 下按整轴切分,破坏 Q/K/V 布局 @ rtp_llm/models_py/layers/linear.py:773
    • 建议:先按全局 Q/K/V 尺寸拆分融合 tensor,分别执行 TP/KV-aware 分片后再拼接;bias、per-channel scale 和 block scale 使用同一映射。增加 TP=2 的逐 rank reference 测试。
  • KV head 复制使用取模 rank,与连续 Q-head 分片不一致 @ rtp_llm/models_py/layers/linear.py:987
    • 建议:统一按 kv_tp = gcd(num_kv_heads, tp_size) 计算 local KV head 数和 replication rank,并同时用于 weight、bias、channel scale 与 block scale。增加 1 < num_kv_heads < tp_size 的全 rank 参数化测试并对齐旧 loader reference。

Checklist Violations (9 fail / 90 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue ignored MoE 仅在加载侧回退未量化,运行时仍选择量化 executor
    命中 is_layer_ignored 后,这里把 _quant_family 设为 none 并创建未量化 w13/w2;但 _maybe_build_fused_moe() 仍传入全局 model_config.quant_config。Factory 会据此选择 FP8/W4A8 executor,而权重字典没有 moe_s1/moe_s2,构建时会报错或按量化布局解释 BF16。新增测试同时 mock 掉 factory 并把全局 quant config 设为 None,未覆盖该断点。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue KV head 复制使用取模 rank,与连续 Q-head 分片不一致
    num_kv_heads < tp_size 时,代码用 tp_rank % num_kv_heads 选择 KV head;旧 loader 使用 GCD replication group,即 tp_rank // (tp_size // gcd(kv,tp))。例如 KV=2、TP=8 时,rank 0-3 应复用 KV0、rank 4-7 复用 KV1,当前实现却交替选择。KV=3、TP=8 时还会把每 rank KV 数固定为 1,丢失其余 KV heads。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue ignored MoE 仅在加载侧回退未量化,运行时仍选择量化 executor
    命中 is_layer_ignored 后,这里把 _quant_family 设为 none 并创建未量化 w13/w2;但 _maybe_build_fused_moe() 仍传入全局 model_config.quant_config。Factory 会据此选择 FP8/W4A8 executor,而权重字典没有 moe_s1/moe_s2,构建时会报错或按量化布局解释 BF16。新增测试同时 mock 掉 factory 并把全局 quant config 设为 None,未覆盖该断点。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue KV head 复制使用取模 rank,与连续 Q-head 分片不一致
    num_kv_heads < tp_size 时,代码用 tp_rank % num_kv_heads 选择 KV head;旧 loader 使用 GCD replication group,即 tp_rank // (tp_size // gcd(kv,tp))。例如 KV=2、TP=8 时,rank 0-3 应复用 KV0、rank 4-7 复用 KV1,当前实现却交替选择。KV=3、TP=8 时还会把每 rank KV 数固定为 1,丢失其余 KV heads。

RTP-LLM Checklist

  • [B] 正确性与逻辑 — GCD 分组后整除关系校验 → issue KV head 复制使用取模 rank,与连续 Q-head 分片不一致
    num_kv_heads < tp_size 时,代码用 tp_rank % num_kv_heads 选择 KV head;旧 loader 使用 GCD replication group,即 tp_rank // (tp_size // gcd(kv,tp))。例如 KV=2、TP=8 时,rank 0-3 应复用 KV0、rank 4-7 复用 KV1,当前实现却交替选择。KV=3、TP=8 时还会把每 rank KV 数固定为 1,丢失其余 KV heads。
  • [B] 正确性与逻辑 — 逻辑错误、off-by-one、null/zero 检查 → issue 融合 QKV 权重在 TP 下按整轴切分,破坏 Q/K/V 布局
    qkv_key is None 时直接调用继承的 _split_weight(..., dim=0),把 [Q|K|V] 当作均匀矩阵切分。例如 Q/K/V 行数为 8/4/4、TP=2 时,rank0 得到前 8 行全是 Q,正确结果应为 Q 的 4 行、K 的 2 行和 V 的 2 行。当前融合权重测试仅覆盖 TP=1,bias 和辅助 tensor 也没有 QKV-aware 切分。
  • [E] 分布式 — 跨 rank 数据一致性 → issue KV head 复制使用取模 rank,与连续 Q-head 分片不一致
    num_kv_heads < tp_size 时,代码用 tp_rank % num_kv_heads 选择 KV head;旧 loader 使用 GCD replication group,即 tp_rank // (tp_size // gcd(kv,tp))。例如 KV=2、TP=8 时,rank 0-3 应复用 KV0、rank 4-7 复用 KV1,当前实现却交替选择。KV=3、TP=8 时还会把每 rank KV 数固定为 1,丢失其余 KV heads。
  • [H] 测试与 CI — mock/fake/stub 只能隔离非目标依赖,不能 stub 掉本次声称覆盖的生产边界;涉及 pybind/C++/runtime 的 bug 必须有真实边界集成或 smoke 覆盖 → issue ignored MoE 仅在加载侧回退未量化,运行时仍选择量化 executor
    命中 is_layer_ignored 后,这里把 _quant_family 设为 none 并创建未量化 w13/w2;但 _maybe_build_fused_moe() 仍传入全局 model_config.quant_config。Factory 会据此选择 FP8/W4A8 executor,而权重字典没有 moe_s1/moe_s2,构建时会报错或按量化布局解释 BF16。新增测试同时 mock 掉 factory 并把全局 quant config 设为 None,未覆盖该断点。

Python Static-First Checklist

  • [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue ignored MoE 仅在加载侧回退未量化,运行时仍选择量化 executor
    命中 is_layer_ignored 后,这里把 _quant_family 设为 none 并创建未量化 w13/w2;但 _maybe_build_fused_moe() 仍传入全局 model_config.quant_config。Factory 会据此选择 FP8/W4A8 executor,而权重字典没有 moe_s1/moe_s2,构建时会报错或按量化布局解释 BF16。新增测试同时 mock 掉 factory 并把全局 quant config 设为 None,未覆盖该断点。

Strengths

  • 权重完整性检查、EP/TP 参数校验和 FP8 block 对齐检查能够较早阻止未初始化权重进入推理。
  • scratch/fastsafetensors 两条加载路径均显式处理失败回退、资源释放和安全的 weights_only 反序列化。
  • 新增测试覆盖了缺失 scale、分片不可整除和 ignored MoE 的加载侧行为。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/5 · P2/0 · P3/0

Blocking Issues

P1

  • 分布式 rank 可能选择不同加载路径并在 WORLD collective 中死锁 @ rtp_llm/models_py/model_loader.py:662
    • 建议:在进入 fastsafetensors 前 all-gather 各 rank 的 eligibility 和失败原因,由所有 rank 共同选定唯一加载方法。fast path 运行期失败也应协调为全体 fallback 或全体失败,并补充多 rank 测试覆盖能力不一致、单 rank OOM/读取失败和 loader 清理。
  • LoRA 权重未转置却沿用转置后布局的 TP 切分规则 @ rtp_llm/models_py/model_loader.py:890
    • 建议:在执行 _LORA_TP_RULES 前将 HF LoRA tensor 转置并 contiguous,保持现有内部布局约定;增加 TP=1/TP=2 的端到端测试,将 gate/up/down/o projection 的结果与旧加载器对比。
  • GCD 分组后缺少 GQA 整除校验并接受非法头数配置 @ rtp_llm/models_py/layers/linear.py:717
    • 建议:保留 GCD 分组支持时,同时校验 num_heads % num_kv_heads == 0 以及分片后的 Q/KV head 比例有效。将 8/6/4 改为拒绝用例,并新增 KV head 少于 TP 但分组合法的边界用例。
  • MoE 量化配置解析变更破坏现有 adapter 契约和测试 @ rtp_llm/models_py/modules/factory/fused_moe/utils/config_resolver.py:38
    • 建议:使用 sentinel 区分“未传入”和“显式传入 None”:未传入时继承 model_config.quant_config,仅 ignored layer 等显式 None 场景覆盖为未量化。同步补充继承、显式覆盖和 strategy 选择测试。
  • CPU 暂存路径在设备迁移前构建 MoE executor @ rtp_llm/models_py/model_loader.py:542
    • 建议:CPU 上只执行必要的量化后处理,将 fused MoE executor 构建延后到模型迁移完成之后;或在迁移后显式重建 executor。增加 force-CPU 的 MoE 端到端测试,断言 executor 所有权重与输入位于同一目标设备并实际执行一次 forward。

Checklist Violations (6 fail / 110 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue CPU 暂存路径在设备迁移前构建 MoE executor
    force_cpu 路径先运行 post-load hooks,再执行 model.to(device);MoE hook 会立即构建 FusedMoe,executor 将 w13.dataw2.data 等保存为普通对象属性,并非注册子模块或参数。后续 model.to() 只迁移 layer 参数,executor 仍可能引用旧 CPU tensor,forward 时会发生设备不匹配或继续使用失效权重。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue 分布式 rank 可能选择不同加载路径并在 WORLD collective 中死锁
    _resolve_load_method() 按各 rank 的本地 import、CUDA 状态和可用显存独立决定是否使用 fastsafetensors;该路径随后用 WORLD 进程组执行 expert broadcast。任一 rank 选择 scratch,或仅某个 rank 在 fast path 异常后执行本地 fallback,都会使其余 rank 留在 collective 中。现有测试均为单进程 mock,未覆盖 rank 一致性与单 rank 失败。
  • [6.1] Software Engineering — LSP:子类/重写保持基类契约 → issue MoE 量化配置解析变更破坏现有 adapter 契约和测试
    resolver 从 config.model_config.quant_config 改读 config.quant_config,但 MoEConfigAdapter 的该字段默认就是 None。既有 config resolver 和 CUDA strategy 测试只在 model_config 中设置量化配置,并省略 adapter 参数;修改后这些配置会被误判为未量化,现有 Bazel 测试断言也会直接失败。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue 分布式 rank 可能选择不同加载路径并在 WORLD collective 中死锁
    _resolve_load_method() 按各 rank 的本地 import、CUDA 状态和可用显存独立决定是否使用 fastsafetensors;该路径随后用 WORLD 进程组执行 expert broadcast。任一 rank 选择 scratch,或仅某个 rank 在 fast path 异常后执行本地 fallback,都会使其余 rank 留在 collective 中。现有测试均为单进程 mock,未覆盖 rank 一致性与单 rank 失败。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue CPU 暂存路径在设备迁移前构建 MoE executor
    force_cpu 路径先运行 post-load hooks,再执行 model.to(device);MoE hook 会立即构建 FusedMoe,executor 将 w13.dataw2.data 等保存为普通对象属性,并非注册子模块或参数。后续 model.to() 只迁移 layer 参数,executor 仍可能引用旧 CPU tensor,forward 时会发生设备不匹配或继续使用失效权重。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue GCD 分组后缺少 GQA 整除校验并接受非法头数配置
    构造逻辑仅检查 Q head 可被 TP 整除,并以 gcd(num_kv_heads, tp_size) 计算 KV 分组,没有验证 Q/KV 的全局或本地整除关系。新增测试甚至接受 num_heads=8,num_kv_heads=6,tp=4,得到每 rank 2 个 Q head、3 个 KV head;既有 attention 配置会拒绝该组合,消费者也按整数 Q/KV 分组计算。

Strengths

  • 新加载器对缺失权重、重复权重和 block scale 对齐提供了明确的完整性校验。
  • 测试覆盖了 fused QKV 拆分、量化 block scale、BERT 权重映射和加载方法回退等关键基础路径。
  • 模块化的 weight mapper、quant method 和 registry 边界较清晰,便于后续扩展模型与量化方案。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/2 · P2/0 · P3/0

Blocking Issues

P1

  • 单 rank 加载失败仍会把其他 rank 留在 collective 中 @ rtp_llm/models_py/model_loader.py:502
    • 建议:让 fast loader 在任一 rank 失败时向所有参与 rank 传播失败并终止未完成 collective,例如在 loader/process-group 层提供明确的 cancel/abort 协议。补充真实双进程测试,分别在首次 broadcast 前后注入单 rank 失败,并断言所有 rank 在有界时间内以同一错误退出。
  • 多 rank 聚合使显式 fastsafetensors 的显存豁免不可达 @ rtp_llm/models_py/model_loader.py:717
    • 建议:聚合结构化的 reason、free bytes 和 rank share;各 rank 完成本地显存豁免判断后,再 all-gather 最终 eligibility,并仅在所有 rank 均允许时进入 fast path。增加显式模式、多 rank、负 headroom 但 rank share 可容纳的回归测试。

Checklist Violations (5 fail / 103 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue 多 rank 聚合使显式 fastsafetensors 的显存豁免不可达
    显式模式原本在 reason 为 insufficient GPU free memoryfree_bytes >= rank_share 时允许继续。多 rank 下新增代码先把原因拼成 rankN: insufficient GPU free memory,因此后续精确字符串比较永远不成立;即使所有 rank 都能容纳各自权重,也会无条件抛错。现有测试只覆盖 AUTO 聚合决策。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue 多 rank 聚合使显式 fastsafetensors 的显存豁免不可达
    显式模式原本在 reason 为 insufficient GPU free memoryfree_bytes >= rank_share 时允许继续。多 rank 下新增代码先把原因拼成 rankN: insufficient GPU free memory,因此后续精确字符串比较永远不成立;即使所有 rank 都能容纳各自权重,也会无条件抛错。现有测试只覆盖 AUTO 聚合决策。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue 多 rank 聚合使显式 fastsafetensors 的显存豁免不可达
    显式模式原本在 reason 为 insufficient GPU free memoryfree_bytes >= rank_share 时允许继续。多 rank 下新增代码先把原因拼成 rankN: insufficient GPU free memory,因此后续精确字符串比较永远不成立;即使所有 rank 都能容纳各自权重,也会无条件抛错。现有测试只覆盖 AUTO 聚合决策。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue 多 rank 聚合使显式 fastsafetensors 的显存豁免不可达
    显式模式原本在 reason 为 insufficient GPU free memoryfree_bytes >= rank_share 时允许继续。多 rank 下新增代码先把原因拼成 rankN: insufficient GPU free memory,因此后续精确字符串比较永远不成立;即使所有 rank 都能容纳各自权重,也会无条件抛错。现有测试只覆盖 AUTO 聚合决策。

Python Static-First Checklist

  • [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue 单 rank 加载失败仍会把其他 rank 留在 collective 中
    load() 在本 rank 捕获 fast path 异常后仅于多 rank 场景直接重新抛出。PerExpertParallelLoader 会逐 expert 执行 WORLD broadcast;某 rank 在建模、读盘、OOM 或 tensor 处理时先失败,peer 没有 cancel、abort 或统一失败信号,仍可能阻塞在后续 broadcast。新增测试 mock 了整个 fast path 和 world size,只证明不会本地 fallback,未覆盖真实 collective。

Strengths

  • LoRA 权重现在先转换为引擎内部转置布局再执行 TP 切分,并补充了对应布局断言。
  • QKV 构造同时校验全局及 rank-local 的 Q/KV head 整除关系,合法的 KV 复制边界也有覆盖。
  • MoE 配置通过 sentinel 区分继承与显式 None,executor 也延迟到参数完成设备迁移后构建。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/3 · P2/0 · P3/0

Blocking Issues

P1

  • TP 分片未校验 FP8 block 的原始边界 @ rtp_llm/models_py/layers/linear.py:239
    • 建议:在 Column/Row 的 weight_scale_inv 分支按 weight_block_size 校验 rank 起点和本地权重尺寸均 block-aligned;不能对齐时 fail-fast,或先反量化再按 rank 边界重新量化。补充 scale-grid 数量可整除但权重边界不对齐的 TP 用例。
  • 单 rank 会把 stacked MoE 的 .weight 当作 scale @ rtp_llm/models_py/layers/moe_experts.py:283
    • 建议:对三维 stacked tensor 的 param_name == "weight" 调用 _load_stacked_experts,仅将明确的 scale/meta 后缀送入 auxiliary 分支;为带和不带 .weight 后缀的格式分别覆盖 ep_size=1 与 ep_size>1。
  • BERT 测试绕过了本次新增的真实模型边界 @ rtp_llm/models_py/quant_methods/test/test_new_weight_loader_imports.py:66
    • 建议:增加一个由 Bazel target 执行的微型 BERT/Roberta 集成用例:生成真实 safetensors,走注册表和 NewModelLoader.load(),不 mock BertModel,至少完成真实构造与一次 initialize/forward;GPU 专属路径再由对应平台 smoke 覆盖。

Checklist Violations (4 fail / 126 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue 单 rank 会把 stacked MoE 的 .weight 当作 scale
    当 ep_size=1 且 key 为 experts.gate_up_proj.weightexperts.down_proj.weight 时,三维 tensor 命中 len(parts)==2 分支并以 param_name="weight" 进入 _load_stacked_expert_aux,不会复制权重或更新 _loaded_keys,最终完整性检查失败。ep_size>1 时 _ExpertRangeFilter 却会把同一格式展开成逐 expert 权重,因此行为随 EP 配置变化。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue BERT 测试绕过了本次新增的真实模型边界
    import smoke 直接 mock BertModel.__init__,其余 BERT 用例也 mock _build_inner_model;scratch loader 测试使用 fake model。当前没有测试通过 NewModelLoader.load() 加载真实 BERT checkpoint 后执行真实 BertModel 构造、initialize 或 forward,因此 QKV 转置、ModelWeights 名称、device 迁移及运行时依赖错误均可绕过现有 CI。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue 单 rank 会把 stacked MoE 的 .weight 当作 scale
    当 ep_size=1 且 key 为 experts.gate_up_proj.weightexperts.down_proj.weight 时,三维 tensor 命中 len(parts)==2 分支并以 param_name="weight" 进入 _load_stacked_expert_aux,不会复制权重或更新 _loaded_keys,最终完整性检查失败。ep_size>1 时 _ExpertRangeFilter 却会把同一格式展开成逐 expert 权重,因此行为随 EP 配置变化。

Python Static-First Checklist

  • [P.G] 测试规范 — mock/fake/stub 不得替代本次声称覆盖的生产边界 → issue BERT 测试绕过了本次新增的真实模型边界
    import smoke 直接 mock BertModel.__init__,其余 BERT 用例也 mock _build_inner_model;scratch loader 测试使用 fake model。当前没有测试通过 NewModelLoader.load() 加载真实 BERT checkpoint 后执行真实 BertModel 构造、initialize 或 forward,因此 QKV 转置、ModelWeights 名称、device 迁移及运行时依赖错误均可绕过现有 CI。

Strengths

  • fastsafetensors 未完成分布式失败协议前保持禁用,并对显式选择给出明确错误。
  • 多数叶子层在 post-load 阶段校验必需权重和量化 scale,避免直接使用未初始化的 torch.empty
  • QKV、merged linear、EP expert 范围及多种 FP8 布局补充了较细的分片和完整性测试。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1164

Status: BLOCKING

Summary: P0/0 · P1/4 · P2/0 · P3/0

Blocking Issues

P1

  • fused MoE 的 scale 拆分轴与支持的 checkpoint 布局不一致 @ rtp_llm/models_py/layers/moe_experts.py:467
    • 建议:按量化族规范化 fused 轴:支持最后一维 [gate|up];block scale 拆分后转置为运行时 [M_blocks,H_blocks]。补充包含权重、scale、process_weights_after_loading 的 TP/EP 端到端用例。
  • RowParallel 会误切分一维 per-channel weight scale @ rtp_llm/models_py/layers/linear.py:371
    • 建议:依据 quant method 和参数语义决定分片:RowParallel 的 output-channel weight_scale 必须复制,仅 input-axis scale 才沿 K 分片。增加 N == K、TP>1、scale 分别为 [N][N,1] 的回归用例。
  • force-CPU 模式仍在 CPU 权重上执行 SM100 CUDA 重量化 @ rtp_llm/models_py/quant_methods/fp8_moe.py:547
    • 建议:将离线 block 的设备相关重量化与 MoE executor 一并延迟到模型迁移后;或者提供 CPU 实现并在不支持的组合上入口处明确失败。增加 SM100 分支下 CPU 权重到目标设备的完整时序测试。
  • LoRA 全部未映射时仍返回成功 @ rtp_llm/models_py/model_loader.py:623
    • 建议:统计成功加载的 A/B tensor,并对零映射、A/B 不成对和层越界 fail-fast。若暂不支持 BERT/Roberta,应在入口明确拒绝;同时增加真实 adapter checkpoint 的加载测试。

Checklist Violations (5 fail / 120 total)

General Principles Checklist

  • [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue force-CPU 模式仍在 CPU 权重上执行 SM100 CUDA 重量化
    force_cpu_load_weights 会先在 CPU 执行全部 post-load hook,再调用 model.to(device)。离线 FP8 block MoE hook 在此处查询 CUDA capability;SM100/SM120 随后调用 requant_weight_ue8m0 处理仍位于 CPU 的 w13/w2,因此该公开开关会在目标平台加载阶段崩溃。现有测试只覆盖 online block 的显式拒绝。
  • [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue LoRA 全部未映射时仍返回成功
    名称解析仅接受 base_model.model.model.layers.*,当前注册的 BERT/Roberta adapter 名称不会匹配。循环对解析失败、层越界及未知模块均继续跳过,且不统计成功写入数量;即使所有 tensor 都被丢弃,仍记录 Loaded 并返回空 LoRAWeights,服务会接受一个实际无效果的 adapter。
  • [6.1] Tests — 分布式/跨平台变更有对应覆盖 → issue force-CPU 模式仍在 CPU 权重上执行 SM100 CUDA 重量化
    force_cpu_load_weights 会先在 CPU 执行全部 post-load hook,再调用 model.to(device)。离线 FP8 block MoE hook 在此处查询 CUDA capability;SM100/SM120 随后调用 requant_weight_ue8m0 处理仍位于 CPU 的 w13/w2,因此该公开开关会在目标平台加载阶段崩溃。现有测试只覆盖 online block 的显式拒绝。
  • [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue LoRA 全部未映射时仍返回成功
    名称解析仅接受 base_model.model.model.layers.*,当前注册的 BERT/Roberta adapter 名称不会匹配。循环对解析失败、层越界及未知模块均继续跳过,且不统计成功写入数量;即使所有 tensor 都被丢弃,仍记录 Loaded 并返回空 LoRAWeights,服务会接受一个实际无效果的 adapter。
  • [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue RowParallel 会误切分一维 per-channel weight scale
    Fp8PerChannelLinearMethod 声明支持 [N][N,1] scale。RowParallel 却在 scale.shape[-1] == global K 时切分所有 weight_scale。当常见方阵投影满足 N == K 且 checkpoint 使用 [N] 时,scale 被切成 N/tp,随后与参数 [N,1] 发生 shape mismatch;现有测试只覆盖 [N,1]

Strengths

  • 权重完整性检查能够在使用未初始化的 torch.empty 缓冲区前失败。
  • 新增了真实 safetensors BERT 加载用例,并对 TP/FP8 block 对齐约束采用显式校验。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants