【多模态学习】QA2:Tokenize和Embedding?BPE算法?交叉熵损失函数?
Q&A
- Tokenize和Embedding生成分别指代的是什么步骤?
- 1. Tokenize:输入序列预处理
- (1)文本分词(Tokenization)
- (2)Token到ID的映射
- (3)序列填充/截断(Padding/Truncation)
- 2. Embedding生成
- (1)Token Embedding(词嵌入)
- (2)位置编码(Positional Encoding)
- (3)Segment Embedding(可选)
- (4)Embedding相加与归一化
- (5)输出结果
- Tokenize中提到的BPE算法具体是什么?
- 1. 是什么?
- 2. 为什么需要?(解决的问题)
- 3. 算法步骤(训练阶段)
- 4. 编码(推理阶段)
- 5. 优势与劣势
- 6. 变体与相关算法
- 7. 总结
- LLM最常见的损失函数是交叉熵损失函数吗?具体是什么?
- 1. 它在计算什么?
- 2. 数学公式与计算
- 3. 为什么交叉熵是LLM的理想选择?
- 4. 在LLM训练中的具体应用:因果语言建模
- 5. 总结:Cross-Entropy in a Nutshell
Tokenize和Embedding生成分别指代的是什么步骤?
1. Tokenize:输入序列预处理
(1)文本分词(Tokenization)
- 目的:将原始文本拆分为模型可处理的离散单元(Token)。
- 方法:
- 使用预定义的词表(如BERT的WordPiece、GPT的Byte-Pair Encoding)进行分词。
- 示例:输入句子
"Hello, world!"
→ Tokens["Hello", ",", "world", "!"]
。
- 特殊Token添加:
- 添加任务相关的控制Token(如
[CLS]
、[SEP]
用于BERT的分类或句子分隔)。 - 可能包含起始符
<s>
、结束符</s>
等。
- 添加任务相关的控制Token(如
(2)Token到ID的映射
- 根据词表将Token转换为对应的整数ID。
- 示例:假设词表中
"Hello"=5
、","=3
,则映射为[5, 3, ...]
。
- 示例:假设词表中
(3)序列填充/截断(Padding/Truncation)
- 统一序列长度(如512):
- 短序列:填充
[PAD]
(通常ID=0)。 - 长序列:截断到最大长度。
- 短序列:填充
2. Embedding生成
(1)Token Embedding(词嵌入)
- 查找表(Lookup Table):
- 通过嵌入矩阵(Embedding Matrix)将每个Token ID转换为固定维度的向量。
- 矩阵维度:
[词表大小V, 隐藏维度d_model]
(如[50257, 768]
)。 - 示例:ID=5 → 查找矩阵第5行,得到向量
[0.2, -0.5, ..., 0.7]
(维度=d_model)。
(2)位置编码(Positional Encoding)
- 目的:为序列中的每个位置注入顺序信息。
- 方法:
- 绝对位置编码:使用正弦/余弦函数(原始Transformer)或可学习的位置嵌入(如BERT)。
- 公式(正弦版本):
PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel)PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}}) \\ PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}}) PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel) - 相对位置编码:某些模型(如T5)使用更复杂的位置表示。
(3)Segment Embedding(可选)
- 用于区分多个输入片段(如句子对任务)。
- 示例:BERT中
[SEP]
分隔的句子A/B分别用EmbeddingE_A
和E_B
标记。
- 示例:BERT中
(4)Embedding相加与归一化
- 相加:将Token Embedding、Positional Encoding、Segment Embedding逐元素相加。
- 公式:
Final_Embedding = Token_Embed + Position_Embed + Segment_Embed
- 公式:
- 层归一化(LayerNorm):某些模型(如BERT)在Embedding后应用归一化。
(5)输出结果
- 最终得到形状为
[序列长度, d_model]
的Embedding矩阵,可直接输入到Transformer的编码器层。- 示例:输入序列长度=10,
d_model=768
→ 输出矩阵形状[10, 768]
。
- 示例:输入序列长度=10,
Tokenize中提到的BPE算法具体是什么?
BPE算法是一种非常经典且广泛使用的子词分割(Subword Tokenization)算法,几乎是当前所有大型语言模型(如GPT、BERT、LLaMA)的标配预处理步骤。
1. 是什么?
- 全称:Byte Pair Encoding(字节对编码)
- 核心思想:迭代地合并最频繁出现的相邻字节对(或字符对),从而将单词分解为更常见的、可管理的子词单元。
- 目标:在“字符级”和“单词级”之间找到一个平衡点,解决自然语言处理中的未登录词(OOV, Out-Of-Vocabulary) 问题。
2. 为什么需要?(解决的问题)
- 词表过大:如果使用单词级词表,模型需要处理数十万甚至上百万个单词,其中很多是长尾词(出现次数极少),这会导致计算和存储的低效。
- 未登录词(OOV)问题:在测试阶段遇到训练时没见过的单词时,模型无法处理。例如,训练时见过“looked”和“looking”,但没见过“looker”,单词级模型就会失败。
- 数据稀疏性:罕见词由于缺乏足够的上下文,难以学习到好的表示。
BPE的解决方案:通过将未知的、复杂的单词分解为已知的、简单的子词(如词根、前缀、后缀),模型就能理解和生成它们。
3. 算法步骤(训练阶段)
BPE训练的目的是从一个初始词表构建一个最终的子词词表。其过程是一个贪婪的迭代合并过程:
-
初始化:
- 将文本中所有单词拆分为字符(bytes) 序列,并在每个单词末尾添加一个特殊的结束符(如
</w>
)以标记单词边界。 - 初始词表就是所有字符加上结束符。
- 统计所有单词中每个字符对的频率。
示例:假设语料库很小:“low lower newest widest”*
- 初始状态(添加
</w>
):
{'l o w </w>': 2, 'n e w e s t </w>': 1, 'w i d e s t </w>': 1}
- 初始词表:
{'l', 'o', 'w', '</w>', 'n', 'e', 's', 't', 'i', 'd'}
- 将文本中所有单词拆分为字符(bytes) 序列,并在每个单词末尾添加一个特殊的结束符(如
-
迭代合并:
- 找出频率最高的相邻字节对(bigram)。
- 将这个字节对合并成一个新的“符号”(symbol),并加入到词表中。
- 更新语料库中所有出现该字节对的地方,用新的合并符号替换它们。
- 重复此过程,直到达到预定的合并次数(即最终词表大小)。
接上例:
- 第1轮:最高频对是
e
和s
(在"newest"和"widest"中各出现1次,但注意"low"和"lower"中没有)。合并e s
->es
。更新语料:
{'l o w </w>': 2, 'n e w es t </w>': 1, 'w i d es t </w>': 1}
词表新增:es
- 第2轮:最高频对是
es
和t
(出现了2次)。合并es t
->est
。更新语料:
{'l o w </w>': 2, 'n e w est </w>': 1, 'w i d est </w>': 1}
词表新增:est
- 第3轮:最高频对是
l
和o
(在"low"和"lower"中出现了2次)。合并l o
->lo
。更新语料:
{'lo w </w>': 2, 'n e w est </w>': 1, 'w i d est </w>': 1}
词表新增:lo
- …(继续合并,比如下一步可能合并
lo
和w
->low
)
-
停止:当达到了预设的合并操作次数(例如,10,000次)后停止。最终得到一个包含所有字符和所有合并后的子词的词表。
4. 编码(推理阶段)
如何用训练好的BPE词表对一个新单词进行分词?
- 将新单词拆分为字符序列,并加上结束符
</w>
。 - 从左到右,贪婪地寻找词表中存在的最长可能子词。
- 不断迭代,直到整个单词被分解为词表中的子词。
示例:假设我们的最终词表包含了 low
, er
, </w>
, est
, new
等。
- 对未知词
lower
进行编码:- 初始:
l o w e r </w>
- 最长匹配:词表中有
low
,所以匹配 ->low e r </w>
- 下一步:
e
不在词表中,但er
在 ->low er </w>
- 结果:
["low", "er"]
- 初始:
5. 优势与劣势
优势 | 劣势 |
---|---|
1. 有效解决OOV问题:通过子词组合,几乎可以表示任何新词。 | 1. 编码不唯一:一个单词有时可能有多种分割方式(尽管贪婪算法通常能保证唯一性)。 |
2. 极大的压缩词表:用较小的子词词表(典型值3万-10万)即可表示极大的词汇空间。 | 2. 可能产生反直觉分割:例如,“asked”可能被分割为 ask 和 ed ,这虽然语义上合理,但破坏了原始单词形态。 |
3. 更好的泛化能力:共享子词表示(如“##ing”)有助于模型学习词形变化和构词法。 | 3. 增加序列长度:一个单词可能被分成多个token,增加了模型需要处理的序列长度。 |
4. 多语言友好:不依赖空格,对德语(复合词)、中文(字符)、阿拉伯语(黏着语)等都有效。 | 4. 解码麻烦:生成文本后,需要将子词拼接回完整的单词。 |
6. 变体与相关算法
- WordPiece:被BERT采用。与BPE类似,但合并策略不同:BPE合并最高频的对,而WordPiece合并能最大程度提升语言模型概率的对。
- SentencePiece:将文本直接视为原始字节流,无需预处理(如空格分词),尤其适合处理多种语言混合且没有空格的文本(如中文、日文)。
- Unigram LM:与BPE方向相反,从一个大的种子词表开始,逐步移除最不重要的子词,直到达到目标词表大小。
7. 总结
BPE是一种巧妙的数据压缩技术在NLP中的应用。它通过统计和迭代合并,找到了词汇表示的最佳平衡点,使其成为支撑现代大语言模型处理海量词汇的基石技术。理解了BPE,就理解了为什么GPT等模型能如此流畅地生成和理解它们从未“见过”的单词。
LLM最常见的损失函数是交叉熵损失函数吗?具体是什么?
是的,交叉熵(Cross-Entropy) 是大型语言模型(LLM)训练中最核心、最常用的损失函数。它直接衡量的是模型预测的概率分布与真实的概率分布(即目标答案)之间的“距离”。
1. 它在计算什么?
想象一下LLM的核心任务:预测下一个词。
- 输入:“The capital of France is”
- 期望输出:“Paris”
- 模型输出:一个在整个词表上的概率分布。例如,它可能给"Paris"分配了0.85的概率,给"Lyon"分配了0.1的概率,给"Rome"分配了0.02的概率,等等。
交叉熵损失的目标是:惩罚模型为错误词分配的高概率和为目标词分配的低概率。
- 如果模型对"Paris"的预测概率是 1.0,损失为 0(完美)。
- 如果模型对"Paris"的预测概率是 0.1,而给"Rome"分配了0.9,损失会 非常大。
核心思想:损失值越高,说明模型的预测分布与真实分布相差越远。
2. 数学公式与计算
对于一个具体的例子(一个token),交叉熵损失的公式为:
L=−∑i=1Vyilog(pi)L = -\sum_{i=1}^{V} y_i \log(p_i)L=−i=1∑Vyilog(pi)
其中:
- VVV 是词表的大小。
- yiy_iyi 是真实标签的 one-hot 编码。只有目标词对应的位置是1,其他全是0。
- 例如,如果目标词是"Paris"(假设它在词表中的索引是第12345位),那么 y12345=1y_{12345} = 1y12345=1,其余 yi=0y_i = 0yi=0。
- pip_ipi 是模型预测的整个词表上第 iii 个词的概率。
由于真实分布 yyy 是one-hot形式,这个求和公式实际上简化为了只计算目标词那一项的负对数概率:
L=−log(ptarget)L = -\log(p_{\text{target}})L=−log(ptarget)
计算示例:
- 如果模型预测目标词"Paris"的概率 ptarget=0.85p_{\text{target}} = 0.85ptarget=0.85,则损失 L=−log(0.85)≈0.16L = -\log(0.85) \approx 0.16L=−log(0.85)≈0.16。
- 如果模型预测的概率很低,比如 ptarget=0.1p_{\text{target}} = 0.1ptarget=0.1,则损失 L=−log(0.1)≈2.3L = -\log(0.1) \approx 2.3L=−log(0.1)≈2.3。
在实际训练中,我们计算一个批次(batch)中所有序列的所有token的损失,然后求平均,称为 平均交叉熵损失。
3. 为什么交叉熵是LLM的理想选择?
-
与最大似然估计(MLE)完美契合:
LLM的训练本质是最大似然估计——寻找能使训练数据出现的概率最大的模型参数。最大化似然概率等价于最小化负对数似然损失,而交叉熵损失正是负对数似然损失在分类问题中的形式。 -
梯度行为友好:
- 交叉熵损失的梯度计算非常干净和高效。对于目标类别,梯度为 (pi−1)(p_i - 1)(pi−1),对于非目标类别,梯度为 pip_ipi。
- 这意味着当模型预测错误时(ptargetp_{\text{target}}ptarget 很小),梯度幅度会很大,从而推动模型参数进行快速修正。当预测接近正确时,梯度变小,更新放缓,训练趋于稳定。
-
衡量分布差异:
交叉熵源于信息论,它衡量的是用模型分布 PPP 来编码真实分布 QQQ 所需的额外比特数。我们希望通过训练,这个“额外开销”降为0,即两个分布完全一致。 -
实践有效性:
在大量的实践中,使用交叉熵损失训练的模型收敛稳定,并能产生出色的结果,这使其成为了一个可靠的标准。
4. 在LLM训练中的具体应用:因果语言建模
在GPT等自回归模型中,训练过程如下:
- 输入一个序列:
"The capital of France is Paris"
。 - 模型的任务是预测下一个token。它的输入和标签分别是:
- 输入 (Input):
"The capital of France is"
- 标签 (Target):
"capital of France is Paris"
(即输入向右偏移一位)
- 输入 (Input):
- 模型会为序列中的每一个位置(除了第一个)计算一个预测分布。
- 计算每个位置预测的交叉熵损失(将“前一个词”的预测与“当前词”的标签对比),然后对所有位置的损失求平均。
这个过程确保了模型学会根据上文来预测下一个词。
5. 总结:Cross-Entropy in a Nutshell
方面 | 解释 |
---|---|
是什么 | 衡量模型预测概率分布与真实分布之间差异的函数。 |
在LLM中的作用 | 作为训练阶段的损失函数,指导模型参数更新的方向。 |
核心计算 | L=−log(ptarget)L = -\log(p_{\text{target}})L=−log(ptarget),即目标词预测概率的负对数。 |
为何有效 | 与最大似然估计等价,梯度性质优秀,能有效推动模型学习正确的预测。 |
别名 | 因其普遍性,常直接被称为语言建模损失(Language Modeling Loss)。 |
可以说,没有交叉熵损失,就没有现代LLM的成功。它是连接模型输出与学习目标的桥梁,是驱动LLM从海量文本中学习知识的引擎。