Sophgo INT4 编译中的 scale 通道对齐限制
在 BM1684X 上将 Qwen3.5-2B 转为 W4BF16 时,编译出现 invalid scale channel。W4BF16 表示四位权重配合 BF16 激活,是权重量化路径;量化后的 scale、zero point 与权重本身都需要转换为目标后端能够读取的布局。
复现组合为 tpu_mlir 1.28.1、BM1684X、Qwen3.5-2B。q_group_size=128 和 64 都失败,BF16 转换通过。这首先是后端模型生成问题,还没有进入“量化模型可运行,但任务精度下降”的阶段。
公开问题记录还包含从 AutoRound INT4 权重转换时的同类报错。原始浮点权重由转换器量化,与读取已有量化权重,是两种不同的输入路径。已有量化权重的分组和编码需要从配置与张量中确认,不能仅凭命令行改了 group size,就认为它已经按新的分组重新量化。
因此,“工具支持 W4”只是入口条件。实际模型的层形状、分组方式、元数据布局和 kernel 约定也必须匹配,才会生成可用产物。
一、分组与通道对齐
按输出通道组织 Linear 权重为
这一表达式假设所选路径满足分组条件;实际模型还要核对转置和已发生的重排。
scale: [ N, K/g ]
↑ ↑
通道数 输入方向的组数BM1684X 对应的重排路径需要把通道分配到
[N, K/g]
→ reshape [N/P, P, K/g]
→ permute [P, N/P, K/g]在该 BM1684X 路径中
| 调整项 | 主要影响 | 是否自动修复通道对齐 |
|---|---|---|
q_group_size | K 方向的分组数量和元数据量 | 否 |
| 输出通道 N | scale 的通道维 | 还需保持模型语义 |
| 浮点路径 | 不使用这套 W4 重排 | 可用于隔离问题,仍需完整验收 |
这里的 64 指芯片内部 lane 数,不是 64 张卡,与 num_device 也不是同一个概念。
用一个假设形状看会更直观:若
分组大小还会改变存储成本
group size 对这次断言无效,不代表它没有作用。更小的组允许不同输入区间使用各自的量化参数,同时也增加元数据。若暂不考虑 padding 和其他对齐,权重有
这里按上述 BM1684X 重排分支中 scale 和 zero point 分别存储的方式估算,不代表所有 INT4 格式。
因此分组大小首先应作为量化精度与存储的取舍来评估。遇到通道重排失败时,必须先解决 N 方向的布局问题,之后再比较不同分组的任务效果和运行成本。
二、重排的约束
关键检查及后续维度计算可以压缩为:
int64_t npu_num = backend::Arch::NPU_NUM;
if (scale_shape[0] % npu_num) {
llvm_unreachable("invalid scale channel");
}
auto groups = scale_shape[1];
auto channels_per_lane = scale_shape[0] / npu_num;这个检查并非多余限制。后续重排以整数
令
这说明 scale 和 zero point 必须按相同顺序移动。某个输出通道的权重如果配上了另一个通道的量化参数,张量大小即使正确,反量化结果也会出错。因而检查不能停留在元素总数相等,还要核对通道的对应关系。
这属于所选后端重排实现的布局约束,不能直接扩大成“BM1684X 无法计算任何非 64 倍数输出的 Linear”。浮点路径、其他量化分支以及带补齐或尾部处理的实现,需要分别判断。当前主线的相关分支仍能看到这一检查,但它不能单独证明新版本对该模型仍会失败:上游导出和 lowering 也可能改变进入此处的形状。
补齐通道需要改哪些内容
一种值得研究的修复方向是 padding,但它要同时覆盖权重、scale、zero point、kernel 和输出裁剪。只把一个 shape 改大,并不等于完成等价转换。
例如对单个 Linear,按行补零可写为:
这是局部数学等价的示意。它不能直接证明量化后的布局等价,也不能让额外通道不经裁剪就进入后续 attention、reshape 或残差。修复必须在正确边界上补齐,再恢复原接口。
量化存储中的“补零”尤其容易出错。按常见的仿射反量化关系
补齐也会引入额外计算和元数据。对前面的示意形状,96 个通道补到 128,物理通道增加了三分之一;这不代表整模型耗时增加三分之一,却说明低位权重节省的存储不一定能完全转化为运行收益。是否值得补齐,需要在正确性通过后测量。
子图导出、后端编译和设备运行的分工见 Sophgo Qwen3-VL 子图导出与部署边界,可先据此确认失败停在哪一阶段。
三、排查与验证
先保存失败 block 的 MLIR,核对 A16MatMul 的权重、scale 和 zero point 形状;然后确认到底进入哪条重排分支。这样才能把报错落到具体层及布局条件,而不是继续盲调 group size。
公开复现日志的失败阶段包含 --weight-reorder,并有多个 block 的并行编译报错。应先保留其中一个失败 block 对应的完整 tpuc-opt 命令、输入 MLIR 和权重文件,再单独复现。这样可以排除并行日志交错,也能判断是所有失败 block 共享同一种层形状,还是存在多个问题。
逐层检查时,至少记录以下信息:
来源层与 MLIR location
输入、输出、权重的逻辑形状
weight_bits、q_group_size、w_transpose
进入 weight-reorder 前的 scale / zero point 形状与类型
目标芯片、工具版本和完整失败命令不能直接从打包后权重的某个维度推断 N:INT4 两个值可能共用一个字节,转换过程中也可能已经转置或拆分。应结合 A16MatMul 的属性和模型原始投影形状,确认 scale_shape[0] 对应的实际通道,再回溯到导出端的 Linear 构造。
实际排查可以把检查分成两个位置。进入重排之前,确认量化参数是否已经对应正确的逻辑通道;重排之后,确认数据能否按 kernel 的布局恢复原通道顺序。如果前者已错,继续改后端重排只会掩盖导出问题;如果前者正确而后者失败,才有依据修改这条后端路径。
即使模型的 hidden size 是 64 的倍数,也不能据此跳过其他投影。模型中不同 Linear 的输出宽度可能不同,融合后的输出也可能被拆分;最终应检查失败算子的实际 N,而不是模型配置中的某一个主维度。
| 检查层次 | 要回答的问题 |
|---|---|
| 导出图 | 哪个投影层产生了不满足条件的 N? |
| 量化数据 | scale 的转置、分组和 padding 是否一致? |
| 编译路径 | 浮点通过而 W4 失败,差异集中在哪里? |
| 修复验收 | 生成模型后,局部输出和整模型任务是否都正确? |
已有证据解释了为何修改 group size 无效,但尚未记录具体投影层的 N,也没有新版工具链复测。修复方案应当继续验证,不能把这篇诊断当成已完成补丁。
若继续处理,可以先用固定版本的新工具链重跑同一个最小样例,再比较失败层在重排前的形状。需要修改编译器时,先对单层验证量化参数重排、补齐和裁剪,再验证整模型输出。已有记录中 BF16 转换通过,可作为隔离 W4 编译路径的对照,但还不等于部署后的性能和任务效果已经完成验证。
把布局误差与量化误差分开验证
只比较最终生成文本,很难知道修复是否正确。更直接的方法是给失败 Linear 固定一组输入,并保留三份输出:
| 对照输出 | 生成方式 | 用途 |
|---|---|---|
| 浮点参考 | 原始权重执行 Linear | 观察量化带来的误差 |
| 量化参考 | 用同一组 q、scale、zero point 在主机上解包、反量化后计算 | 固定量化方案本身的结果 |
| 设备输出 | 经重排、补齐及裁剪后的算子执行结果 | 检查后端实现是否保留量化语义 |
量化参考与浮点参考的差异,主要反映当前量化方案的损失;设备输出与量化参考的差异,则包含布局、打包和设备数值计算的影响。两组对比要分别记录,不能将明显的通道错位解释成“四位量化本来就有误差”。主机参考还必须采用源格式实际使用的 signed/unsigned 编码与 zero point 定义。
输入可以先包含全零、固定随机值,以及只激活某个输入维度的向量。全零输入用于核对偏置;单维输入便于观察某列权重是否落到了错误通道。除最大绝对误差外,也查看逐输出通道的误差分布;参考值接近零时,相对误差会失真,不能单独使用。
单层通过后,再检查 prefill 与 decode 的输出形状和数值。它们可能使用同一份权重,却经过不同的切分与执行路径。最后才进行整模型任务与性能测试,避免补齐只在某一种输入形状下成立。
四、一些反思
量化故障至少分成两类:一种是模型已经可运行,需要研究误差如何影响输出;另一种是后端根本无法表示当前量化布局。校准集和敏感层搜索主要帮助前者,具体方法见 Sophgo INT8 量化与精度调优;对后者应先检查 shape、对齐和数据重排。
判断参数是否值得调整,要先知道它控制哪个维度。group size、NPU lane 数和多设备数量虽然都表现为整数配置,作用对象却不同;定位到实际 shape 后,许多无效尝试可以直接排除。
这里更有价值的线索是,量化参数的布局本身也是算子实现的一部分。权重位宽、分组大小都合法,并不意味着这些数据已经符合后端的读取方式。修复应当让导出布局与 kernel 约定一致,并证明原通道的结果没有改变。