 是什么)
bits-per-byte (bpb) 是什么一句话概括bpb 衡量的是模型平均需要用多少个 bit比特来编码/预测文本中的每一个字节byte。数值越低说明模型对文本的预测越准、语言建模能力越强。它本质上是语言模型压缩文本能力的度量——一个越好的模型越能把文本压缩得更小。为什么要用 bpb而不用 loss 或 PPL语言模型训练时最直接的指标是交叉熵 loss但它有个大问题它依赖于你用的 tokenizer分词器。不同分词器把同一句话切成的 token 数量不同。如果一个模型的 tokenizer 把文本切成很少的 token每个 token 的 loss 看起来会偏高切得很碎则相反。所以per-token 的 loss / perplexity 在不同 tokenizer 之间根本没法公平对比。bpb 的巧妙之处在于它把每个 token 的损失换算成每个字节的损失。字节是文本的物理底层单位与 tokenizer 无关因此bpb 是一个跨模型、跨分词器都可比的客观指标——这正是 Karpathy 用它当自动实验打分器的原因。计算方式从交叉熵换算过来bpb (total_loss_in_nats) / (ln(2) × total_bytes)拆开来看模型在验证集上算出总的交叉熵损失通常以自然对数 nats 为单位。除以ln(2)≈ 0.693把单位从nats 换成 bits因为 bit 是以 2 为底的。再除以验证集的总字节数不是 token 数得到平均每字节多少 bit。用 per-token loss 表达也可以bpb (loss_per_token / ln(2)) × (token 数 / byte 数)后面那个token数 / byte数就是归一化因子把 token 尺度拉回到字节尺度。怎么理解这个数值一个字节原始有8 bit。如果模型完全没学到东西随机瞎猜bpb 会接近甚至等于 8。训练得越好模型对下一个字符/字节越有把握bpb 越低。现代 LLM 在英文文本上的 bpb 大致在0.6 ~ 1.0区间数值越接近 0 越好意味着强大的压缩/预测能力。直觉bpb 0.8 意味着模型平均只用 0.8 个 bit 就能猜出下一个字节的信息把原本需要 8 bit 的内容压缩了约 10 倍。在 Karpathy 这个 Autoresearch 场景里的意义放到你描述的实验里就很清楚了agent 整夜疯狂改train.py架构、超参、优化器、batch size……。每次固定跑 5 分钟训练然后测val_bpb验证集上的 bpb。用 bpb 而不是 loss 当分数是因为 agent 可能连 tokenizer、词表大小都改了只有 bpb 能保证这一版和上一版的分数是公平可比的。越低越好→ 新配置 bpb 更低就保留否则丢弃形成客观、自动化的迭代闭环。换句话说bpb 就是这个自动实验循环里那个统一、客观、不会被 tokenizer 作弊影响的裁判。一句话总结bpbbits-per-byte 模型平均编码每个文本字节所需的比特数是一个与分词器无关、可跨模型公平比较的语言建模质量指标越低代表模型越强——非常适合当自动化超参搜索的评分函数。问题如果一个模型的 tokenizer 把文本切成很少的 token每个 token 的 loss 看起来会偏高切得很碎则相反。这是为什么这是个很好的问题触及了 per-token loss 的本质。我来拆解一下。核心直觉一个 token 承载的信息量决定了它的 lossper-token loss 本质上衡量的是模型预测这一个 token 平均有多惊讶surprise。而一个 token 到底有多难预测取决于它承载了多少信息。关键点在于同一段文本总信息量是固定的比如这句话就那么多内容但你可以把它切成不同数量的 token。切成很少的 token→ 每个 token 是个大块塞了很多信息 → 每个 token 更难猜 →per-token loss 高。切成很多碎 token→ 每个 token 是个小块信息很少 → 每个 token 好猜 →per-token loss 低。信息总量守恒只是分摊到每个 token 头上的多少变了。用极端例子体会假设要建模英文文本apple。情况 A粗粒度word-level整个apple是 1 个 token。模型看到前文要一次性猜出 “apple” 这整个词。词表可能有几万个词要从中选对一个很难所以这 1 个 token 的 loss 很高。情况 B细粒度char-level切成a,p,p,l,e共 5 个 token。猜第一个a有点难但猜到a之后下一个是p就相对容易英文里 ap- 很常见看到app之后猜l、e几乎是顺理成章的模型很有把握。于是这 5 个 token 里大部分 token 的 loss 都很低平均下来 per-token loss 就低。关键总 loss 不变变的是分母这里有个守恒律。对于同一段文本如果两个模型建模能力一样好那么整段文本的总 loss总信息量/不确定性≈ 恒定而per-token loss 总 loss / token 数量所以token 数量少切得粗→ 分母小 →per-token loss 大token 数量多切得碎→ 分母大 →per-token loss 小per-token loss 的高低很大程度上只是反映了你把总信息量分摊到几个 token 上而不一定反映模型真实好坏。这就是它不能跨 tokenizer 比较的根本原因。为什么 bpb 能修正这个问题bpb 换的分母不是 token 数而是字节数bpb ∝ 总 loss / 字节数同一段文本字节数是物理固定的apple就是 5 个字节谁来切都一样不会因为你换 tokenizer 而变化。于是无论 tokenizer 切成 1 个 token 还是 5 个 token总 loss 大致相同信息量守恒分母字节数也相同→算出来的 bpb 就一致、可比了。它把分词粒度这个变量彻底消掉了。一个常见的疑惑那分摊到底怎么保证守恒严格来说是通过概率链式法则保证的。一段文本x xx的总负对数似然− log P ( x ) − ∑ i log P ( token i ∣ 前文 ) -\log P(x) -\sum_i \log P(\text{token}_i \mid \text{前文})−logP(x)−i∑logP(tokeni∣前文)无论你怎么分词只要模型完美P ( x ) P(x)P(x)这段文本作为一个整体出现的概率是同一个客观的东西。你把它拆成 1 项相乘还是 5 项相乘乘积不变取 log 后求和的总量也不变——只是项数token 数变了。项数少 → 每项平均值大per-token loss 高项数多 → 每项平均值小per-token loss 低这就是守恒的数学根源。小结粗粒度 tokenizer细粒度 tokenizer每个 token 信息量大小每个 token 是否好猜难容易per-token loss高低token 总数少多整段总 loss≈ 相同≈ 相同按字节归一的 bpb≈ 相同≈ 相同一句话per-token loss 高低主要取决于信息被摊到多少个 token 上而 bpb 用固定的字节数当分母把这个人为的分词变量消掉了所以才客观可比。问题bpb (total_loss_in_nats) / (ln(2) × total_bytes)通过例子解释这个公式自然对数 nats 为单位是什么第一步nats 是什么natnatural unit of information自然信息单位就是用**自然对数 ln以 e 为底**来度量信息量。回到信息论基础bit用 log₂ 度量“用 2 进制编码需要几位”nat用 ln 度量“用 e 进制编码需要几位”虽然 e 进制不实际存在但数学上完全合法两者换算关系1 nat 1/ln(2) bit ≈ 1.443 bit 1 bit ln(2) nat ≈ 0.693 nat为什么神经网络训练常用 nats因为交叉熵 loss 用的是自然对数 lnloss-ln(P(正确答案))PyTorch 的nn.CrossEntropyLoss输出的就是 nats 单位的 loss。这纯粹是数学方便求导简单、和概率分布的自然形式对应。第二步通过例子理解公式假设一个超简单的玩具场景场景设置验证集文本hi2 个字节使用 char-level tokenizerh和i各是 1 个 token模型要预测这 2 个字符模型预测过程简化预测第 1 个字符h假设模型给出的概率分布只看 3 个字符P(h) 0.5 P(i) 0.3 P(x) 0.2正确答案是hloss 为loss₁ -ln(0.5) 0.693 nats预测第 2 个字符i假设模型看到h后给出P(i) 0.7 P(h) 0.2 P(x) 0.1正确答案是iloss 为loss₂ -ln(0.7) ≈ 0.357 nats总 losstotal_loss 0.693 0.357 1.05 nats套公式计算 bpb现在套用公式bpb total_loss_in_nats / (ln(2) × total_bytes)代入数值bpb 1.05 / (0.693 × 2) 1.05 / 1.386 ≈ 0.758 bits/byte拆解公式的每一部分total_loss_in_nats 1.05这是模型在验证集上的总交叉熵损失单位是 nats自然对数单位。ln(2) ≈ 0.693这是单位换算因子作用是把 nats 转成 bitsloss_in_bits loss_in_nats / ln(2)所以这一步把 1.05 nats 换算成1.05 / 0.693 ≈ 1.515 bits含义整段文本hi总共产生了约 1.515 bits 的惊讶不确定性。total_bytes 2文本hi有 2 个字节。这是归一化分母用来算平均每字节。最终结果bpb 1.515 bits / 2 bytes ≈ 0.758 bits/byte含义模型平均需要 0.758 个 bit 来编码原始文本里的每一个字节。第三步直觉理解为什么要除以 ln(2)你可能会问为什么不直接用 nats非要转成 bits因为bit 更有物理意义一个字节原本是 8 bits这是信息存储的物理单位。如果用 bpb你能直观看出完全随机→ 8 bits/byte没压缩模型很强→ 接近 0 bits/byte压缩到极致一般模型→ 比如 0.758 bits/byte压缩了约 10 倍如果用 “nats per byte”npb 1.05 / 2 0.525 nats/byte这个数字对人类缺乏直觉——你不会说一个字节原本有 5.545 nats因为 8 bits 8 × ln(2) ≈ 5.545 nats大家对 8 bits 更熟悉。除以 ln(2) 就是为了让结果单位对齐到人类习惯的 bits。第四步换一个例子感受 tokenizer 无关性假设同样的文本hi换个 tokenizer。用 word-level tokenizer整个hi是 1 个 token模型一次性预测hi这个词P(hi) 0.35 loss -ln(0.35) ≈ 1.05 nats巧合地和前面总 loss 一样现实中可能不同但信息论保证能力相同的模型会趋近套公式bpb 1.05 / (0.693 × 2) ≈ 0.758 bits/byte结果完全一样虽然char-level 是 2 个 tokenper-token loss 1.05/2 0.525 natsword-level 是 1 个 tokenper-token loss 1.05/1 1.05 natsper-token loss 差了一倍但bpb 都是 0.758——因为分母用的是固定的字节数不是 token 数。第五步完整的计算流程总结训练框架如 PyTorch输出 cross_entropy_loss (nats 单位) Σ -ln(P(正确token)) 计算 bpb 1. 统计验证集总字节数total_bytes 2. 累加所有 token 的 losstotal_loss (nats) 3. 套公式 bpb total_loss / (ln(2) × total_bytes) (total_loss / ln(2)) / total_bytes ↑ ↑ ↑ nats→bits 单位换算 归一到每字节一句话总结bpb 公式的本质把神经网络吐出来的 nats 单位 loss先除以 ln(2) 换成人类熟悉的 bits再除以文本的物理字节数做归一化得到平均每个字节需要多少 bit 来编码——这个指标与 tokenizer 切分方式无关只反映模型对文本的真实建模能力。后记2026年8月15日于上海在claude opus 4.8辅助下完成。