The model page said 8-bit. The checkpoint is 4.49 bits per parameter.

本文为原文前 6,000 字符的节选翻译,完整内容请查看原文。

I was working out how much disk Kimi K3.1 Pro would need, and the model page gave me two numbers that cannot both be true. The card says MXFP4 weights / MXFP8 activations. The badge beside the download button says 8-bit precision. So I added up the shards I would actually pull and divided by the parameter count the platform reports, and got 4.49 bits per parameter. That is not a rounding difference. 2.78 trillion parameters at 8 bits is 2,780 GB; at 4 bits it is 1,390 GB. The 96 shards in that repository come to 1,561 GB. So one of those three numbers is answering a question I did not ask, and if you are sizing a download or a disk you need to know which one it is.

我当时正在计算 Kimi K3.1 Pro 需要多少磁盘空间,但模型页面给出的两个数字不可能同时成立。卡片上写着 MXFP4 权重 / MXFP8 激活值,而下载按钮旁边的徽章却显示 8-bit 精度。于是,我将实际需要拉取的碎片(shards)大小相加,并除以平台报告的参数量,得出的结果是每个参数 4.49 位。这并非舍入误差:2.78 万亿参数在 8 位下应为 2,780 GB,在 4 位下应为 1,390 GB,而该仓库中的 96 个碎片总计 1,561 GB。因此,这三个数字中必有一个在回答我并未提出的问题;如果你正在估算下载量或磁盘空间,你需要弄清楚究竟是哪一个。

The check is fifteen lines and it runs against the public API: import json, urllib.request REPO = “moonshotai/Kimi-K3.1-Pro” api = json.load(urllib.request.urlopen( f”https://huggingface.co/api/models/{REPO}?blobs=true”)) files = [f for f in api[“siblings”] if f.get(“size”) and f[“rfilename”].endswith(“.safetensors”) and ”/” not in f[“rfilename”]] weight_bytes = sum(f[“size”] for f in files) params = api[“safetensors”][“total”] print(f”{len(files)} shards, {weight_bytes / 1e9:,.1f} GB”) print(f”{params:,} weights, {weight_bytes * 8 / params:.4f} bits each”) Two details in it carry more weight than the arithmetic. ?blobs=true is what makes the endpoint return a size per file at all; without it you get file names and nothing to add up. And the ”/” not in … filter keeps only the shards sitting at the top of the repository, because plenty of repositories keep other copies of the same weights in subfolders. I will come back to that filter, because it is the line that saved me from a wrong answer I had already written down. For Kimi K3.1 Pro it prints 96 shards, 1,561.3 GB, 2,780,110,363,392 weights, 4.4928 bits each.

这段检查代码只有十五行,运行在公共 API 之上:[代码略]。其中有两个细节比算术本身更重要。?blobs=true 是让端点返回每个文件大小的关键,否则你只能得到文件名而无法进行累加。而 "/" not in ... 过滤器只保留了位于仓库根目录的碎片,因为许多仓库会在子文件夹中保留相同权重的其他副本。我会回头再谈这个过滤器,因为它让我避免了一个已经写下的错误答案。对于 Kimi K3.1 Pro,它输出的结果是 96 个碎片、1,561.3 GB、2,780,110,363,392 个权重,每个权重 4.4928 位。

Where the 1,561 GB goes The number stops being mysterious once you subtract the three things a four-bit checkpoint always contains. what it is bytes GB 4-bit weights, at half a byte each 1,361,370,415,104 1,361.4 tensors kept at 16 and 32 bits 114,761,311,232 114.8 block scales, one byte per 32 weights 85,085,650,944 85.1 the three terms together 1,561,217,377,280 1,561.2 the files on disk 1,561,293,160,568 1,561.3 left over 138,589,683 0.139 Three terms take you to within 139 MB of the download, which is 0.009% of it. That is what convinced me the 4.49 is a reading of the checkpoint and not a misunderstanding of it: the dtype counts the platform publishes, half a byte per four-bit weight, plus the tensors it left alone, plus one scale byte per 32, lands on the file size.

1,561 GB 的去向:一旦减去四位检查点(checkpoint)中必然包含的三部分内容,这个数字就不再神秘了。这三部分分别是:每个权重占半字节的 4 位权重(1,361.4 GB)、以 16 位和 32 位存储的张量(114.8 GB),以及每 32 个权重占 1 字节的块缩放因子(85.1 GB)。这三项相加为 1,561.2 GB,与磁盘上的 1,561.3 GB 仅相差 139 MB,即总量的 0.009%。这让我确信 4.49 是对检查点的准确读取,而非误解:平台发布的数据类型计数,加上未量化的张量和缩放字节,正好吻合文件大小。

The interesting part is the middle row. Some of that model is not four bit at all — 114.8 GB of it is kept at 16 and 32 bits, either because quantizing those layers costs quality nobody wanted to spend or because they are too small to be worth packing. Whatever the reason, a “4-bit model” is a description of the bulk, not of every byte. And the card’s parameter count holds up. The platform’s counts add to 2,780,110,363,392, which is the card’s 2.8T rounded. So the vendor and the platform agree on how many weights there are. They disagree only on what each one costs.

有趣的部分在于中间那一行。该模型的一部分根本不是四位的——其中 114.8 GB 以 16 位和 32 位存储,原因可能是量化这些层会造成没人愿意承担的质量损失,或者是因为它们太小而不值得压缩。无论原因如何,“4 位模型”是对主体部分的描述,而非对每一个字节的描述。卡片上的参数量是准确的,平台统计的总数为 2,780,110,363,392,这正是卡片上四舍五入后的 2.8T。因此,供应商和平台在权重数量上达成了一致,只是在每个权重的存储成本上存在分歧。

The number that talked me out of my own check Before I pointed this at Kimi, I had it pointed at a model whose weights I already knew were four bit: openai/gpt-oss-120b. It printed 195.8 GB and 13.405 bits per parameter. That is nonsense on its face — 116.8 billion weights do not cost 13 bits each in a four-bit checkpoint. The repository holds the same weights three times over: the mxfp4 shards at the top of the tree, original/ at 65.2 GB, and metal/ at 65.2 GB. Every one of them went into my sum. Restricted to the 15 top-level shards it is 65.2 GB, and 4.4680 bits per parameter, and the same three-term breakdown predicts 65.2 GB to the tenth.

让我放弃原有检查逻辑的数字:在将此方法用于 Kimi 之前,我曾将其用于一个已知权重为四位的模型:openai/gpt-oss-120b。它输出的结果是 195.8 GB 和每个参数 13.405 位。这显然是荒谬的——在四位检查点中,1168 亿个权重不可能每个占用 13 位。该仓库将相同的权重存储了三次:根目录下的 mxfp4 碎片、original/ 文件夹下的 65.2 GB 以及 metal/ 文件夹下的 65.2 GB。它们全部被计入了我的总和。如果仅限于顶层的 15 个碎片,则为 65.2 GB,每个参数 4.4680 位,且同样的三项分解法能精确预测出 65.2 GB。

The lesson is not “be careful with sums”. A checkpoint repository is not a file, it is a directory that may contain several encodings of the same weights, so “how big is this model” has one answer per encoding, and my first instrument did not know which question it was answering. It reported a number three times too large with complete confidence, and it would have gone into this post if I had not had a second model to compare against.

教训不是“计算要小心”。检查点仓库不是一个文件,而是一个可能包含相同权重多种编码的目录,因此“这个模型有多大”对于每种编码都有一个答案,而我最初的工具并不知道它在回答哪个问题。它极其自信地报告了一个大出三倍的数字,如果我没有第二个模型进行对比,这个错误数字就会出现在这篇文章里。

The badge Both of those repositories are tagged 8-bit by the platform. Neither is tagged 4-bit. gpt-oss-120b also carries mxfp4, so the same tag list will tell you both things about the same repository at the same time. I do not know how the badge is computed, and I am not going to guess in a sentence that reads like a finding. What I can report is what it says on two checkpoints whose weights are unambiguously four bit, and on both of them it says eight. If the badge is reading the storage dtype, that would explain it: a packed four-bit weight sits inside an unsigned 8-bit container, so the dtype of the tensor is U8 even though two weights share a byte. That is a guess about somebody else’s code, it is testable only from the inside, and I have marked it as a guess. What I can say without guessing is that the badge is a summary, not a measurement of your download, and the arithmetic above is five lines.

关于徽章:这两个仓库都被平台标记为 8-bit,没有一个被标记为 4-bit。gpt-oss-120b 也包含 mxfp4,因此同一个标签列表会同时告诉你关于同一个仓库的这两件事。我不知道徽章是如何计算出来的,我不会用听起来像结论的句子去猜测。我能报告的是,在两个权重明确为四位的检查点上,它们都显示为八位。如果徽章读取的是存储数据类型(dtype),那就解释得通了:打包的四位权重位于无符号 8 位容器内,因此张量的 dtype 是 U8,即使两个权重共享一个字节。这是对他人代码的猜测,只能从内部测试,我已经将其标记为猜测。无需猜测的是,徽章只是一个摘要,而非你下载量的度量,而上述算术过程只需五行代码。

What I did not do I did not download 1.5 TB. I did not load the model. I have no throughput, no VRAM figure, no benchmark for it — every number here came from the two API responses this check reads, and from nothing else. There is one soft spot in the accounting and it belongs in the post rather than in my notes. Block scales are not exposed per tensor in that API, so I assumed the common one byte per 32 weights. It fits to 0.009%, and I cannot tell from the outside whether that is a correct assumption or a lucky one. If some architecture uses a different block size, the middle of that table would drift and the total would still look plausible, which is the shape of error I like least. What to do with it If you are sizing a download, the number you want is the bytes under the path you will actually pull, divided by th

我没做的事:我没有下载 1.5 TB 的数据,也没有加载模型。我没有吞吐量、显存占用数据或基准测试结果——这里的所有数字都来自该检查代码读取的两个 API 响应,别无他处。核算中有一个薄弱环节,我必须在文中说明:该 API 并未按张量暴露块缩放因子,所以我假设了常见的每 32 个权重占 1 字节。结果吻合度达到 0.009%,我无法从外部判断这是一个正确的假设还是仅仅是运气好。如果某些架构使用了不同的块大小,表格中间的数据就会产生偏差,但总数看起来依然合理,这是我最不喜欢的错误形式。如何处理:如果你正在估算下载量,你需要的数字是你实际将要拉取的路径下的字节数,除以……