权重文件

因为 weight_0.binweight_107.bin108 个参数 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

所以:

Text Only
53 × (conv_weight + conv_bias) + matmul_weight + classifier_bias
= 106 + 1 + 1
= 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 的 Addresnet50.mlir (line 817)
  • 输入目录的 weight_map.json 也明确把编号 1–108 映射到 weight_0.binweight_107.binweight_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 模型:

Text Only
resnet50-v1-12-qdq_cix_s.onnx

其中 qdq 表示模型已经包含 Quantize / Dequantize(量化)节点;配置里还写明使用 adaround 校准/量化策略:onnx_resnet_v1_50build.cfg (line 18)

过程是:

Text Only
训练好的 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 中再导出的。

Text Only
已量化 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.binLiftConstantWeight.cpp (line 145)

也就是说,若你以现有 resnet50.mlir 为输入重新跑后端,导出的 bin 应来自该 MLIR;若从配置指定的 ONNX 重跑完整前端,则先由 ONNX 重新生成 MLIR,再生成相同语义的一组 bin。


对,理解正确。按这次日志,流程是:

Text Only
ONNX(含模型参数)
  ── cix-import-onnx ──> resnet50.mlir(参数作为 MLIR 常量)
  ── cix-compiler ──> input/weight_*.bin(把常量抽出)
                         + weight_map.json / weight_offset.json
                         + resnet50.ll

具体来说:

  1. 第一阶段 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<...>
  2. 第二阶段 cix-compiler 处理这个 MLIR。 其中的常量会被 LiftConstantWeight 抽成外部函数参数,写到:
  3. input/weight_0.bininput/weight_107.bin
  4. input/weight_map.json:MLIR 函数参数索引 → 文件名
  5. input/weight_offset.json:文件名 → DDR 地址
  6. 后端生成的程序不再把 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.literaldense_resource<...> 的形式保存。

随后 cix-compiler 再从这份 MLIR 常量导出 input/weight_*.bin

因此在编译过程完成后,磁盘上会同时存在:

Text Only
resnet50-v1-12-qdq_cix_s.onnx  原始量化模型,含参数
resnet50.mlir                  导入后的 IR,仍含参数
input/weight_*.bin             从 MLIR 抽出的部署参数

它们是同一组模型参数在不同阶段/格式下的副本。program.bin 本身通常不包含这批大权重。