Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

模型格式

提交一个名为 model.onnx 的自包含 ONNX 文件,连同 manifest.json。节点从 protobuf 中读取计算图,在 tract 下为它构建执行计划,再用你的适配器产出的张量运行它。训练代码和框架由你选择;导出的文件必须能在部署的运行时和审核策略之内工作。

ONNX 兼容性

部署接受 ONNX opset 13 到 19(含两端),并应用一份算子允许列表。能在你开发机上加载的计算图仍可能违反这些规则。把权重放在计算图内部:契约是一个模型文件加一个清单文件,所以指向旁边权重文件的计算图在那里什么也找不到。

允许列表包含以下算子:

Abs Add And ArgMax ArgMin AveragePool BatchNormalization Cast Ceil
Clip Concat Constant ConstantOfShape Conv Div Elu Equal Erf Exp
Expand Flatten Floor Gather GatherElements Gemm GlobalAveragePool
GlobalMaxPool Greater HardSigmoid Identity InstanceNormalization
LayerNormalization LeakyRelu Less Log LogSoftmax MatMul Max MaxPool
Mean Min Mul Neg Not Or Pad Pow PRelu Range Reciprocal ReduceMax
ReduceMean ReduceMin ReduceSum Relu Reshape Resize Selu Shape Sigmoid
Sign Slice Softmax Softplus Split Sqrt Squeeze Sub Sum Tanh Tile
Transpose Unsqueeze Where

这份列表是部署策略的一个快照,并不承诺每种算子/类型组合都能执行。未列出的变体(如 GatherND)即使有相关算子在列表上,也仍被拒绝。审核会读出文档中的每一个算子,包括 If、Loop 或 Scan 主体内以及模型局部函数内的算子,所以要检查导出器实际生成了什么:它可能与你写的不同。

赛季的列表只能收窄运行时所支持的范围。赛季可以发布自己更短的列表,平台会将其与这份列表取交集,所以赛季无法允许运行时跑不了的算子。若允许了,就会放过一个在对局中才失败的模型,那是最糟糕的拒绝时机。

属性不是算子

允许列表只列算子,对它们的属性不作规定。Conv 在列表上,所以空洞卷积是允许的:在 ONNX 中,膨胀率是 Conv 的一个属性,带空洞的计算图检查起来和其他任何 Conv 一样。步长、分组和非对称卷积核也都是属性,同理适用。

观测是一张地图,你计算图的感受野(输入能影响某格输出的最远距离)限定了站在该格的单元能响应的范围。一叠普通的 3x3 卷积每层让它增长两格。每层把膨胀率翻倍,增长就变成 2 的幂。膨胀率为 1、2、4、8 时,四层就能向每个方向触及十五格;四层不带空洞的卷积参数量相同,却只触及四格。

输入与输出

清单给出它们的名称,且名称必须是计算图自己的。inputs 中的每一项给出一个计算图输入的名称、dtype 和形状,以及构建它的适配器;outputs 中的每一项给出一个输出的名称、dtype 和形状。节点从文档的 graph.input 和 graph.output 中读取它们。这些是张量名称。计算图里产出这些张量的节点另有自己的名称,由导出器选定,例如 /fc2/Gemm,清单不得使用它们。

边界 dtype 有 bool、i8、u8、i16、u16、i32、u32、i64、u64、f32 和 f64。按计算图实际的样子声明 ONNX 边界类型:计算图内部使用的 dtype 不会成为边界 dtype,经过 Cast 的 float16 初始化器,其边界是 f32。

支持每一种地图尺寸。维度可以是名称(怎么做),所以全卷积计算图声明 [1, P, "H", "W"],通过一次审核就能应付赛季所用的每张地图,包括后来新增的。在内部自行计算索引的计算图无法为空间轴命名,因为运行时无法用符号对其做类型检查。它必须声明具体的轴,于是只能玩一种地图尺寸,其他尺寸会被运行时拒绝。

节点在回合之间什么都不保留。模型的记忆由运行器公开携带:声明一个名为 memory(整张地图)或 ant_memory(每只蚂蚁一行)的输出,运行器就会在你席位的下一次观测中以同一名称把上次的值交还给你,供你的适配器读取。你所在赛季的重量级规定它可以多大,不允许记忆的重量级会拒绝声明了记忆的模型。记忆一页涵盖传递和上限两方面。

大小如何测量

比赛的度量是:

S' = bytes(model.onnx) + bytes(manifest.json)

节点不做压缩,也不做估算。两项都按它重新计算哈希后的摘要来测量,所以无论怎样把权重打包进文件,都不会让它显得更小。tinybrains check 以 size metric 报告这个值。

原始字节堵住了一个漏洞。如果度量对计算图的初始化器做压缩,就会漏掉把训练好的权重放在 Constant 节点属性里的计算图:那种图根本没有初始化器,测出来几乎为零。

审核分配能容纳 S' 的最小重量级。它在重量级旁报告你的参数数量,但参数数量不参与分配。赛季可以对参数数量另设上限,该上限涵盖文档携带的每一个数值,无论放在哪里。

哈希逐字节标识文件,所以哪怕只改了格式的清单编辑,也会改变你的哈希和大小。

审核检查什么

审核按你声明的值重新计算两个文件的哈希,并从 protobuf 中读取计算图(参数、节点、算子、IR 版本、opset)。它构建执行计划,在你的 probe_dims 下用全零输入运行五次推理,把平台的策略应用到测得的结果上,再在游戏的参考观测上运行你的清单。它报告所用的输入形状、每个适配器的开销,以及测得的推理时间。有一个时间是门槛:探测的中位数必须在游戏的一个回合之内,Ants 是 1,000 ms,因为在最大地图上比一个回合还慢的模型根本打不了一局。除此之外没有任何重量级限制你的算力,对局时的回合时限才是真正起作用的边界。

提交前的测试介绍如何在你的机器上用同样的两个库做同样的测量。通过表示在参考用例上兼容。它完全不能证明对局实力,也不保证未来的每一份观测都在你的预算之内。