1.2 “词”是什么,如何“分词”
小冰一愣,回答:你说了,“N”就是将文本分割成连续N个词的组合的“N”,一个2-Gram表示两个相邻的词组成的序列,例如“我爱吃肉”分成“我爱”“爱吃”和“吃肉”;而一个3-Gram表示三个相邻的词组成的序列,例如“我爱吃肉”分成“我爱吃”和“爱吃肉”。以此类推,还可以有4-Gram、5-Gram……而“Gram”,我猜应该就是“词”的意思吧。
咖哥:你理解得基本正确。不过,我必须指出的是,在自然语言处理中,我们所说的“词”并不像想象的那么简单,笼统地把“Gram”称为“词”是不合适的。
“Gram”这个词来源于希腊语的词根,意为“字母”或“写作”。在N-Gram模型中,它表示文本中的一个元素,“N-Gram”指长度为N的连续元素序列。
那么你看,这里的“元素”在英文中可以指单词,也可以指字符,有时还可以指“子词”(Subword);而在中文中,可以指词或者短语,也可以指字。
咖哥发言
在自然语言处理中,子词分词算法将单词切分成更小的部分,即子词,以便更好地处理未登录词(语料库词汇表里面找不到的词)、拼写错误和词汇变化等问题。这种算法有助于减小词汇表大小,同时提高模型的泛化能力。
例如,如果我们使用子词分词算法将英文单词 "embedding" 进行切分,可能得到以下几个子词:["em", "bed", "ding"]。这是一个简化的示例,实际上的子词分词可能更复杂。
在BERT等自然语言处理模型中,常用的子词分词算法有WordPiece(使用贪婪算法或者基于统计的方法进行训练,可以根据训练数据自动学习,得出最优的分词方案)和SentencePiece(在切分子词的同时还考虑了多个子词的共性)。这些算法可以根据训练数据集自动学习出高频子词。以WordPiece为例:
"playing"可以切分为 ["play", "##ing"]
"unstoppable"可以切分为 ["un", "##stop", "##able"]
在这些例子中,双井号(##)表示该子词是一个前缀或后缀,而非一个独立的词。这有助于模型了解子词在原始单词中的位置。通过使用子词分词算法,自然语言处理模型可以更好地应对不同语言中词汇的复杂性和变化。
刚才的图示“孙悟空三打白骨精”中,是把“白骨精”作为一个“词”,也就是一个Gram来处理的,我们也可以进行粒度更细的拆分,如左图所示,那么,每一个Gram代表的就是一个“字”了。此时的一元组就是一个字,二元组就是两个字。
Gram为字时的N-Gram
小冰:那么在实际应用中,到底应该把语料库拆分成什么元素呢?是字还是词?
咖哥:那就具体情况具体分析了。在自然语言处理中,一个非常关键的预处理环节就是按照需要对语料库,也就是自然语言数据集中的句子进行分词,分词之后,文本序列就形成了可以输入语言模型的一个个“元素”(或者称为“单元”),这个元素的英文名叫作“Token”。Token翻译成中文,常常词不达意。有人叫它“令牌”,有人叫它“子词”或者“分词”。总而言之,当你看到Token,就应该知道,我们已经通过分词工具,把语料,也就是一个个句子,切成了能够被语言模型读取并处理的一个个元素。
小冰:那么,分词是怎么进行的呢?
咖哥:一般的自然语言处理工具包都为我们提供好了分词的工具。比如,英文分词通常使用NLTK、spaCy等自然语言处理库,中文分词通常使用jieba库(中文NLP工具包),而如果你将来会用到BERT这样的预训练模型,那么你就需要使用BERT的专属分词器Tokenizer,它会把每个单词拆成子词——这是BERT处理生词的方法。
咖哥发言
上面说的分词,是自然语言处理的一个预处理环节。当然,除了分词之外,还有文本清洗、去停用词[2]、词干提取和词性标注等很多NLP数据预处理技术。本书的目的是讲解GPT模型的技术演进,所以本书将笔墨聚焦于语言模型的发展、注意力机制,以及Transformer架构,对各种文本数据预处理技术不做赘述。
[2] 指在自然语言文本中频繁出现但通常对文本分析任务没有太大贡献的词语。常见的停用词包括“的”“是”“在”“和”“那”“这”“个”。
咖哥:下面我们一起用Python创建一个Bigram模型吧[3],这个模型能够根据给定的文本,预测下一个元素。
[3] 本书所有程序代码都通过Python语言实现。