分词器的词表大小以及如果分词器的词表比模型的词表大,那么模型的嵌入矩阵需要被调整以适应新的词表大小。
分词器的词表大小是怎么来的
分词器的词表大小是由分词器在训练过程中使用的数据集决定的。具体来说,分词器会根据输入的文本数据集进行分词,然后构建一个词汇表。这个词汇表包含了所有分词器能够识别和处理的词语或子词的集合。词表大小就是这个集合中元素的数量。
分词器如何构建词表:
-
数据集准备:
分词器在训练时需要大量的文本数据。通常,这些文本数据包括大量的文章、书籍、网页内容等,以确保覆盖尽可能多的语言现象。 -
分词策略:
分词器会使用不同的分词策略来将文本划分为词或子词。例如:- Word-level Tokenization(词级分词):把文本按空格划分成独立的词。
- Subword-level Tokenization(子词级分词):将词语进一步拆分为子词,这种方式在处理低频词和新词时特别有用。常见的子词分词方法有 BPE(Byte Pair Encoding)、WordPiece、SentencePiece 等。
-
构建词表:
- 分词器会统计所有出现的词汇或子词的频率。
- 根据词频的高低来决定哪些词或子词应该进入词表。通常会选择出现频率较高的词或子词。
- 如果采用子词级别的分词方法,分词器会将低频的单词拆分为多个常见的子词,这样可以提高模型的处理能力。
-
词表大小的确定:
词表大小通常是通过设定一个最大词汇数来控制的。例如,如果词表大小设置为 30,000,那么分词器最终将包含 30,000 个最常见的词或子词,剩余的低频词将被拆分成更小的单位(如子词)。
词表大小的影响:
- 较小的词表:如果词表较小,分词器会倾向于将更多的词拆分成子词。这可以有效应对低频词和新词,但会增加模型的复杂性,因为每个词都需要拆分成多个子词进行处理。
- 较大的词表:较大的词表会减少子词拆分的需求,可能会使得模型对常见词的处理更加高效,但同时会增加模型的存储需求,并且可能导致未登录词(Out of Vocabulary,OOV)的问题。
举个例子:
假设你有一个文本数据集,里面有一句话:
"Hello, world! This is an example sentence."
- 在进行分词时,分词器可能会根据空格和标点符号将其拆分为以下几个词:
如果分词器采用子词级分词方法,它可能会将“example”拆分为更小的单位,比如:["Hello", ",", "world", "!", "This", "is", "an", "example", "sentence", "."]
在这种情况下,词表大小会根据训练数据中所有独立的词或子词的数量来确定。["exam", "##ple"]
总结:
- 分词器的词表大小是根据分词器在训练时遇到的所有词或子词的数量决定的。
- 词表大小通常是由设定的最大词汇数量限制的。
- 如果词表太小,可能需要使用子词来处理低频词;如果词表过大,会增加模型的存储和计算开销。
需要把所有训练数据都导入之后才能知道分词器的词表大小
要确定分词器的词表大小,通常需要在训练过程中使用大量的文本数据。因为分词器的词表大小是根据数据集中出现的词汇或子词的数量来构建的,因此所有的训练数据都需要被输入到分词器中,才能准确地构建出一个完整的词表。
具体流程:
-
准备训练数据:分词器会读取训练数据集,通常这会是一个大规模的文本数据集。
-
分词处理:分词器会处理这些数据,将每个句子或文本分割成词或子词(根据分词器使用的策略)。比如,如果使用 BPE 或 WordPiece 等方法,分词器会将长词拆分成子词。
-
词表构建:通过统计数据集中所有分词单元(词或子词)的频率,分词器会生成一个词汇表(词表大小),通常会按照出现频率从高到低排序,选择前 N 个最常见的词或子词作为最终的词表。
-
词表大小确认:只有在处理完所有训练数据之后,才能确定最终的词表大小。
示例:
假设你有一个数据集,包含以下句子:
1. "I love natural language processing."
2. "This is a great example of NLP."
3. "Deep learning is part of AI."
- 分词器会将这些句子分割为词或子词,例如:
["I", "love", "natural", "language", "processing", ".", "This", "is", "a", "great", "example", "of", "NLP", ".", "Deep", "learning", "is", "part", "of", "AI", "."]
- 然后,分词器会统计词汇表,可能会得到以下词表:
在这个例子中,词表大小为 16(如果没有其他词出现)。["I", "love", "natural", "language", "processing", ".", "This", "is", "a", "great", "example", "of", "NLP", "Deep", "learning", "part", "AI"]
为什么需要整个数据集?
- 词频统计:为了确保词表包含足够多的常用词或子词,分词器需要在整个数据集上统计词频。如果没有足够的数据,词表可能会漏掉一些重要的词。
- 低频词处理:如果某些词在数据集中出现次数较少,分词器可能会将这些词拆分成子词。如果没有足够的数据,某些较为特殊的词可能无法被正确处理。
例外情况:
如果使用预训练模型的分词器(例如,Hugging Face 提供的 AutoTokenizer
),那么分词器的词表是事先构建好的,不需要重新训练。此时,你只需加载该分词器,它会自动加载一个已经准备好的词表。因此,加载预训练模型时不需要全部训练数据来确定词表大小,只需要知道模型使用的词表。
总结来说,如果是在从头训练分词器,的确需要完整的训练数据集来构建词表并确定词表的大小。
训练数据的分词如果比预训练模型的分词器多怎么办?
这句话的意思是:
分词器的词表大小可以来源于两种情况:
- 如果使用预训练模型的分词器,词表大小是固定的,比如 50,000 个词(由模型的开发者定义)。
- 如果你自己定义分词器,那么词表大小由你的训练数据决定,比如训练数据里有 60,000 个独特的词或子词。
如果你的训练数据分词后产生的词表比预训练模型的分词器多,那么需要**扩展模型的嵌入层(embedding layer)**来适配新词表。否则,模型会报错,因为它无法处理超出原始词表的那些新词。
用一个简单的例子说明:
假设有一个预训练模型
- 它的分词器词表中有以下内容(简化版本):
["hello", "world", "[PAD]", "[UNK]", "[CLS]"]
- 词表大小:5
[PAD]
是填充符,[UNK]
是未知词标记,[CLS]
是分类任务的特殊符号。
现在你有一份新训练数据
- 比如:
"hello AI world"
- 分词器把它分成:
["hello", "AI", "world"]
- 分词器把它分成:
这时会发现:
"hello"
和"world"
已经在预训练模型的分词器里了。- 但
"AI"
是新词,它不在原始的词表中。
怎么办?
-
扩展词表:
- 把
"AI"
加入到词表中,扩展后的词表变成:["hello", "world", "[PAD]", "[UNK]", "[CLS]", "AI"]
- 词表大小从 5 增加到 6。
- 把
-
扩展嵌入矩阵:
- 模型的嵌入矩阵是一个二维张量,形状为
[词表大小, 嵌入维度]
,比如[5, 768]
。 - 如果词表大小增加到 6,就需要把嵌入矩阵扩展到
[6, 768]
。 - 新加入的
"AI"
的嵌入向量会被随机初始化,或者用某种策略初始化。
- 模型的嵌入矩阵是一个二维张量,形状为
具体代码例子:
from transformers import AutoTokenizer, AutoModel
# 假设加载预训练模型和分词器
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# 原始词表大小
print("原始词表大小:", len(tokenizer)) # 比如 30522
# 新训练数据中发现的新词
new_words = ["AI", "machine_learning"]
# 把新词加入分词器
tokenizer.add_tokens(new_words)
print("扩展后的词表大小:", len(tokenizer)) # 比如 30524
# 扩展模型嵌入矩阵
model.resize_token_embeddings(len(tokenizer))
总结
如果你的训练数据中有预训练模型词表中没有的新词,你需要扩展分词器的词表,同时调整模型的嵌入矩阵大小,这样模型才能正确处理新词。如果不扩展词表,模型会把这些新词当成 [UNK]
(未知词),无法很好地学习到它们的语义。
填充操作(Padding)和 词表大小的确定
填充(padding)操作通常是在训练前完成的,它的目的是确保输入到模型中的每个样本具有相同的长度,方便批量处理(batch processing)。但填充的顺序和操作与词表大小的确定是两个不同的过程。
1. 词表大小的确定
- 训练前:分词器会读取所有的训练数据,并根据数据中出现的单词或子词来构建词表。
- 分词器构建词表:这一步完成之后,词表的大小就固定了。词表包含所有模型能识别的单词或子词的映射,通常包括一些特殊标记(如
[PAD]
、[UNK]
、[CLS]
等)。 - 如果使用预训练模型的分词器:预训练模型的分词器通常已经有了一个固定的词表,你只需要加载这个词表并进行后续的训练或微调。
2. 填充操作(Padding)
填充操作的目的是使每个输入序列的长度一致。因为深度学习模型通常要求每个输入的长度相同,所以当处理变长的序列时,需要将较短的序列填充到相同的长度。
填充的过程
-
填充顺序:填充通常是在训练前完成的,并且是在分词之后执行。填充的方式有几种:
- 前填充(pre-padding):填充的部分在序列的前面。比如,将序列
["I", "love", "NLP"]
填充到长度 5 时,变为["[PAD]", "[PAD]", "I", "love", "NLP"]
。 - 后填充(post-padding):填充的部分在序列的后面。比如,将序列
["I", "love", "NLP"]
填充到长度 5 时,变为["I", "love", "NLP", "[PAD]", "[PAD]"]
。
填充的方式取决于任务的需要和模型的要求,一般来说,后填充是比较常见的方式。
- 前填充(pre-padding):填充的部分在序列的前面。比如,将序列
-
填充标记:填充时使用的特殊标记(通常是
[PAD]
)。该标记并不参与模型的训练,只是为了让输入的长度一致。
填充操作与训练
- 在模型训练之前,所有的输入序列都会根据最大长度进行填充。这个最大长度可以是固定的,或者是动态调整的(如选取数据集中的最大序列长度)。
- 填充不会影响词表的大小,词表大小是由分词器在训练前确定的,而填充仅仅是在每个样本的序列上进行操作。
训练时如何处理填充:
在训练过程中,模型通常会忽略填充部分(如在计算损失函数时不考虑 [PAD]
标记)。例如,在计算损失时,填充标记的部分通常会被掩蔽(mask),从而避免它们对训练的贡献。
示例:
假设你有以下两个序列:
- 序列 1:
["I", "love", "NLP"]
- 序列 2:
["Deep", "learning", "is", "great"]
假设你设置的最大序列长度是 5,并使用后填充。分词器的词表已经确定,包含了每个单词和子词的映射:
- 词表大小:假设包括了
[PAD]
标记。 - 序列 1 填充后:
["I", "love", "NLP", "[PAD]", "[PAD]"]
- 序列 2 填充后:
["Deep", "learning", "is", "great"]
经过填充后,所有序列长度一致,可以进行批量处理。
总结
- 词表大小是在训练前确定的,是由训练数据或预训练模型的分词器决定的。
- 填充是在训练前完成的,目的是使输入序列的长度一致。填充的操作不会影响词表大小,填充只是对序列本身进行操作,保证每个输入样本可以以相同的长度输入到模型中。