Datawhale 组队学习 fun-transformer😁Task 4 Decoder
Datawhale 组队学习 fun-transformer
解码器
Datawhale项目链接:https://www.datawhale.cn/learn/summary/87
笔记作者:博客园-岁月月宝贝💫
微信名:有你在就不需要🔮给的勇气
Task 04 Decoder
在讲下面的知识前,我们先来看一个解码器的预测过程~❤
1️⃣初始步骤:
输入准备:在这个例子中,编码器处理的是一篇文章的全文,而解码器的初始输入同样包括编码器的编码结果和一个开始符号<bos>
。
2️⃣生成序列:
- 预测第一个单词:解码器基于编码器的输出和开始符号来预测摘要的第一个单词。例如,这可能是摘要中的第一个词,如"Summary"。
- 迭代生成:
- 第二步,输入"
<bos>
Summary",解码器可能会预测下一个单词"of"。 - 第三步,输入"
<bos>
Summary of",解码器接着预测"the"。
- 第二步,输入"
- 逐步构建:在每个步骤中,解码器的输出通过线性层调整维度,并通过softmax函数转换为概率分布,选择概率最高的单词作为预测结果。
3️⃣结束条件:
序列终止:生成过程持续进行,直到解码器预测出序列结束的标记<eos>
。
4️⃣完整示例:
假设我们正在生成文章的摘要:
第一步,输入"<bos>
",输出"Summary"。
第二步,输入"<bos>
Summary",输出"of"。
第三步,输入"<bos>
Summary of",输出"the"。
第四步,输入"<bos>
Summary of the",输出"key"。
第五步,输入"<bos>
Summary of the key",输出"points"。
最后,输入"<bos>
Summary of the key points",输出"<eos>
",表示摘要生成完毕。
😊到这里,大家其实已经看到了Decoder这节最核心的部分,接着请大家继续阅读,解开解码机制的奥秘吧!
1. Decoder 的设计目标
解码器的主要任务是根据编码器的输出和已生成的序列信息,逐步生成目标序列。为此,解码器引入了右向偏移、位置编码、掩码多头自注意力机制、编码器-解码器注意力交互以及基于位置的前馈网络等关键机制。
2. 解码时的流程
解码器的工作流程如下:
- 初始化输入
假设当前已被解码的序列为 $s_1, s_2, \ldots, s_{t-1} $,将该序列进行词向量嵌入(也叫输出嵌入)和位置向量嵌入(也叫位置编码)。位置编码用于保留符号的序列顺序信息。 - Masked Multi-Head Attention
对嵌入向量进行掩码多头自注意力操作。掩码技术确保模型在生成当前符号时,只能参考之前生成的符号(专注于输入序列的相关片段以及之前产生的符号),避免“看到”未来的符号。 - 编码器-解码器注意力交互
将编码器的输出作为键向量 $K $ 和值向量 $ V $,结合上一步得到的查询向量 $ Q $,进行编码器-解码器注意力交互。这一步使解码器能够聚焦于输入序列的重要部分,进而生成受输入语境影响的输出符号。 - 前馈网络
将上一步的输出传递到基于位置的前馈网络,进一步捕捉输入和已生成符号之间的复杂模式与关联。 - 重复子结构
重复上述子结构,最终得到输出,并通过概率分布解码得到下一个输出词。
🌟在开始处理输入序列之前,模型对输出嵌入进行向右偏移一个位置 。它的作用是确保解码器在生成当前符号时,只能使用之前生成的符号作为上下文,而不会“看到”未来的符号。它是通过输入数据的预处理和掩码机制间接实现的。具体表现:
- 输入序列的预处理:在目标序列的开头添加开始标记
<sos>
,并将整个序列向右偏移一个位置。- 掩码机制:在掩码多头自注意力中,使用掩码矩阵隐藏未来时间步的信息。
这两者共同确保了解码器在生成当前符号时,只能使用之前生成的符号作为上下文,从而模拟自然语言生成的顺序性。
3. Decoder 的结构特点
与编码器相比,解码器增加了一个名为“Mask”的多头注意力子层。Mask操作隐藏未来的序列元素,确保解码过程的一致性和正确性。这一操作的必要性源于解码并非一次性完成,而是按时间步逐个生成输出。
解码器的结构
解码器与编码器相同, 采用了6层堆叠的结构,每层解码器由三个子结构组成。与编码器相比,解码器主要有以下三个不同点:
- 子结构-1:带掩码的多头自注意力机制
解码器的第一个子结构实现了带掩码的多头自注意力机制。这种掩码机制的作用是防止模型在预测当前符号时“窥视”到未来的数据,从而避免信息泄露。具体来说,掩码会隐藏当前时间步之后的信息,确保解码器只能利用之前生成的符号作为上下文。 - 子结构-2:编码器-解码器多头注意力机制
解码器的第二个子结构是一个编码器-解码器多头注意力机制。这一机制的作用是将编码器的输出(即输入序列的上下文表示)与解码器的中间表示相结合。通过这种方式,解码器能够利用编码器提取的上下文信息,更好地生成目标序列。 - 子结构-3:前馈网络与输出层
解码器的第三个子结构是一个前馈网络,用于进一步处理和转换解码器的中间表示。与编码器类似,这个前馈网络是逐位置独立操作的。
在前馈网络之后,解码器的输出会经过一个线性层,然后通过softmax层,最终生成目标词汇的概率分布。这一过程用于预测下一个最可能的词汇。
解码器的多头注意力
🔐 解码器的多头自注意力作用于目标序列(已生成的部分,其实还包含原序列,所以说成已存在的部分更好),以生成输出序列的下一个词。为了确保按照实际操作流程有序展开, 需要维持生成过程的因果性, 解码器的自注意力加入了掩码机制, 避免泄露未来信息。
📦编码器的多头自注意力作用于输入序列。编码器的任务是提取输入序列的全局特征(全局依赖建模),不受因果顺序的限制。这种机制允许每个词自由地关注序列中的任何位置,因此不需要掩码机制。
解码器的掩码机制
🔐解码器:解码器的自注意力采用因果掩码机制,确保在生成输出序列时,当前词只能与之前的词进行交互,通过将未来词的注意力值设为负无穷大来实现。
📦编码器:编码器没有掩码机制,可以自由地处理和理解(任何时候访问任何信息in)整个输入序列。
解码器的交叉注意力层
🔐解码器:解码器的多头自注意力除了包含自注意力层外,还额外包含一个交叉注意力层,允许解码器访问编码器编码的输入序列的隐藏表示,以结合输入信息生成输出序列。
📦编码器:编码器的多头自注意力仅依赖于输入序列的信息,不涉及其他模块。
解码器的训练与推理过程
🔐解码器:在训练阶段,解码器可以并行处理整个目标序列(因为目标序列已经存在,作为参考);而在推理阶段,目标序列是逐步生成的,解码器必须依次(逐步)计算每个新词的注意力。
📦编码器:在训练和推理阶段,编码器的自注意力处理方式相同,因为它总是可以访问整个输入序列(不管训练还是推理,输入序列必须提供, 对吧)。
❓❓❓Decoder预测产生的词不是我们想要的:
模型没有收敛得很好时,Decoder预测产生的词很可能不是我们想要的。这个时候如果再把已经生成的目标序列的错误数据输给Decoder,就会越跑越偏。这个时候怎么办?
(1)在训练过程中使用 “teacher forcing”。
因为我们知道应该预测的word是什么,那么可以给Decoder喂一个正确的结果作为输入。
(2)优化词选择算法:
- 贪婪搜索(Greedy Search):在每一步选择概率最高的词作为下一个词。这种方法简单但容易陷入局部最优,一旦某一步选择错误,后续步骤很难纠正。
- Beam Search:在每一步保留多个(Top-K)高概率的候选词,而不是只选择一个。这些候选词会形成多条路径,每条路径代表一种可能的生成序列。
💐Beam Search的路径概率计算
在 Beam Search 中,每条路径的概率是通过每一步输出的概率的乘积来计算的。具体解释如下:
(1) 路径的概率
假设我们正在生成一个句子,每一步有多个候选词,每条路径代表一种可能的生成序列。路径的概率是通过以下方式计算的:
在第 \(t\) 步,假设我们选择了词 \(w_t\),其概率为 \(P(w_t | w_1, w_2, \ldots, w_{t-1})\)。
在第 \(t+1\) 步,假设我们选择了词 \(w_{t+1}\),其概率为 \(P(w_{t+1} | w_1, w_2, \ldots, w_t)\)。
那么,这条路径的总概率就是每一步概率的乘积:
\[P(\text{路径}) = P(w_1) \times P(w_2 | w_1) \times \cdots \times P(w_t | w_1, w_2, \ldots, w_{t-1}) \](2) 路径概率的意义
- 路径概率:路径概率反映了这条路径在整个生成过程中的合理性。路径概率越高,说明这条路径越可能是正确的生成序列。
- 选择路径:在 Beam Search 中,我们保留多条路径(Top-K),并在每一步扩展这些路径。最终选择路径概率最高的那条路径作为最终的生成结果。
*训练阶段可能涉及学习率调度策略,如热身计划或衰减计划。
解码器的输出处理
在解码器的最终阶段,其输出将通过一个线性层(Linear)进行转换,该层的作用是将解码器的输出映射到与词汇表大小相匹配的维度。随后,应用softmax函数将这些数值转换成概率分布,其中每个词汇对应一个概率值。在这一过程中,概率最高的词汇被选为预测的下一个词。
❓❓❓怎么评估预测准不准:
在训练Transformer模型的过程中,我们使用损失函数(例如交叉熵损失)来评估生成的输出概率分布与目标序列之间的差异。损失函数通过比较这两个概率分布,为每个单词在特定位置上的出现概率打分。
概率分布的比较
- 目标概率分布(Target Probabilities):表示目标序列中每个单词在对应位置上的理想概率。例如,如果目标序列是“De nada END”,则在第一个位置上“De”的概率应为1,其他单词的概率为0;在第二个位置上“nada”的概率应为1,以此类推。
- 输出概率分布(Output Probabilities):表示模型生成的序列中每个单词在对应位置上的预测概率。模型的目标是使这个分布尽可能接近目标概率分布。
![]()
上图展示了目标概率分布和输出概率分布在三个位置上的对比:
- 第一个位置(1st Pos):目标是“De”,因此“De”的概率应为1,其他单词(如“Bueno”和“Nada”)的概率为0。
- 第二个位置(2nd Pos):目标是“nada”,因此“nada”的概率应为1,其他单词的概率为0。
- 第三个位置(3rd Pos):目标是“END”,因此“END”的概率应为1,其他单词的概率为0。
损失函数的作用
损失函数计算输出概率分布与目标概率分布之间的差异,并生成梯度。这些梯度通过反向传播算法用于更新模型的权重[梯度下降],从而优化模型的性能(减少此差异)[优化器]。
- 交叉熵损失:常用的损失函数,用于衡量两个概率分布之间的差异。在这种情况下,它衡量的是模型输出的概率分布与目标概率分布之间的差异。
4.Decoder在不同阶段的信息传递机制
预测阶段
在预测阶段,解码器采用自回归的方式生成序列,一次生成一个标记,然后将该标记作为新输入传递回模型。这个阶段的目的是利用模型生成文本或翻译等任务的输出。预测阶段的信息传递机制如下:
-
穷举法:
对所有候选词预测概率,然后使用所有的排列组合找出输出条件概率最大的序列,作为decoder的输出,可以保证全局最优,但是计算复杂度非常高。 -
贪心搜索:
对所有候选词预测概率,选概率最大的词作为当前单元的输出,这种方法虽然计算简单,但满足局部最优策略期望未必产生全局最优解。 -
束搜索(Beam search):
在贪心搜索的基础上,每轮保留前
beam size
个最可能的词作为候选,这样可以在一定程度上找到更好的全局最优解。
训练阶段
在训练阶段,解码器的目标是学习如何根据输入序列生成正确的输出序列。这个阶段的信息传递机制如下:
- 自由运行(Free Running)模式:解码器使用上一个时间单元的预测值作为下一个时间单元的输入(如果模型在某一步预测错误,那么这个错误会随着时间单元的推进而累积🐂)。这种方法可能导致错误累积,影响模型的收敛。
- 教师强制(Teacher Forcing)模式:在训练时使用真实的标签作为输入,而不是模型的预测值(即使模型在前一个时间单元预测错误,它仍然可以在下一个时间单元接收到正确的信息😄)。这种方法有助于模型更容易地学习和收敛,但可能在训练和预测阶段存在不一致性。
- 计划采样(Scheduled Sampling):结合自由运行和教师强制的优点,在训练过程中动态地结合真实标签和模型的预测。
可见计划采样结合了这两种方法的优点, 这种方法的核心思想是在训练过程中动态地结合真实标签和模型的预测。
计划采样具体做法:
是设定一个概率值p:
以p
的概率,使用上一个时间单元的预测值作为输入;以1-p
的概率,使用真实的标签作为输入。
这样,解码器在训练时既能得到真实标签的适当指导,又能逐渐学会依赖自己的预测,从而提高了模型在预测阶段的稳健性和一致性。
难点突破
📏模型预测出的句子长短不一
前面有讲每个单元预测出来的每个词会和真实 label 比较计算 loss 更新预测效果,但是模型预测出来的整个句子是长短不一的,我们如何判断翻译出来的整句话的效果好坏呢?
当然可以,以下是对BLEU评分系统评估翻译质量的严谨解释:
小故事讲解
设想我们有一份标准的食谱(参考翻译),以及多位厨师(模型)根据这份食谱制作出的菜肴(生成的翻译句子)。BLEU评分系统通过以下方式来评估这些翻译的质量:
- 精确度(Precision):
- BLEU评分首先计算预测句子中不同长度的n-gram(连续的n个词的组合)与参考句子中n-gram的匹配程度。
- 精确度是指预测句子中正确匹配的n-gram数量与预测句子中总n-gram数量的比例。
- 召回率(Recall):
- 召回率是指预测句子中正确匹配的n-gram数量与参考句子中总n-gram数量的比例。
- 这一指标衡量的是预测句子能够覆盖参考句子中多少信息。
- 短句惩罚(Brevity Penalty):
- 如果预测的句子比参考句子短很多,BLEU会施加惩罚,因为过短的翻译可能丢失重要信息。
- 短句惩罚通过比较预测句子和参考句子的长度来计算,如果预测句子过短,其BLEU分数会相应降低。
- 最终评分:
- BLEU分数是精确度和召回率的调和平均值,同时考虑了短句惩罚。
- BLEU分数越高,表示预测句子与参考句子在内容和结构上越接近,翻译质量通常认为越好。
通过这种评估机制,BLEU评分系统确保了评分的公正性,即对于短句子(信息不完整)进行惩罚,而对于长句子(信息完整且匹配度高)则给予更高的权重。这类似于评估厨师制作的菜品,如果菜品的配料和步骤与标准食谱高度匹配,那么评分会更高,表明厨师的技艺更加精湛。
指标介绍
BLEU分数:
- BLEU(双语评估研究)分数通常用于评估机器翻译文本的质量。
- 它将生成的翻译与一个或多个由人类翻译人员提供的参考翻译进行比较。
- BLEU分数范围从0到1,较高的分数表示更好的翻译质量。
困惑度(perplexity):
- 困惑度是用于评估语言模型性能的常见指标,包括Transformer。
- 它衡量模型对给定标记序列的预测能力。
- 较低的困惑度值表示更好的性能,理想值接近词汇量大小。
🙎Teacher forcing
什么是“教师强制”?
想象一下,你是一个学生,正在参加一个多步骤的考试。每一题的答案都依赖于前一题的答案。比如,第一题是数学计算,第二题需要用第一题的结果来解答。
- 在现实生活中:老师会在你完成所有题目后,一次性告诉你哪些题目答对了,哪些答错了。这样,你只能在看到最终答案后,才知道自己哪些步骤做错了。
- 在“教师强制”模式下:老师会在你完成每一题后,立即告诉你正确答案,即使那一题你答错了。这样,你就可以在解答下一题时,使用正确的信息,而不是你之前的错误答案。
RNN中的“教师强制”
在RNN的训练中,“教师强制”是一种策略,用来提高模型的学习效率:
- Free-running 模式:
- 这是RNN的常规训练方式。在这种模式下,模型在每个时间步使用自己前一时间步的预测结果作为下一时间步的输入。这就像是你完全依靠自己的记忆和计算来解答每一题,没有外部帮助。
- Teacher-forcing 模式:
- 在这种模式下,RNN在每个时间步使用真实的标签(即正确的答案, ground truth)作为下一时间步的输入,而不是使用模型自己的预测结果。这就像是老师在你完成每一题后立即告诉你正确答案,让你在解答下一题时使用正确的信息。
为什么使用“教师强制”?
- 提高学习效率:通过在每个时间步提供正确的答案,模型可以更快地学习到正确的序列依赖关系,因为模型总是基于正确的信息进行学习。
- 避免错误累积:在free-running模式下,如果模型在早期时间步犯了错误,这个错误可能会影响后续所有时间步的预测(eg.误差累积,梯度消失或爆炸)。而在teacher-forcing模式下,即使模型在某个时间步犯了错误,它仍然可以在下一个时间步使用正确的信息。
“教师强制”是一种在训练阶段帮助模型更有效学习的技术,通过在每个时间步提供正确的答案,帮助模型更快地收敛到正确的解。
训练迭代过程早期的RNN预测能力非常弱,几乎不能给出好的生成结果。如果某一个unit产生了错误结果,必然会影响后面一片unit的学习。
错误结果会导致后续的学习都受到不好的影响,导致学习速度变慢,难以收敛。teacher forcing最初的动机就是解决这个问题的。
5.高级主题和应用
BERT(来自 Transformers 的双向编码器表示)
BERT(Bidirectional Encoder Representations from Transformers)是一个基于Transformer的模型,它在自然语言处理(NLP)领域产生了深远影响。BERT通过掩码语言建模(Masked Language Model, MLM)和下一句预测(Next Sentence Prediction, NSP)等目标,在大规模文本语料库上进行预训练。BERT学习了单词的深层上下文表示,捕捉双向上下文,使其在广泛的下游NLP任务中表现良好🎉。
以下是使用BERT的一个简单示例:
from transformers import BertModel, BertTokenizer
# 初始化分词器(Tokenizer),用于将文本转换为模型可以理解的格式
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# 初始化BERT模型
model = BertModel.from_pretrained('bert-base-uncased')
# 对输入文本进行编码,包括转换为token IDs、注意力掩码和token类型ID(对于BERT不重要)
inputs = tokenizer("Hello, world!", return_tensors="pt")
# 将编码后的输入传递给模型,获取输出
outputs = model(**inputs)
# 输出模型的最后隐藏层状态
print(outputs.last_hidden_state)
输出:
模型输出解释
- 最后一个隐藏状态 (
last_hidden_state
):- 这是模型处理完输入序列后,最后一个Transformer层的输出。
- 它捕获含了输入数据的全部信息,并通常用于后续的任务,比如分类或文本生成。
- 池化输出 (
pooler_output
):- 这是经过一个线性层和tanh激活函数处理后的输出,专门用于分类任务。
- 它从所有隐藏状态中提取出用于分类的特征。
- 隐藏状态列表 (
hidden_states
):- 包含模型中每个Transformer层的输出隐藏状态。
- 这些状态对于理解模型内部的工作机制非常有用,可以用于进一步分析或可视化。
- 先前的键值 (
past_key_values
):- 在序列到序列的任务中使用,存储先前序列的注意力权重和隐藏状态。
- 这些信息在生成下一个序列时再次使用,帮助模型维持序列间的依赖关系。
- 注意力权重 (
attentions
):- 包含每个Transformer层的注意力权重矩阵。
- 这些权重展示了模型在处理输入时,如何关注输入序列的不同部分,常用于可视化分析。
- 交叉注意力 (
cross_attentions
):- 用于序列到序列任务中,存储不同序列间的注意力权重。
- 这有助于模型在处理多序列输入时,理解它们之间的关系。
GPT(生成式预训练 Transformer)
GPT(生成式预训练Transformer)是一个基于Transformer的模型,以其生成能力而闻名。与双向的BERT不同,GPT采用仅解码器的架构和自回归训练来生成连贯且上下文相关的文本。研究人员和开发人员已经成功地将GPT应用于各种任务,如文本完成、摘要、对话生成等。
以下是使用GPT的一个简单示例:
from transformers import GPT2LMHeadModel, GPT2Tokenizer
# 初始化GPT-2的分词器,用于将文本转换为模型可以理解的格式
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
# 初始化GPT-2模型,带有语言模型头(LM Head),用于生成文本
model = GPT2LMHeadModel.from_pretrained('gpt2')
# 输入文本,模型将基于此生成后续文本
input_text = "Once upon a time, "
# 对输入文本进行编码
inputs = tokenizer(input_text, return_tensors='pt')
# 使用模型生成文本,max_new_tokens指定生成的最大新token数
output_sequences = model.generate(**inputs, max_new_tokens=100)
# 解码生成的token序列,跳过特殊token
output = tokenizer.decode(output_sequences[0], skip_special_tokens=True)
# 打印生成的文本
print(output)
输出:
Once upon a time, I was a little bit of a fan of the original series, but I was also a little bit of a fan of the original series. I was a little bit of a fan of the original series, but I was also a little bit of a fan of the original series. I was a little bit of a fan of the original series, but I was also a little bit of a fan of the original series. I was a little bit of a fan of the original series, but I......
在这两个示例中,我们首先导入了必要的库和模型,然后初始化了分词器和模型。接着,我们将输入文本编码为模型可以理解的格式,并使用模型进行预测或生成文本。最后,我们打印出模型的输出或生成的文本。😎
分词
1.分词的原因
分词(Tokenization)是自然语言处理(NLP)中的一个关键步骤,它将文本切分成更小的单元,以便计算机能够理解和处理。以下是分词的几个主要原因:
1️⃣将复杂问题转化为数学问题
通过将非结构化文本数据转化为结构化数据,我们可以将文本转化为计算机可以处理的数字信息。这一过程通常涉及以下几个步骤:
- 统计计算:计算每个标识符(tokens)在文件中出现的频次,形成一个频次向量组来表示文件。
- 特征输入:将这些频次向量作为特征输入到机器学习或深度学习模型中进行训练。
2️⃣词的粒度比较合适
(1)“词”是表达完整含义的最小单位。 “字”的粒度太小,无法表达完整含义。
词粒度Tokenized:
#我喜欢喝咖啡。
#词粒度Tokenized结果:
['我', '喜欢', '喝', '咖啡', '。']
#I like coffee
#词粒度Tokenized结果:
['I', 'like', 'coffee', '.']
字符粒度Tokenized:
#Hello, world!
#字符粒度Tokenized结果:
['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']
eg.单独的字符(如“牛”)可能具有多种含义(如“牛市”或“牛人”),只有将其放入特定的词汇或句子中,我们才能理解其完整含义。
(2)subword
粒度Tokenization
介于词粒度和字符粒度之间
Subword粒度Tokenization是一种介于词粒度(整词切分)和字符粒度(单个字符切分)之间的文本切分方法。
主要目的:
- 处理未登录词(OOV,Out-Of-Vocabulary)问题:未登录词是指那些在训练数据中没有出现过的词。Subword粒度Tokenization通过将词切分成更小的单元(如词根、前缀、后缀)来处理这些词。
- 保持语义完整性:通过使用词的子串,Subword粒度Tokenization能够在一定程度上保留词的语义信息。
常见的subword Tokenization方法: Byte Pair Encoding (BPE)、WordPiece等。
这些方法通过统计文本数据中的子串频率,自动生成一种分词词典。
子词粒度Tokenized:
#helloworld
#经过BPE算法训练后,生成的子词词典包含以下条目:
h, e, l, o, w, r, d, hel, low, wor, orld
#子词粒度Tokenized结果:
['hel', 'low', 'orld']
(3)“句子”的粒度太大,承载的信息量多,很难复用
比如“牛人在吃牛排”,这个例子就很容易让人或机器产生疑惑。
深度学习时代,部分任务也可以分“字”。
------《Is Word Segmentation Necessary for Deep Learning of Chinese Representations?》
不过在如关键词提取、命名实体识别等的特定任务中,分词还是必要的。
2.中英文分词的3个典型区别
区别 | 中文分词 | 英文分词 |
---|---|---|
分词方式 | 无明显分隔符,需复杂算法和语言知识 | 有空格分隔,分词简单 |
形态变化处理 | 无形态变化,无需处理 | 有时态、复数等变化,需词性还原和词干提取 |
粒度问题 | 需考虑粒度,平衡准确性与召回率 | 通常不需考虑粒度问题 |
3.中文分词的3大难点
难点 | 描述 | 示例 |
---|---|---|
难点 1:没有统一的标准 | 中文分词缺乏普遍认可的标准或规范,使得系统开发和评估复杂,跨系统、跨领域的文本处理困难。 | 无 |
难点 2:歧义词如何切分 | 中文中存在大量歧义词,同一字符串可能有多种分词方式,表达不同含义。 | “乒乓球拍卖完了”可以分词为“乒乓球 拍卖 完了”或“乒乓 球拍 卖 完了” |
难点 3:新词的识别 | 新词更新速度快,分词系统需定期更新词库以包含新词。快速识别新词是一大难点。 | “雨女无瓜”需要在被广泛使用时迅速识别和正确处理 |
4.典型的分词方式
4.1 简单单字分词方式
方法简介: 在一些课程示例中,中文分词的一种基础方法是将每个字符视为一个独立的token。这种方法简单直接,易于实现。
原理
- 处理方式:将每个汉字看作一个独立的语义单元。
- 示例:对于句子“我喜欢猫猫”,分词结果为[“我”,“喜”,“欢”,“猫”,“猫”]。
- 目的:使模型能够学习字符间的组合规律、语义关联及其在不同语境中的作用。
优点
- 信息保留:最大程度地保留文本的原始信息。
- 适用性:适用于文本数据规模较小或初步探索性任务,不需要复杂的词汇表或语言学规则。
缺点
- 语义丢失:忽略了中文中的词汇和词组等固定语义搭配。
- 计算成本:可能导致输入序列过长,增加模型处理的计算量和复杂度。
适用场景: 对中文语言理解要求不高、文本数据规模较小或初步探索性的任务场景。
🔥计算成本:
在多头注意力机制里需要处理更多的元素时,计算它们之间的注意力权重等操作次数都会增加,而且模型需要从更多的单字组合中去学习语义和语法规则,学习难度相对也会提高,需要更多的数据和训练轮次来达到较好的效果。
4.2 与其他常见中文 Tokenization 方法对比
分词方式 | 原理 | 优点 | 缺点 | 示例 |
---|---|---|---|---|
简单单字分词 | 将每个字符视为一个独立的语义单元 | 最大程度保留文本原始信息 | 忽略词汇、词组的固定语义搭配 | “猫猫”被分成“猫”“猫” |
基于词表的分词(如jieba) | 依据预先构建的中文词表进行分词 | 贴合中文语义理解常规模式 | 需要不断更新词表 | “我喜欢猫猫”被分为“我”“喜欢”“猫猫” |
基于统计的分词 | 使用统计模型(如HMM、CRF)考虑词语上下文关系 | 适应性强,处理歧义词和未登录词 | 训练成本高,分词速度慢 | CRF算法处理“我喜欢猫猫” |
基于深度学习的分词(如BERT) | 预训练模型学习字符间的语义和语法关系 | 适应性强,处理各种文本风格 | 需要大量计算资源和语料 | BERT分词“我喜欢猫猫” |
说明:
- 简单单字分词:这种方法操作相对简单,易于实现,尤其适用于一些对中文语言理解要求不那么高、文本数据规模相对较小或者初步探索性的任务场景。但它忽略了中文里大量存在的词汇、词组等具有固定语义搭配的语言现象。
- 基于词表的分词:这类方法通常会依据一个预先构建好的中文词表来进行分词操作。词表中包含了常见的中文词汇、成语、固定搭配等词条。这种方法更贴合人们对中文语义理解的常规模式,但词表需要不断更新维护以适应新出现的词汇。
- 基于统计的分词:这类方法通常使用隐马尔可夫模型(HMM)、条件随机场(CRF)、支持向量机(SVM)、深度学习等算法。它们通过对汉字进行标注训练,不仅考虑词语的出现频率,还考虑词语的上下文关系,从而具备较强的学习能力。但训练模型的成本较高,因为需要大量的标注数据和计算资源。
- 基于深度学习的分词:在海量语料上进行训练,学习到字符之间的语义和语法关系,进而自动进行分词。这类方法需要大量的计算资源和语料进行训练,上手比较困难,但具有很强的适应性,能够处理各种不同风格、领域的文本。
5.LLM时代下的繁体中文Tokenization
当前,针对中文PLMs的分词策略普遍采取将每个汉字视为不可分割的基本单元。然而,这种处理方式未能充分考虑到中文书写系统的特性,即字元(通常是指最小的语言单位,即单个汉字)级别上蕴含着丰富的语义信息。
为了有效地挖掘并利用这些信息,学术界在简体中文领域提出了一种称为子字元(Sub-character, 简称SubChar)的分词方法。
该方法的具体实现包括两个步骤:
- 首先,依据汉字的字形或发音特征,将中文字元转换成一系列基本单元的序列,从而实现对原始文本的编码;
- 其次,基于上述编码结果,执行子词分割操作,进而构建出词汇表。
研究结果显示,SubChar分词方法相较于传统的分词技术,展现出两个显著优势:
- 一是通过生成更短的序列,显著提升了计算效率;
- 二是基于发音的SubChar分词器能够将同音字映射至相同的拼音序列,并产生一致的分词结果,从而对于同音字的拼写错误展现出较强的容错性。
相关研究成果的代码已公开发布,可于以下链接获取:https://github.com/thunlp/SubCharTokenization。
此外,Hugginface平台亦提供了该研究的标记化模型,便于研究者及开发者使用:https://huggingface.co/thunlp/SubCharTokenization/tree/main。
他们的分词示例如下图,而五笔、拼音是2种中文输入法。
其中五笔输入法的例子如下图:
个人和小型企业通常只能对基础模型进行微调,这要求使用与基础模型相同的标记化方法。针对繁体中文的定制化标记化需要对模型进行持续预训练,包括使用新的tokens和更新词汇表。
Taiwan-LLM团队在台湾地区实施了这一预训练,基于如llama2等基础模型,在大规模台湾语料库上进一步预训练,以提高模型对繁体中文的适应性。尽管进行了数据筛选和修复,但研究未提出专门针对繁体中文标记化的解决方案,限制了模型对繁体中文特性的捕捉。
目前,大型语言模型(LLM)时代尚未发展出适合繁体中文的分词方法,而是依赖特制的繁体中文分词语料库进行训练。
👩🏫其他工具
序号 | 工具名称 | 描述 | 链接 |
---|---|---|---|
1 | HanLP | 中文分词、词性标注、命名实体识别等 | GitHub |
2 | CoreNLP | 核心NLP工具集,支持tokenization、NER等 | GitHub |
3 | ANSJ分词 | Java实现的中文分词工具 | GitHub |
4 | LTP | 语言技术平台,支持多种中文NLP任务 | GitHub |
5 | KCWS | 基于深度学习的中文分词工具 | GitHub |
6 | Cppjieba | 结巴中文分词的C+++版本 | GitHub |
7 | IKAnalyzer | 轻量级中文分词工具包(不再维护) | GitHub |
8 | THULAC | 高效的中文词法分析工具包 | GitHub |
9 | THULAC | 高效的中文词法分析工具包 | GitHub |
10 | Keras | 深度学习框架 | GitHub |
11 | spaCy | 英文分词工具 | GitHub |
12 | Gensim | 英文分词工具 | GitHub |
13 | NLTK | 英文分词工具 | GitHub |
参考链接
1.训练和预测过程中的 Mask 实现:链接
2.Mask代码详解:链接
3.Teacher forcing:
4.研究论文
- 《Attention is All You Need》
- 《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》
- 《Language Models are Unsupervised Multitask Learners》
- Attention in transformers, visually explained
- Transformer Neural Networks, ChatGPT's foundation
5.https://www.sciencedirect.com/science/article/pii/S1319157823002082#f0005
7.深入理解 Tokenization](https://jina.ai/zh-CN/news/a-deep-dive-into-tokenization/)
8.GitHub - thunlp/SubCharTokenization
9.Hugging Face - thunlp/SubCharTokenization
最后,谢谢Datawhale提供优质学习资源!👍