前言

Subword算法如今已经成为了一个重要的NLP模型性能提升方法。自从2018年BERT横空出世横扫NLP界各大排行榜之后,各路预训练语言模型如同雨后春笋般涌现,其中Subword算法在其中已经成为标配。所以作为NLP界从业者,有必要了解下Subword算法的原理。

1. 与传统空格分隔tokenization技术的对比

  • 传统词表示方法无法很好的处理未知或罕见的词汇(OOV问题)。

    • 在自然语言处理或者文本处理的时候,我们通常会有一个字词库(vocabulary)。这个vocabulary要么是提前加载的,或者是自己定义的,或者是从当前数据集提取的。
    • 假设之后你有了另一个的数据集,这个数据集中有一些词并不在你现有的vocabulary里,我们就说这些词汇是Out-of-vocabulary,简称OOV。
  • 传统词tokenization方法不利于模型学习词缀之间的关系。
    • E.g. 模型学到的“old”, “older”, and “oldest”之间的关系无法泛化到“smart”, “smarter”, and “smartest”。
  • Character embedding作为OOV的解决方法粒度太细。
  • Subword粒度在词与字符之间,能够较好的平衡OOV问题。

2. Byte Pair Encoding (Sennrich et al., 2015)[1]

BPE(字节对)编码或二元编码是一种简单的数据压缩形式,其中最常见的一对连续字节数据被替换为该数据中不存在的字节[2]。后期使用时需要一个替换表来重建原始数据。OpenAI GPT-2 与Facebook RoBERTa均采用此方法构建subword vector。

  • 优点

    • 可以有效地平衡词汇表大小和步数(编码句子所需的token数量)。
  • 缺点
    • 基于贪婪和确定的符号替换,不能提供带概率的多个分片结果。

2.1 算法[3]

  1. 准备足够大的训练语料
  2. 确定期望的subword词表大小
  3. 将单词拆分为字符序列并在末尾添加后缀“ </ w>”,统计单词频率。本阶段的subword的粒度是字符。例如,“ low”的频率为5,那么我们将其改写为“ l o w </ w>”:5
  4. 统计每一个连续字节对的出现频率,选择最高频者合并成新的subword
  5. 重复第4步直到达到第2步设定的subword词表大小或下一个最高频的字节对出现频率为1

停止符"“的意义在于表示subword是词后缀。举例来说:“st"字词不加”“可以出现在词首如"st ar”,加了”“表明改字词位于词尾,如"wide st”,二者意义截然不同。

每次合并后词表可能出现3种变化:

  • +1,表明加入合并后的新字词,同时原来的2个子词还保留(2个字词不是完全同时连续出现)
  • +0,表明加入合并后的新字词,同时原来的2个子词中一个保留,一个被消解(一个字词完全随着另一个字词的出现而紧跟着出现)
  • -1,表明加入合并后的新字词,同时原来的2个子词都被消解(2个字词同时连续出现)

实际上,随着合并的次数增加,词表大小通常先增加后减小。

例子

输入

{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}

Iter 1, 最高频连续字节对"e"和"s"出现了6+3=9次,合并成"es"。输出:

{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3}

Iter 2, 最高频连续字节对"es"和"t"出现了6+3=9次, 合并成"est"。输出:

{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3}

Iter 3, 以此类推,最高频连续字节对为"est"和"" 输出:

{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3}

……
Iter n, 继续迭代直到达到预设的subword词表大小或下一个最高频的字节对出现频率为1。

2.2 BPE实现[4]

import re, collectionsdef get_stats(vocab):"""统计字节对出现的频率,类似于数据结构中哈夫曼树的构建,不过这里是选择频率最高的字符组成字节对"""# 构建字典,value为intpairs = collections.defaultdict(int)for word, freq in vocab.items():symbols = word.split()for i in range(len(symbols)-1):pairs[symbols[i],symbols[i+1]] += freqreturn pairsdef merge_vocab(pair, v_in):v_out = {}# 转义 pattern 中的特殊字符。# 如果你想对任意可能包含正则表达式元字符的文本字符串进行匹配,它就是有用的。bigram = re.escape(' '.join(pair))# (?<!\S) 反向否定预查,前面应该是空格# (?!\S)')正向否定预查,后面也是空格p = re.compile(r'(?<!\S)' + bigram + r'(?!\S)')for word in v_in:# 符合相应模型 完成替换w_out = p.sub(''.join(pair), word)v_out[w_out] = v_in[word]return v_outvocab = {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}
num_merges = 1000
for i in range(num_merges):pairs = get_stats(vocab)if not pairs:breakbest = max(pairs, key=pairs.get)vocab = merge_vocab(best, vocab)print(best)# print output
# ('e', 's')
# ('es', 't')
# ('est', '</w>')
# ('l', 'o')
# ('lo', 'w')
# ('n', 'e')
# ('ne', 'w')
# ('new', 'est</w>')
# ('low', '</w>')
# ('w', 'i')
# ('wi', 'd')
# ('wid', 'est</w>')
# ('low', 'e')
# ('lowe', 'r')
# ('lower', '</w>')

2.3 编码和解码[4]

编码

在之前的算法中,我们已经得到了subword的词表,对该词表按照子词长度由大到小排序。编码时,对于每个单词,遍历排好序的子词词表寻找是否有token是当前单词的子字符串,如果有,则该token是表示单词的tokens之一。

我们从最长的token迭代到最短的token,尝试将每个单词中的子字符串替换为token。最终,我们将迭代所有tokens,并将所有子字符串替换为tokens。如果仍然有子字符串没被替换但所有token都已迭代完毕,则将剩余的子词替换为特殊token,如。

# 给定单词序列
[“the</w>, “highest</w>, “mountain</w>]# 假设已有排好序的subword词表
[“errrr</w>, “tain</w>, “moun”, “est</w>, “high”, “the</w>, “a</w>]# 迭代结果
"the</w>" -> ["the</w>"]
"highest</w>" -> ["high", "est</w>"]
"mountain</w>" -> ["moun", "tain</w>"]

编码的计算量很大。在实践中,我们可以pre-tokenize所有单词,并在词典中保存单词tokenize的结果。如果我们看到字典中不存在的未知单词。我们应用上述编码方法对单词进行tokenize,然后将新单词的tokenization添加到字典中备用。

解码

将所有的tokens拼在一起。

# 编码序列
[“the</w>, “high”, “est</w>, “moun”, “tain</w>]# 解码序列
“the</w> highest</w> mountain</w>

3. WordPiece (Schuster et al., 2012)[5]

WordPiece算法可以看作是BPE的变种。不同点在于,WordPiece基于概率生成新的subword而不是下一最高频字节对。

3.1 算法[3]

  1. 准备足够大的训练语料

  2. 确定期望的subword词表大小

  3. 将单词拆分成字符序列

  4. 基于第3步数据训练语言模型

  5. 从所有可能的subword单元中选择加入语言模型后能最大程度地增加训练数据概率的单元作为新的单元

    • 我们一般在训练语义模型时,判断一句话的概率有多大。这里是考虑字符构成单词的概率有多大
  6. 重复第5步直到达到第2步设定的subword词表大小或概率增量低于某一阈值

4. Unigram Language Model (Kudo, 2018)[6]

ULM是另外一种subword分隔算法,它能够输出带概率的多个子词分段。它引入了一个假设:所有subword的出现都是独立的,并且subword序列由subword出现概率的乘积产生。WordPiece和ULM都利用语言模型建立subword词表。

4.1 算法[3]

  1. 准备足够大的训练语料
  2. 确定期望的subword词表大小
  3. 给定词序列优化下一个词出现的概率
  4. 计算每个subword的损失
  5. 基于损失对subword排序并保留前X%。为了避免OOV,建议保留字符级的单元
  6. 重复第3至第5步直到达到第2步设定的subword词表大小或第5步的结果不再变化

总结

  1. subword可以平衡词汇量和对未知词的覆盖。极端的情况下,我们只能使用26个token(即字符)来表示所有英语单词。一般情况,建议使用16k或32k子词足以取得良好的效果,Facebook RoBERTa甚至建立的多达50k的词表。

  2. 对于包括中文在内的许多亚洲语言,单词不能用空格分隔。因此,初始词汇量需要比英语大很多。

参考文献:

[1] Sennrich, Rico, Barry Haddow, and Alexandra Birch. "Neural machine translation of rare words with subword units."arXiv preprint arXiv:1508.07909(2015).

[2] Byte pair encoding - Wikipedia - https://en.wikipedia.org/wiki/Byte_pair_encoding

[3] 3 subword algorithms help to improve your NLP model performance - https://medium.com/@makcedward/how-subword-helps-on-your-nlp-model-83dd1b836f46

[4] Lei Mao’s Log Book – Byte Pair Encoding - https://leimao.github.io/blog/Byte-Pair-Encoding/

[5] Schuster, Mike, and Kaisuke Nakajima. “Japanese and korean voice search.” 2012 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP). IEEE, 2012.

[6] Kudo, Taku. “Subword regularization: Improving neural network translation models with multiple subword candidates.” arXiv preprint arXiv:1804.10959 (2018).
[7] https://mp.weixin.qq.com/s/xD4LlhkwT_wd51yDWxFp8Q

理解 NLP Subword算法:BPE、WordPiece、ULM相关推荐

  1. Subword算法:BPE,WordPiece,ULM

    本文基本转载于 深入理解NLP Subword算法:BPE.WordPiece.ULM bpe分词 子词技巧:The Tricks of Subword 对于一句话你需要: 将输入切分成小块 将输入表 ...

  2. 深入理解NLP Subword算法:BPE、WordPiece、ULM

    CHANGLOG 4/18/2020,规范化引用 3/27/2020,新增目录. 前言 Subword算法如今已经成为了一个重要的NLP模型性能提升方法.自从2018年BERT横空出世横扫NLP界各大 ...

  3. 深入理解NLP Subword算法:BPE、WordPiece、ULM ,sentencepiece

    https://zhuanlan.zhihu.com/p/86965595 https://zhuanlan.zhihu.com/p/75271211

  4. 有必要了解的Subword算法模型

    点击下方标题,迅速定位到你感兴趣的内容 前言 介绍 Subword算法 Byte Pair Encoding(BPE) Wordpiece\Sentecepiece model unigram lan ...

  5. NLP Subword三大算法原理:BPE、WordPiece、ULM

    Subword算法如今已经成为了一个重要的NLP模型性能提升方法.自从2018年BERT横空出世横扫NLP界各大排行榜之后,各路预训练语言模型如同雨后春笋般涌现,其中Subword算法在其中已经成为标 ...

  6. 谷歌AI论文BERT双向编码器表征模型:机器阅读理解NLP基准11种最优(公号回复“谷歌BERT论文”下载彩标PDF论文)

    谷歌AI论文BERT双向编码器表征模型:机器阅读理解NLP基准11种最优(公号回复"谷歌BERT论文"下载彩标PDF论文) 原创: 秦陇纪 数据简化DataSimp 今天 数据简化 ...

  7. 看图轻松理解数据结构与算法系列(合并排序)

    前言 推出一个新系列,<看图轻松理解数据结构和算法>,主要使用图片来描述常见的数据结构和算法,轻松阅读并理解掌握.本系列包括各种堆.各种队列.各种列表.各种树.各种图.各种排序等等几十篇的 ...

  8. 杭州校招 | ​阿里钉钉算法技术团队招聘NLP/机器学习算法工程师

    合适的工作难找?最新的招聘信息也不知道? AI 求职为大家精选人工智能领域最新鲜的招聘信息,助你先人一步投递,快人一步入职! 阿里巴巴 钉钉是阿里巴巴集团打造的全球头部企业级智能移动办公平台,致力于帮 ...

  9. 看图轻松理解数据结构与算法系列(2-3树)

    前言 推出一个新系列,<看图轻松理解数据结构和算法>,主要使用图片来描述常见的数据结构和算法,轻松阅读并理解掌握.本系列包括各种堆.各种队列.各种列表.各种树.各种图.各种排序等等几十篇的 ...

最新文章

  1. 抢人饭碗了!推荐一款全自动的机器学习建模神器PyCaret
  2. 十几万人同时在线的直播间聊天,如何设计服务端架构?
  3. LiveVideoStackCon 2019上海 优秀出品人与讲师
  4. pythonpath manager_python 路径操作工具 pathlib,比 os 模块好用太多
  5. 入行二十年的一些认知
  6. buck电路pscad仿真_100kVar SVG模块主电路选型分析[李博士]
  7. 【报告分享】中国城市人工智能发展指数报告.pdf(附下载链接)
  8. 柯洁:我受够了AI围棋;联发科校园招聘,应届生年薪约45万元;Chrome 100发布 | 极客头条...
  9. easypoi 大数据 百万_燃烧大数据 | 分析了2百万份成绩后发现,女跑者更稳?
  10. MVC学习第七节 UrlHelper
  11. Spring Boot 2.X默认连接池HikariCP详解
  12. 二进制文件(.bin)查看
  13. 直击进博会 | 强生、默沙东、史赛克、雅培、丹纳赫、罗氏、拜耳等医药巨头带来哪些新产品、新技术?...
  14. 什么是虚拟内存?虚拟内存的原理
  15. Socket编程之聊天室
  16. 迈克菲实验室:2018五大网络安全威胁
  17. 友善串口工具接收数据随机换行_mfc串口收发数据。编辑框希望每接收一次就换行。怎么实现。我的换行结果不对...
  18. 弹性盒子布局(下面有代码)
  19. 集成MOB 社会分享遇到的坑
  20. 今天的区块链现状是九十年代的互联网?

热门文章

  1. 中国羽绒服行业何时诞生第二个“波司登”?
  2. lazada根据ID取商品详情详细解析?(详细解释)
  3. CentOS搭建Open服务(集成openldap认证)
  4. android 电话号码标记,Android
  5. 目前微博粉丝的四大来源
  6. 从微信扔骰子看iOS应用安全与逆向分析
  7. 【Tool】保留labelImg的上一张标注信息
  8. ssm+java计算机毕业设计党员发展管理系统c8z53(程序+lw+源码+远程部署)
  9. ceb 抽取 linux 工具,Linux系统常用监控工具
  10. 身高和弹跳摸高达到多少才能扣篮