ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Transformers 分词算法详解:BPE、WordPiece、Unigram 与 SentencePiece 的原理与源码实现

Transformers 分词算法详解:BPE、WordPiece、Unigram 与 SentencePiece 的原理与源码实现 Transformers 分词算法详解BPE、WordPiece、Unigram 与 SentencePiece 的原理与源码实现【免费下载链接】transformers Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers本文以 HuggingFace Transformers 仓库中的分词算法概述文档为主体系统讲解文本分词的核心难点、子词subword分词的设计动机以及 BPE、字节级 BPE、WordPiece、Unigram 和 SentencePiece 五种算法的原理与典型工作过程并结合仓库中 BERT、GPT-2、XLNet 等真实分词器源码展示每种算法在 Transformers 里的落地方式帮助读者既理解分词算法背后的训练机制也能看懂库内分词器的实现细节。一、为什么文本分词不是简单的事词法分词tokenization的目标是把文本切分成词或子词subword再通过查找表lookup table将每个词或子词映射为整数 ID。由于词/子词 → ID这一步本身是直接的分词的核心难点在于如何把文本切成合适的单元。文档用一个经典例子说明这一点考虑句子Dont you love Transformers? We sure do.第一步按空格切分[Dont, you, love, , Transformers?, We, sure, do.]这是最直观的切法但仔细看Transformers?和do.可以发现标点符号附着在单词Transformers和do上。这并不理想——如果标点不剥离模型就必须为每个词 每种可能跟随的标点学习不同的表示这会令模型需要学习的表示数量急剧膨胀。第二步考虑标点[Don, , t, you, love, , Transformers, ?, We, sure, do, .]这样好多了但Dont的切分方式仍然不理想。Dont实际是do not的缩写更合理的分析是把它当作两个压缩在一起的词[Do, nt]。事情到这里开始变得复杂而这正是每个模型都有自己的分词器tokenizer的原因。根据切分文本所采用的规则不同同一文本会被切成不同的结果一个预训练模型只有拿到按照训练时相同规则切分的输入才能正常发挥作用。基于规则的分词器spaCy 和 Moses 是两个常见的基于规则的分词器。应用到上面的例子它们的输出类似[Do, nt, you, love, , Transformers, ?, We, sure, do, .]这里同时使用了空格切分、标点剥离以及基于规则的词法分析。空格切分、标点剥离和规则分析都属于词切分word segmentation可以粗略定义为把句子切成词。虽然这是把文本切小的最直觉方式但它对大规模语料会造成问题空格标点切分通常会生成过大的词表vocabulary即语料中所有出现过的唯一词/符号的集合。例如 Transformer XL 使用空格切分其词表大小达到 267735超大词表迫使模型在输入层和输出层都配备庞大的嵌入矩阵embedding matrix同时推高时间复杂度和内存占用。一般来说Transformers 模型的词表很少超过 50000尤其是只在单一语言上预训练的模型。既然简单的空格标点切分不满足需求那为什么不干脆按字符切分按字符切分虽然极其简单能大幅降低时间复杂度和内存需求却让模型很难学到有语义的输入表示学习一个上下文中立的字符t的表示比学习一个上下中立的词如今天的表示难得多因此纯字符级切分往往伴随性能下降。二、子词分词Subword Tokenization为了兼得两者的优点Transformers 采用介于词级切分与字符级切分之间的混合方案——子词分词subword tokenization。子词算法的核心原则是常用词不应再被拆成更小的单元而稀有词应被分解为有语义的子词。例如annoyingly可被视为稀有词拆成annoying和ly两个子词各自出现频率更高而annoyingly的语义通过annoying与ly的组合得以保留。这在黏着语如土耳其语中尤其有用——长复合词可以通过拼接子词近乎任意地构造出来。子词分词带来三个好处词表体积保持合理模型仍能学到有语义的上下中立表示模型能处理训练中从未见过的词——把它们分解为已知子词即可。以仓库中 BERT 的分词器为例google-bert/bert-base-uncased为不区分大小写的模型输入会先被转小写 from transformers import BertTokenizer tokenizer BertTokenizer.from_pretrained(google-bert/bert-base-uncased) tokenizer.tokenize(I have a new GPU!) [i, have, a, new, gp, ##u, !][i, have, a, new]这些词都在分词器词表中但gpu不在——于是它被拆成已知子词[gp, ##u]。前缀##表示该子词应与前一个子词无空格连接对应反分词/解码操作。另一个例子是 XLNet 的分词器它对文档开头那句示例文本的输出 from transformers import XLNetTokenizer tokenizer XLNetTokenizer.from_pretrained(xlnet/xlnet-base-cased) tokenizer.tokenize(Dont you love Transformers? We sure do.) [▁Don, , t, ▁you, ▁love, ▁, ▁Transform, ers, ▁We, ▁sure, ▁do, .]其中▁符号的含义会在后文 SentencePiece 部分解释。可以看到稀有词Transformers被拆成更高频的子词Transform和ers。最后强调一点所有子词切分算法都依赖某种形式的训练且训练通常在模型最终要训练的那份语料上进行——词表是学出来的不是拍脑袋定出来的。三、BPEByte Pair EncodingBPE字节对编码由 Sennrich 等人 2015 年的论文《Neural Machine Translation of Rare Words with Subword Unitization》提出。它的工作流程分两阶段第一阶段预切分pre-tokenization。BPE 依赖一个初始分词器把训练数据切成词。预切分可以很简单如按空格切分GPT-2、RoBERTa 就是如此也可以更复杂、基于规则如 XLM、FlauBERT 对大多数语言使用 MosesGPT 使用 spaCy ftfy。预切分之后得到一组唯一词及每个词在训练语料中的出现频次。第二阶段学习合并规则。BPE 由所有出现过的符号构成基础词表base vocabulary然后反复学习把基础词表中的两个相邻符号合并成一个新符号的规则直到词表达到目标大小。注意目标词表大小是训练分词器前就要确定的超参数。完整训练示例假设预切分后得到的唯一词及其频次为(hug, 10), (pug, 5), (pun, 12), (bun, 4), (hugs, 5)于是基础词表是[b, g, h, n, p, s, u]。把所有词用基础词表符号展开(h u g, 10), (p u g, 5), (p u n, 12), (b u n, 4), (h u g s, 5)随后 BPE 统计每个相邻符号对出现的总次数选择出现最频繁的一对合并u后跟g共出现 10 5 5 20次hug、pug、hugs是最频繁的符号对h u 只出现 10 5 15 次。于是第一条合并规则是所有u后跟g合并为ug并把ug加入词表。词集变为(h ug, 10), (p ug, 5), (p u n, 12), (b u n, 4), (h ug s, 5)第二频繁的符号对是un出现 12 4 16 次合并为un并入词表。第三频繁的是hug出现 10 5 15 次合并为hug并入词表。此时词表为[b, g, h, n, p, s, u, ug, un, hug]唯一词集的表示为(hug, 10), (p ug, 5), (p un, 12), (b un, 4), (hug s, 5)假设 BPE 训练到此停止学到的合并规则之后会应用到新词上前提是新词不包含基础词表中没有的符号例如bug会切分为[b, ug]而mug会切分为[unk, ug]因为m不在基础词表中。实践中单个字母如m通常不会被替换成unk因为训练数据中每个字母至少出现过一次但极端罕见的符号比如某些 emoji出现这种情况是可能的。词表大小的构成是目标词表大小 基础词表大小 合并次数。例如 GPT 的词表大小为 40478因为它有 478 个基础符号并在执行 40,000 次合并后停止训练。字节级 BPEByte-level BPE如果基础词表要覆盖所有 Unicode 字符它会大得难以接受。GPT-2 的巧妙做法是用字节bytes作为基础词表基础词表被固定为 256 个字节值既保证了词表规模可控又保证了任何基础字符都能被表示。配合一些处理标点的额外规则GPT-2 分词器可以在不需要unk符号的情况下切分任意文本。GPT-2 的词表大小为 50257对应 256 个基础字节符号 1 个特殊符结束符 50,000 次合并学到的符号。源码实现GPT-2 分词器在仓库中GPT-2 分词器的实现印证了上述描述。GPT2Tokenizer 的文档字符串明确写道Based on byte-level Byte-Pair-Encoding其内部构建如下# src/transformers/models/gpt2/tokenization_gpt2.py self._tokenizer Tokenizer( BPE( vocab, merges, dropout..., unk_token..., ) ) self._tokenizer.pre_tokenizer pre_tokenizers.ByteLevel(add_prefix_spaceadd_prefix_space) self._tokenizer.decoder decoders.ByteLevel()参见 tokenization_gpt2.py这里BPE模型负责符号对合并的解码逻辑pre_tokenizers.ByteLevel()把原始字符转成字节序列即字节级基础词表的来源decoders.ByteLevel()则负责把字节序列还原回原文——三者组合完整实现了256 字节基础词表 50,000 次合并的方案。四、WordPieceWordPiece 是 BERT、DistilBERT、Electra 使用的子词分词算法由 Schuster 等人 2012 年的《Japanese and Korean Speech Recognition》提出整体与 BPE 非常相似同样先构造一个包含训练数据中所有字符的初始词表同样逐步学习若干条合并规则。与 BPE 的关键区别在于选对标准BPE 选择出现最频繁的符号对而 WordPiece 选择**能最大化训练数据似然probability of the training data**的那个符号对。具体来说一个符号对被合并当且仅当该对合并后新符号的概率 ÷ 合并前两符号概率的乘积在所有候选符号对中最大。以训练示例为例ug只有在P(ug) / (P(u) × P(g))大于任何其他符号对的对应比值时才会被合并。直观理解WordPiece 与 BPE 略有不同之处在于它会评估把两个符号合并会损失多少概率从而确认这次合并值不值得。源码实现BERT 分词器BERT 分词器的实现位于 tokenization_bert.py。BertTokenizer 继承自TokenizersBackend其核心构建代码清晰展示了 WordPiece 在库中的装配方式# src/transformers/models/bert/tokenization_bert.py self._tokenizer Tokenizer(WordPiece(self._vocab, unk_tokenstr(unk_token))) self._tokenizer.normalizer normalizers.BertNormalizer( clean_textTrue, handle_chinese_charstokenize_chinese_chars, strip_accentsstrip_accents, lowercasedo_lower_case, ) self._tokenizer.pre_tokenizer pre_tokenizers.BertPreTokenizer() self._tokenizer.decoder decoders.WordPiece(prefix##)参见 tokenization_bert.py几个组件对应文档中讲到的行为WordPiece(self._vocab, ...)核心分词模型按前文所述最大化数据似然的方式选择切分unk_token用于处理无法切分的词对应[UNK]BertPreTokenizer预切分器实现 BERT 特有的预切分规则Unicode 规范化、中文/日文字符逐字拆分等decoders.WordPiece(prefix##)解码器把##前缀还原为无空格拼接——这正是文档示例中gpu → [gp, ##u]里##符号的出处构造参数中还可看到完整的特殊符号默认值unk_token[UNK]、sep_token[SEP]、pad_token[PAD]、cls_token[CLS]、mask_token[MASK]以及do_lower_caseTrue这一解释为什么输入被转成小写的参数。五、UnigramUnigram 由 Kudo 2018 年的论文《Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates》提出。它与前两者的思路方向相反BPE / WordPiece 从小词表出发逐步做加法学合并规则Unigram 先构造一个很大的候选词表例如所有预切分词 大量高频子串然后逐步做减法裁剪到目标大小。训练过程在每一步Unigram 基于当前词表和 unigram 语言模型计算整个训练语料上的损失通常取对数似然。然后对词表中每个符号计算若把它从词表中移除总损失会增加多少随后移除p通常为 10% 或 20%损失增加量最小的符号——即那些对整体损失影响最小的符号。重复该过程直到词表达到目标大小。算法始终保留基础字符确保任何词都能被切分。由于 Unigram不依赖合并规则不同于 BPE 和 WordPiece训练完成后对同一新文本可能存在多种合法的切分方式。例如若一个训练好的 Unigram 分词器词表为[b, g, h, n, p, s, u, ug, un, hug]那么hugs可以切分为[hug, s]也可以[h, ug, s]或[h, u, g, s]。选哪一个Unigram 训练时保存了每个符号的概率连同词表一起存储因此可以对所有可能的切分计算概率实践中通常选取概率最高的切分但也支持按概率采样出一种合法切分这正是子词正则化思想的应用。这些概率由训练损失定义。设训练数据由词 ((x_1, \dots, x_N)) 组成词 (x_i) 所有可能切分构成的集合记为 (S(x_i))则总损失定义为$$\mathcal{L} -\sum_{i1}^{N} \log \left ( \sum_{x \in S(x_{i})} p(x) \right )$$文档指出Unigram 本身不直接用于 Transformers 库中任何模型的分词而是与 SentencePiece 配合使用。六、SentencePiece到这里介绍的每种算法都有一个共同假设输入文本用空格分词。但并非所有语言都这样——中文、日文、泰文等不使用空格。一个特例化的解法是使用语言特定的预处理器XLM 就针对中文、日文、泰文使用专门的预处理器。SentencePieceKudo 等人 2018 年论文《SentencePiece: A simple and language independent subword tokenizer and detokenizer for Neural Text Processing》给出了更普适的解法它把输入视为原始字节流raw data stream因此把空格也当作普通字符纳入字符集合然后在这样的集合上运行 BPE 或 Unigram 来构建词表——语言无关不需要任何预处理器。以仓库中 XLNetTokenizer 为例它基于 Unigram 模型 SentencePiece这正是它词表中出现特殊字符▁的原因——▁就是被实体化的空格标记。这也是前文 XLNet 示例输出中▁Don、▁you等前缀的来源。由于空格已显式编码在符号中反分词detokenization极其简单把符号拼接起来再把▁替换回空格即可。从源码看tokenization_xlnet.py 中分词器内部构建的是Unigram(...)模型与 T5、ALBERT 等模型使用的.spmSentencePiece 模型文件配合印证了库中所有使用 SentencePiece 的模型均搭配 Unigram的说法——典型使用者包括 ALBERT、XLNet、Marian、T5T5 分词器在 tokenization_t5.py 中明确声明其基于 SentencePiece 的*.spm模型文件。七、如何查看某模型使用哪种分词器文档给出的实用建议是在库的每个模型文档页中查看其关联的分词器文档即可知道该预训练模型使用哪种分词算法。例如查看BertTokenizer的文档就能确认 BERT 用的是 WordPiece。这一建议在当前仓库中可以直接验证每个模型的tokenization_*.py文件头部都声明了算法依据——模型分词器实现声明的算法BERTtokenization_bert.pyWordPiece源码注释 Based on WordPieceGPT-2tokenization_gpt2.py字节级 BPEBased on byte-level Byte-Pair-EncodingXLNettokenization_xlnet.pyUnigramSentencePiece 格式词表T5tokenization_t5.pyUnigram SentencePiece.spm文件八、小结空格/标点/规则切分直觉上最简单但会产生超大词表Transformer XL 达 267735推高嵌入矩阵的内存与计算开销纯字符切分词表虽小却让模型难以学到有语义的表示。子词分词是折中方案常用词保持完整稀有词/生词被拆成已知子词从而控制词表规模一般不超过 50,000并具备处理 OOV 词的能力。BPE从小词表出发做加法反复合并最频繁的相邻符号对字节级 BPEGPT-2把基础词表固定为 256 个字节彻底消除了unk的需要。WordPieceBERT 系与 BPE 类似但合并标准是最大化训练数据似然的增量而非单纯频次。Unigram思路相反从大候选词表做减法按移除后损失增加最少的比例逐步删减符号并保留各符号概率以支持多种切分与采样。SentencePiece把文本当作原始字节流、将空格编码为▁从机制上解决了非空格分词语言的普适性问题在库中与 Unigram 搭配使用XLNet、T5、ALBERT、Marian 等。所有算法的词表都在训练语料上学习得到且训练语料通常就是模型要训练的那份数据——预训练模型要求推理时输入按相同规则切分这是理解每个模型为何自带专属 tokenizer的根本原因。【免费下载链接】transformers Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表