权重文件
因为 weight_0.bin 到 weight_107.bin 是 108 个参数 tensor,不是 108 个网络层。
这个导出的 ResNet 图实际包含:
| 参数来源 | 数量 | 文件数 |
|---|---|---|
| Conv(包含 shortcut/downsample 的 1×1 projection Conv) | 53 | 53 个 kernel + 53 个 bias = 106 |
| 最后的分类 MatMul | 1 | 1 个 weight |
| 分类输出的 Add bias | 1 | 1 个 bias |
| 合计 | 108 |
所以:
ResNet-50 中的 “50” 是模型架构的命名/主干深度计数,不等价于“导出后只有 50 个参数文件”。尤其 shortcut 的 projection 1×1 卷积在计算图中是独立的 Conv,必须有独立 kernel 和 bias,但通常不按主干的 50 层去计数。
我核对了当前 MLIR:
- 有 53 个
onnx.Conv; - 有 1 个
onnx.MatMul; - 最后分类部分是
MatMul([1,2048], [2048,1024]),随后 slice 到 1000 类并做一次带[1000]bias 的Add:resnet50.mlir (line 817)。 - 输入目录的
weight_map.json也明确把编号 1–108 映射到weight_0.bin–weight_107.bin:weight_map.json (line 1)。
这也意味着你前面讨论的 2 MiB GM 任务中,不能把“一个 kernel 一份 weight”当作固定假设:Conv kernel 常常至少有 weight 和 bias 两个常量输入;QDQ/融合前后还可能有 scale、zero-point 等常量,不过此导出流程没有把它们全部各自单独输出为这 108 个 bin。
对。这里的 weight_*.bin 是“常量参数文件”的统称,不是狭义的卷积 kernel 专用文件。
因此它既包含:
- Conv 的 filter/kernel:例如
[OC, IC, KH, KW] - Conv 的 bias:例如
[OC] - 最后 MatMul 的矩阵参数
- 最后分类
Add的 bias:[1000]
在推理模型里它们都是不随输入变化的常量,需要从 DDR 加载,因此编译/运行时统一按 weight 文件处理。
从文件大小也能看出来:大文件通常是卷积 kernel,小文件通常是 bias。例如目录中的 weight_107.bin 只有 256 B,明显更像最终分类 bias 的对齐存储,而不是卷积核。
不是随机的。它们来自这个示例携带的、已经训练并量化后的 ResNet-50 ONNX 模型:
其中 qdq 表示模型已经包含 Quantize / Dequantize(量化)节点;配置里还写明使用 adaround 校准/量化策略:onnx_resnet_v1_50build.cfg (line 18)。
过程是:
训练好的 ResNet-50 checkpoint
→ 导出/量化为 QDQ ONNX
→ 导入 MLIR,常量仍嵌在 IR 中
→ 编译器把常量逐个抽出
→ weight_0.bin ... weight_107.bin
这些 bin 是该特定 checkpoint + 特定量化配置下的固定数值;同一 ONNX、同一编译选项重新编译,内容应一致。换一个训练 checkpoint、重新量化,或换 calibration 数据/量化参数,bin 内容就会不同。
编译器的 LiftConstantWeight pass 本身不生成随机数,它直接把 MLIR 常量的原始字节写入 weight_i.bin,然后把原常量改成带 cixnn.constant_weight 的函数参数:LiftConstantWeight.cpp (line 145)。
所以更准确地说:
- ResNet-50 的网络结构是固定的;
- 这些
weight_*.bin是某一个已训练、且已量化的 ResNet-50 实例的参数; - 它们不是“ResNet-50 天生唯一的一套固定文件”,也不是编译器随机构造的。
input文件夹总共有25M,onnx文件有35M,onnx文件中有图的信息,还有各个数据值。
逻辑上的源头是 ONNX;weight_*.bin 是从 MLIR 中再导出的。
已量化 ONNX(携带训练后的参数)
└─ 导入得到 resnet50.mlir
└─ 常量以 dense_resource / dense 属性嵌在 MLIR 中
└─ LiftConstantWeight 抽取并写出 weight_*.bin
所以两种说法都对,但层次不同:
resnet50-v1-12-qdq_cix_s.onnx是模型参数最初随模型文件带来的地方;- resnet50.mlir (line 1) 是 ONNX 导入后的中间表示,其中已经含有这些常量,例如
dense_resource<...weight_quantized>; input/weight_*.bin是编译过程从 MLIR 常量实际序列化出来的部署文件,而不是另一套独立下载的随机权重。
代码也能确认:LiftConstantWeight 对每个 MLIR arith.constant 取得 DenseElementsAttr,调用 writeWeightBin() 写出 weight_i.bin:LiftConstantWeight.cpp (line 145)。
也就是说,若你以现有 resnet50.mlir 为输入重新跑后端,导出的 bin 应来自该 MLIR;若从配置指定的 ONNX 重跑完整前端,则先由 ONNX 重新生成 MLIR,再生成相同语义的一组 bin。
对,理解正确。按这次日志,流程是:
ONNX(含模型参数)
── cix-import-onnx ──> resnet50.mlir(参数作为 MLIR 常量)
── cix-compiler ──> input/weight_*.bin(把常量抽出)
+ weight_map.json / weight_offset.json
+ resnet50.ll
具体来说:
- 第一阶段
cix-import-onnx读取resnet50-v1-12-qdq_cix_s.onnx。 ONNX 内的 initializer,包括 Conv kernel、Conv bias、MatMul weight、最终 Add bias 等,都会进入resnet50.mlir的常量,如torch.vtensor.literal/dense_resource<...>。 - 第二阶段
cix-compiler处理这个 MLIR。 其中的常量会被LiftConstantWeight抽成外部函数参数,写到: input/weight_0.bin…input/weight_107.bininput/weight_map.json:MLIR 函数参数索引 → 文件名input/weight_offset.json:文件名 → DDR 地址- 后端生成的程序不再把 25 MiB 权重塞进
program.bin,而是在运行时按aite_individual.ini将这些 bin 文件分别加载到 DDR 指定地址。
所以更准确地说,不是 ONNX importer 直接“放到 input 中”;而是:
ONNX importer 先把 ONNX 参数带入 MLIR,随后
cix-compiler从 MLIR 抽取常量并生成input/weight_*.bin。
对,有一份。
在这次编译流程中,resnet50.mlir 是从 ONNX 新生成的,里面包含所有模型常量,例如 Conv 的 kernel/bias、MatMul weight 等。它们以 torch.vtensor.literal 和 dense_resource<...> 的形式保存。
随后 cix-compiler 再从这份 MLIR 常量导出 input/weight_*.bin。
因此在编译过程完成后,磁盘上会同时存在:
resnet50-v1-12-qdq_cix_s.onnx 原始量化模型,含参数
resnet50.mlir 导入后的 IR,仍含参数
input/weight_*.bin 从 MLIR 抽出的部署参数
它们是同一组模型参数在不同阶段/格式下的副本。program.bin 本身通常不包含这批大权重。