尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Byte Latent Transformer:字节熵动态切Patch,彻底告别分词器

Byte Latent Transformer:字节熵动态切Patch,彻底告别分词器 【面试高频】扔掉分词器Byte Latent Transformer 用字节熵动态切 Patch 全解析如果你最近在准备大模型方向的面试一定遇到过类似问题“为什么大模型不直接用字符或字节训练”、“BPE 词表会不会成为瓶颈”、“如果不用分词器模型结构要怎么改”过去我们通常会答分词器能压缩序列长度、减少计算量但会带来词表固定、跨语言不平衡、罕见词 OOV 等问题。然后面试官会追问一句“那有没有办法从根本上绕过分词器”——这个问题在 2024 年之前不太好答因为主流模型无论怎么做最终都要落到一个预定义的词表上。但 Byte Latent TransformerBLT出现后这个问题有了新的回答思路。它不是换一个更好的分词器而是把“分词”这个过程本身去掉改为让模型在运行时根据字节熵动态切分 patch。这篇文章不打算只复述论文我会把它拆成四块先讲清楚为什么分词器是瓶颈再讲 BLT 的架构和动态切 patch 原理然后给出一套面试答法最后提供一套小规模验证思路。你读完能直接用在两件事上理解 BLT 到底改了什么东西、面试被追问时怎么组织答案。1. 为什么“分词器”会成为大模型发展的瓶颈先明确一个基础概念分词器Tokenizer是 NLP 里把原始文本切分成离散 token 的组件。它做的不是简单的按字切分而是维护一个固定大小的词表把常见子词映射成整数 id模型只能在这个词表上做 embedding 和输出概率分布。主流的词表构建方式有三种BPEByte Pair Encoding按相邻符号的共现频率不断合并WordPiece 按最大似然增量构建SentencePiece/Unigram 用概率模型做剪枝。LLaMA 系列用的是字节级 BPEGPT 系列用的是 byte-level BPE 变体。这些方案在工程上非常成熟但它们有一个共同特征词表在训练前就固定了切分规则在推理时不可变。分词器带来的问题在近两年的大模型应用中被不断放大词表固定导致 OOVout-of-vocabulary问题。虽然字节级 BPE 已经解决了严格意义上的 UNK但新词、emoji、特殊符号、代码语言新语法仍然会被切成不自然的碎片。跨语言不公平。英语在主流语料里占比高平均一个词 1~2 个 token而中文、日文、阿拉伯文这类语言一个字或一个音节往往对应一个或多个 token同样的语义非英语文本的序列长度可能膨胀 2~3 倍。序列长度与信息密度错配。一个空格、一个标点、一行连续重复的日志和一段密集的数学推导在 Tokenizer 看来都是 token模型在每层对它们施加完全相同的计算量。词表本身带来记忆负担和维护成本。词表大小从 32K 到 250K 不等embedding 矩阵随之增大如果要更新词表通常需要重新训练或做 embedding 对齐工程成本很高。从架构角度看分词器真正的问题不是“切得不够好”而是“切分粒度完全由语料统计决定而不是由语义复杂度决定”。这意味着模型无法在信息密度低的地方跳过计算也无法在信息密度高的地方投入更多计算。BLT 想解决的正是这个基础性错配。2. Byte Latent Transformer 的核心思路用字节熵动态切 PatchByte Latent Transformer 的出发点很直接既然词表会带来这么多限制那能不能回到最原始的字节序列在字节级别建模直接做会立刻遇到两个问题字节序列太长Transformer 的注意力计算扛不住单个字节的语义太弱逐字节预测效率很低。BLT 的解法是引入“patch”这个概念。patch 可以理解为一个可变长度的字节片段它替代 token 成为全局 Transformer 的基本输入单位。关键是如何切分 patch。BLT 的做法不是固定窗口而是根据“字节熵”来动态决定切分位置。字节熵在这里指的是模型对当前字节位置不确定性的估计。如果模型对一个字节的预测非常困难说明这里的信息密度高需要更细的粒度去处理如果模型很容易预测下一个字节说明这里冗余度高可以把多个字节合并成一个 patch。这个思路非常像视频编码中的“关键帧与差异帧”画面变化剧烈时频繁插入关键帧画面静止时用很少的帧就能表示。我在看 BLT 设计时最受触动的一点是它把“信息密度”作为计算资源分配的依据而不是把“固定 token 数”当作默认约束。这相当于把传统模型的“每个 token 一律同样计算”改成了“每个 patch 按含有的信息量决定规模”计算预算的分配逻辑发生了本质变化。选择“字节”而不是“字符”也有工程考虑。字符是 Unicode 层面的概念不同语言字符数量差异巨大做字符级建模仍然会有退化问题字节只有 256 种取值能够覆盖任意文本编码天然支持多语言。所以字节是最通用的最低粒度输入而动态 patch 则补足了它序列过长的短板。3. BLT 的架构解析BLT 的整体结构可以分成五个模块按数据流向分别是字节流局部编码器local encoder字节熵估计器entropy estimator动态 patch 切分dynamic patchification全局潜在 Transformerglobal latent transformer局部解码器local decoder第一个模块负责把原始字节映射成局部潜表示。由于单个字节信息量太少通常会用一个轻量级的卷积或一层浅 Transformer对相邻字节做局部上下文建模输出每个字节位置的特征向量。第二个模块是 BLT 的核心创新点即字节熵估计器。它的任务是预测每个字节位置的熵值也就是模型对“下一个字节是什么”的不确定程度。熵估计器的参数很小可以用一个小的分类头独立训练也可以和全局模型联合训练。第三个模块根据熵估计的结果做动态切分。一个常见做法是设定一个累积熵阈值逐个字节累加局部熵值当累积熵达到阈值时在该位置切开一个 patch。这样切出来的 patch内部的字节在信息复杂度上大致“等量”而不是在长度上等量。第四个模块是全局潜在 Transformer。它把切分好的 patch 做 pooling例如取平均或拼接一个可学习的 patch embedding再加上位置编码然后送入一个标准的 Transformer 层堆叠。由于 patch 的数量远小于原始字节数量全局 Transformer 的计算量会显著降低。第五个模块是局部解码器。全局 Transformer 输出的是 patch 级别的表示但最终预测要回到字节级别所以需要一个轻量解码器把 patch 表示上采样展开成每个字节的表示再做字节级预测。整个结构可以理解为局部模块处理字节细节全局模块处理语义抽象熵估计器决定两者之间的边界。这种设计让模型的注意力复杂度由 patch 数量决定而 patch 数量由信息量决定不再由句子长度直接决定。从训练角度看BLT 通常需要先让熵估计器和局部编码器形成稳定的切分和字节表示然后整体联合训练。确切的训练阶段和超参数我建议以官方论文公开版本为准但整体思路是“先让动态切分稳定再优化全局语义建模”。如果你准备在面试中描述这个架构不需要背每一个参数能把上面的数据流讲清楚就可以了。4. 从“固定 token”到“动态 patch”的关键区别传统 token 和 BLT 中的动态 patch虽然都是 Transformer 的基本输入单元但在设计哲学上差异很大维度传统 Tokenizer TokenBLT 动态 Patch切分单位词表子词粒度固定字节连续段长度可变切分依据训练前的统计合并/Unigram 概率运行时的字节熵估计是否依赖固定词表是词表大小固定否直接基于字节序列长度与文本长度近似线性相关与信息密度相关冗余文本会压短多语言公平性词表偏向训练语料分布所有语言都从字节开始天然公平计算分配每个 token 相同计算量信息密度高的地方切碎、多算冗余处合并、少算工程复杂度简单稳定生态成熟需要额外训练熵估计器推理和批处理更复杂从应用场景看动态 patch 的优势在几种情况下特别明显。第一种是代码生成。代码里充满空格、缩进、对称括号和重复性标识符传统 BPE 会把缩进拆成很多 token浪费大量计算。BLT 可以在空格和连续重复符号处快速合并 patch把计算留给真正有意义的逻辑片段。第二种是非拉丁语种文本。中文、日文、阿拉伯文等语言在传统 tokenizer 下序列往往被拉得很长而 BLT 以字节为基础不依赖训练语料中的词频分布能够更好地压缩这些语言的表示长度。第三种是长文档理解。长文本中大量内容是上下文铺垫真正需要精细推理的段落往往不多。BLT 的动态切分天然形成了“粗读开头、精读关键段”的机制虽然它不是显式做摘要但从信息分配角度看效果类似。但要注意动态 patch 不是在所有情况下都更优。对于极短的分类文本、固定格式日志、字段结构高度规则的数据用传统 tokenizer 已经非常稳定BLT 反而引入了熵估计和不规则序列的复杂度。这个边界需要在面试回答中主动讲出来能体现你真的理解它而不是只会背亮点。5. 面试高频题BLT 相关的追问与答法这一节整理几个最常被追问的问题并给出答题框架。面试官想要的不只是一个概念定义而是你能把“字节熵”和“动态 patch”背后的技术选择讲出层次。5.1 为什么大模型不直接用原始字节训练答法要点直接基于字节训练的主要问题是序列过长和局部语义弱。一个几百 token 的句子换成字节后可能变成几千甚至上万Transformer 的注意力是平方复杂度无法承受。而单个字节的语义太弱模型需要从很底层开始学习语言结构训练效率低。传统 BPE 这类方法本质上是“用统计压缩换取序列长度下降”。BLT 的思路是不回到固定字节长度也不回到固定词表而是用熵估计把字节合并成可变长度 patch兼顾了字节的通用性和序列长度的可控性。5.2 BPE 这类分词器存在哪些根本性问题答法要点三个方面。第一词表固定切分粒度在训练前就锁死无法适应新领域新词。第二跨语言不平衡词表偏向训练语料占比高的语言导致非英语语言序列膨胀。第三信息密度与计算量错配每个 token 得到相同计算但空格和一个复杂的数学符号显然不应该等价。更深一层的问题是Tokenzier 把“语言是什么”直接简化成了“词表里有什么”这限制了模型表达任意未见过的文本。5.3 BLT 如何决定什么时候切分一个 patch答法要点BLT 用一个轻量熵估计器预测每个字节位置的熵然后按累积熵阈值切分。如果一个区域内模型很容易预测下一个字节熵低就继续合并一旦累积熵超过阈值说明这里包含的信息复杂度已经到了一定的量级就切开让全局 Transformer 在这一段投入更多计算。这个阈值是超参数可以基于验证集调整。关键点是切分不是看固定长度而是看信息含量。5.4 动态 patch 是否意味着 batch 里每个样本长度都不一样答法要点是的这不是 bug而是动态切分的自然结果。批量训练时可以用 padding、ragged tensor 或 bucketing 策略来缓解。真正有挑战的是推理阶段不同请求切出的 patch 数量不同KV cache 的布局、连续 batch 的调度都要比固定 token 结构更灵活。面试如果能提到这一点说明你已经想到了工程层。5.5 BLT 完全不需要分词器了吗答法要点严格来说BLT 不再依赖传统意义上的固定词表分词器它没有“词表”这个组件。但它仍然需要字节编码仍然需要把字节序列切分成 patch。所以准确的表达是它去掉了“有词表的分词器”而不是去掉了“序列结构化”。面试时最好主动区分这两个概念避免绝对化表述。5.6 BLT 一定比 Tokenizer 模型省算力吗答法要点不一定。在大量冗余文本、空格、重复内容上BLT 的压缩效果明显patch 少全局 Transformer 快。但在信息密度极高的文本上比如数学推导、外语生僻词较多的段落熵估计会切出很多细分 patch序列长度可能不降反升。节省来自“对冗余内容的压缩”而不是来自“免费的结构创新”。这个回答能体现你对动态切分的真实理解。6. 想动手验证 BLT 思路从哪里开始BLT 目前属于研究向工作复现完整模型成本不小但如果只是想验证“动态 patch”是否有效可以做一个 MiniBLT 实验。下面给出一套可行的小规模验证路径。6.1 准备一个小型数据集先准备一个小型双语或混合语料例如几万条中文新闻 几万条代码片段。数据不需要太大重点是让“文本冗余度”和“信息密度”的差异足够明显。数据预处理只需要清洗为纯文本不需要做任何分词。6.2 实现字节熵估计与动态切分用一个小型 CNN 或单层 Transformer 作为局部特征提取器对每个字节做预测得到该位置的熵估计。然后实现动态切分函数import torch import torch.nn.functional as F def estimate_entropy(local_logits: torch.Tensor) - torch.Tensor: 根据局部模型的预测 logits 计算每个字节位置的熵。 local_logits 形状: [batch, seq_len, 256] 返回: [batch, seq_len] probs F.softmax(local_logits, dim-1) # 熵 -sum(p * log p) entropy -(probs * (probs.clamp_min(1e-9).log())).sum(dim-1) return entropy def dynamic_patchify( byte_ids: torch.Tensor, entropy: torch.Tensor, threshold: float 1.0, min_patch_len: int 2, max_patch_len: int 256, ): 按累积熵阈值动态切分 patch。 参数: byte_ids: 字节 id 序列 entropy: 每个字节位置的熵估计 threshold: 累积熵阈值越大 - patch 越长 min_patch_len / max_patch_len: 长度约束 返回: patches: list[list[int]]每个 element 是一个 patch 内的字节 id patches [] current_patch [] current_entropy 0.0 for byte_id, ent in zip(byte_ids.tolist(), entropy.tolist()): current_patch.append(byte_id) current_entropy ent if current_entropy threshold and len(current_patch) min_patch_len: patches.append(current_patch) current_patch [] current_entropy 0.0 if len(current_patch) max_patch_len: patches.append(current_patch) current_patch [] current_entropy 0.0 if current_patch: patches.append(current_patch) return patches这个函数是动态切分的核心逻辑。你可以打印不同文本下 patch 的字节长度分布直观感受“空格和冗余文本处 patch 长生僻词和数学符号处 patch 短”。6.3 实现一个最小化的 BLT 训练循环下面的代码片段展示 MiniBLT 的模块骨架目的是跑通“字节编码 - 熵估计 - 动态 patch - 全局 Transformer - 字节预测”的数据流。import torch import torch.nn as nn class MiniBLT(nn.Module): def __init__(self, d_model256, num_heads4, num_layers4, vocab_size256): super().__init__() # 局部编码器先用一个小卷积处理字节序列 self.local_encoder nn.Sequential( nn.Embedding(vocab_size, d_model), nn.Conv1d(d_model, d_model, kernel_size3, padding1), nn.GELU(), ) # 熵估计器预测每个字节位置的熵 self.entropy_head nn.Linear(d_model, 1) # 全局潜在 Transformer处理 patch 序列 self.patch_proj nn.Linear(d_model, d_model) encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnum_heads, dim_feedforwardd_model * 4, batch_firstTrue, ) self.global_transformer nn.TransformerEncoder(encoder_layer, num_layersnum_layers) # 局部解码器把 patch 表示回到字节级 self.local_decoder nn.Linear(d_model, vocab_size) def forward(self, byte_seq, patch_indices): # byte_seq: [batch, seq_len] local_feat self.local_encoder(byte_seq) # [batch, seq_len, d_model] # 计算熵估计用于训练熵头 entropy_logit self.entropy_head(local_feat).squeeze(-1) # 按 patch_indices 池化得到 patch 表示 patch_feat [] for i, indices in enumerate(patch_indices): # indices: list of patch 内的字节下标 pooled torch.stack([local_feat[i, idx].mean(dim0) for idx in indices]) patch_feat.append(pooled) max_len max(p.shape[0] for p in patch_feat) padded torch.zeros(len(patch_feat), max_len, local_feat.size(-1)) mask torch.ones(len(patch_feat), max_len, dtypetorch.bool) for i, p in enumerate(patch_feat): padded[i, : p.size(0)] p mask[i, : p.size(0)] False global_feat self.global_transformer(self.patch_proj(padded), src_key_padding_maskmask) # 简化用全局特征最后一个位置的表示做字节预测 # 实际实现需要把 patch 表示展开回字节位置这里只展示训练数据流 logits self.local_decoder(global_feat) return logits, entropy_logit这个实现省去了很多细节但能帮助你理解数据流。真正的 BLT 实现里局部解码器需要把 patch 表示展开回字节位置再预测patch 的 batch 处理也会更复杂。建议第一次跑的时候只打印 patch 长度分布和 loss 曲线不要追求和论文一致的效果。6.4 观察与验证训练一段时间后做两件事验证动态切分是否生效打印几条文本的 patch 切分结果。观察空格、逗号、连续重复字符附近是不是容易形成较长的 patch而中英文生僻词、数字、代码符号附近是不是更容易切碎。和固定 tokenizer baseline 对比训练 loss。可以用一个 BPE tokenizer 训练同样参数量的 Transformer比较 perplexity 和训练吞吐。第一次实验不要求 BLT 优于 baseline能观察到“动态切分确实改变了序列长度分布”就已经达到验证目的。如果你准备长期跟进可以在此基础上加入长文本下游任务测试比如文档分类、摘要生成看 BLT 在长文档场景是否比 BPE 模型更稳定。7. 工程落地的挑战与我的判断BLT 在原理上很优雅但工程落地并不轻松。面试官如果喜欢刨根问底通常会问“你觉得它有什么缺点”。这里有几个真实的工程挑战值得展开。动态切分导致批量训练和推理的形状不固定。传统模型给一个 batch 的 token 长度直接 padding 即可BLT 则需要处理 ragged tensor 或动态 bucketing训练框架的 kernels 都要适配。KV Cache 管理更复杂。推理时不同请求的 patch 数量不同显存分配、prefill 阶段的计算调度都变得更动态。现有推理框架如 vLLM、TensorRT-LLM 都是围绕固定 token 序列设计的直接迁移存在成本。熵估计器带来额外计算延迟。虽然熵估计器本身很小但在线推理时它是一个额外的串行环节会影响首 token 延迟。与现有生态不兼容。微调、RLHF、量化、部署工具链都以 token id 为基本单位。BLT 要进入生产需要整个周边生态同步适配。不是所有场景都收益。短文本分类、固定格式日志、规则化数据用传统 tokenizer 稳定又高效BLT 反而因为动态切分引入不确定性。我的判断是BLT 更大的意义不在于“去掉分词器”这个口号而在于它展示了一种新的模型设计思路——让模型自己决定在哪里分配更多的计算。这个方向的影响会逐渐渗透到长上下文建模、多语言模型、端侧小模型等场景。短期内它不会全面取代 BPE但混合粒度的建模方式很可能会成为未来模型的一种基础能力。面试时如果你想升华结尾可以这样讲Tokenizer 是过去十年 NLP 里被默认接受的妥协BLT 的价值不是给出了另一个静态分词方案而是提出了“让模型根据信息密度动态决定计算分配”这个新范式。这句话能把你的回答从“知道一个新模型”提升到“理解一个技术趋势”。8. 总结与后续学习方向这篇文章从问题出发讲清了四层内容分词器为什么是瓶颈核心是“固定粒度与可变信息密度之间错配”。BLT 的核心创新是用字节熵估计动态切 patch而不是选一个更好的词表。架构上局部编码器处理字节细节全局 Transformer 处理 patch 语义熵估计器连接两者。面试作答时关键不是背概念而是能够说出动态切分在不同场景下的收益和代价。下一步你可以这样安排学习路径阅读 BLT 原论文时重点看 entropy estimator 和 patchification 两个模块其他部分可以理解为标准的 Transformer encoder-decoder 改造。用 MiniBLT 的思路在一个小型双语数据集上跑通“字节输入 - 动态 patch - 字节预测”的完整流程。对比阅读其他 tokenizer-free 工作比如基于 byte-level 的模型和基于字符级建模的模型把它们的差异归到“序列长度控制方式”这个维度。面试前把第 5 节的问题自己口头复述一遍尤其是“BLT 完全不需要分词器吗”这个容易被追问的问题。如果你正在面试或准备项目中用到 BLT可以把这篇文章收藏当作复习提纲。技术选型没有银弹但“信息密度决定计算分配”这个判断在未来几年里大概率会反复出现。
返回列表