token 和 tokenization

       Tokenization(分词)在NLP任务中是最基本的一步,把文本内容处理为最小基本单元即token用于后续的处理,如何把文本处理成token呢?有一系列的方法,其基本思想是构建一个词表,通过词表一一映射进行分词,但如何构建合适的词表呢?以下以分词粒度为角度进行介绍。

1. word(词)粒度

        在英文语系中,word级别分词实现很简单,因为有天然的分隔符。在中文中word级别分词实现需要依靠分词工具比如jieba,以下是中英文的例子:

中文句子:我喜欢看电影和读书。
分词结果:我 | 喜欢 | 看 | 电影 | 和 | 读书。

英文句子:I enjoy watching movies and reading books.
分词结果:I | enjoy | watching | movies | and | reading | books.

优点:

- 语义明确:以词为单位进行分词可以更好地保留每个词的语义,使得文本在后续处理中能够更准确地表达含义。

- 上下文理解:以词为粒度进行分词有助于保留词语之间的关联性和上下文信息,从而在语义分析和理解时能够更好地捕捉句子的意图。

缺点:

- 长尾效应和稀有词问题:词表可能变得巨大,包含很多不常见的词汇,增加存储和训练成本,稀有词的训练数据有限,难以获得准确的表示。

- OOV(Out-of-Vocabulary):词粒度分词模型只能使用词表中的词来进行处理,无法处理词表之外的词汇,这就是所谓的OOV问题。

- 形态关系和词缀关系:无法捕捉同一词的不同形态,也无法有效学习词缀在不同词汇之间的共通性,限制了模型的语言理解能力,比如love和loves在word粒度的词表中将会是两个词。

2. char(字符)粒度

        以字符为单位进行分词,即将文本拆分成一个个单独的字符作为最小基本单元,这种字符粒度的分词方法适用于多种语言,无论是英文、中文还是其他不同语言,都能够一致地使用字符粒度进行处理,因为英文就26个字母以及其他的一些符号,中文常见字就6000个左右。

中文句子:我喜欢看电影和读书。
分词结果:我 | 喜 | 欢 | 看 | 电 | 影 | 和 | 读 | 书 | 。

英文句子:I enjoy watching movies and reading books.
分词结果:I |   | e | n | j | o | y |   | w | a | t | c | h | i | n | g |   |...

优点:

- 统一处理方式:字符粒度分词方法适用于不同语言,无需针对每种语言设计不同的分词规则或工具,具有通用性。

- 解决OOV问题:由于字符粒度分词可以处理任何字符,无需维护词表,因此可以很好地处理一些新创词汇、专有名词等问题。

缺点:

- 语义信息不明确:字符粒度分词无法直接表达词的语义,可能导致在一些语义分析任务中效果较差。

- 处理效率低:由于文本被拆分为字符,处理的粒度较小,增加后续处理的计算成本和时间。

3.subword(子词)粒度

         在很多情况下,既不希望将文本切分为单独的词(太大),也不想将其切分为单个字符(太小),而是希望得到介于词和字符之间的子词单元。这就引入了subword(子词)粒度的分词方法。

        在BERT时代,WordPiece分词方法被广泛应用,比如BERT、DistilBERT等。WordPiece分词方法是subword(子词)粒度的一种方法。

3.1 WordPiece

        WordPiece核心思想是将单词拆分为多个前缀符号(比如BERT中的##)最小单元,再通过子词合并规则将最小单元进行合并为子词级别。例如对于单词“word”,拆分为 :

w ##o ##r ##d

然后通过合并规则进行合并,从而循环迭代构建出一个词表,以下是核心步骤:

(1)计算初始词表:通过训练语料获得,或者最初的英文中26个字母加上各种符号以及常见中文字符这些作为初始词表。

(2)计算合并分数:对训练语料拆分的多个子词单元通过合并规则计算合并分数。

(3)合并分数最高的子词对:选择分数最高的子词对,将它们合并成一个新的子词单元,并更新词表。

(4)重复合并步骤:不断重复步骤(2)和步骤(3),直到达到预定的词表大小、合并次数,或者直到不再产生有意义的合并(即进一步合并不会显著提高词表的效益)。

(5)分词:使用最终得到的词汇表对文本进行分词。

例如我们有以下的训练语料中的样例,括号中第2位为在训练语料库中出现的频率:

("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)

我们对其进行拆分为带前缀的形式:

("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12),
("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)

所以这些样例的初始词表将会是:

["b", "h", "p", "##g", "##n", "##s", "##u"]

接下来重要的一步进行计算合并分数,也称作互信息(信息论中衡量两个变量之间的关联程度)。

分数 = 合并pair候选的频率 / (第一个元素的频率 * 第二个元素的频率)

对于上述样例中这个pair("##u", "##g")出现的频率是最高的20次,但是"##u"出现的频率是36次,"##g"出现的频率是20次,所以这个pair的分数是 20/(36*20)=1/36,同理计算这个pair("##g", "##s")的分数为 5/(20*5)=1/20,所以最先合并的pair是("##g", "##s")->("##gs")。此时词表和拆分后的频率将会变成以下:

Vocabulary:["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]

Corpus:("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12),
       ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)

重复上述的操作,直到达到你想要的词表的大小。

Vocabulary:["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]

Corpus:("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4),
       ("hu" "##gs", 5)

一般来说最后会在词表中加上一些特殊词汇、英文26个字母,各种符号以及常见中文字符。不过如果训练语料比较大以及词表比较大那这些应该也是包括了,只需要添加特殊词汇:

all_vocab = vocab + ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + other_alphabet

           在大语言模型时代,最常用的分词方法是Byte-Pair Encoding(BPE)和Byte-level BPE(BBPE)。Byte-Pair Encoding(BPE)最初是一种文本压缩算法在15年被引入到NLP用于分词,后续好多模型(例如GPT,RoBERTa等)都采用了这种分词方法。Byte-level BPE(BBPE)是于19年在BPE的基础上提出以Byte-level(字节)为粒度的分词方法。目前GPT2、BLOOM、Llama、Falcon等采用的是该分词方法。

3.2 Byte-Pair Encoding(BPE)

        Byte-Pair Encoding(BPE)核心思想是逐步合并出现频率最高的子词对而不是像Wordpiece计算合并分数,从而构建出一个词汇表,以下是核心步骤:

(1)计算初始词表:通过训练语料获得或者最初的英文中26个英文字母加上各种符号以及常见中文字符,这些作为初始词表。

(2)构建频率统计:统计所有子词单元对(两个连续的子词)在文本中出现的频率。

(3)合并频率最高的子词对:选择出现频率最高的子词对,将它们合并成一个新的子词单元,并更新词汇表。

(4)重复合并步骤:不断重复步骤(2)和步骤(3),直到达到预定的词汇表大小、合并次数,或者直到不再有有意义的合并(即进一步合并不会显著提高词汇表的效益)。

(5)分词:使用最终得到的词汇表对文本进行分词。

        BPE理论上还是会出现OOV的,当词汇表的大小受限时,一些较少频繁出现的子词和没有在训练过程中见过的子词,就会无法进入词汇表出现OOV,而Byte-level BPE(BBPE)理论上是不会出现这个情况的。

3.3 Byte-level BPE(BBPE)

Unicode:Unicode是一种字符集,旨在涵盖地球上几乎所有的书写系统和字符。它为每个字符分配了一个唯一的代码点用于标识字符。Unicode不关注字符在计算机内部的具体表示方式,而只是提供了一种字符到代码点的映射。Unicode的出现解决了字符集的碎片化问题,使得不同的语言和字符能够在一个共同的标准下共存。然而,Unicode并没有规定如何在计算机内存中存储和传输这些字符。

UTF-8:UTF-8是一种变长的字符编码方案,它将Unicode中的代码点转换为字节序列。UTF-8的一个重要特点是它是向后兼容ASCII的,这意味着标准的ASCII字符在UTF-8中使用相同的字节表示,从而确保现有的ASCII文本可以无缝地与UTF-8共存。在UTF-8编码中,字符的表示长度可以是1到4个字节,不同范围的Unicode代码点使用不同长度的字节序列表示,这样可以高效地表示整个Unicode字符集。UTF-8的编码规则是:

· 单字节字符(ASCII范围内的字符)使用一个字节表示,保持与ASCII编码的兼容性。

· 带有更高代码点的字符使用多个字节表示。UTF-8使用特定的字节序列来指示一个字符所需的字节数,以及字符的实际数据。

        例如,英文字母”A“的Unicode代码点是U+0041,在UTF-8中表示为0x41(与ASCII编码相同);而中文汉字”你“的Unicode代码点是U+4F60,在UTF-8中表示为0xE4 0xBD 0xA0三个字节的序列。所以简单来说:

Unicode是字符集,为每个字符分配唯一的代码点。

UTF-8是一种基于Unicode的字符编码方式,用于在计算机中存储和传输字符。

        计算机存储和处理数据时,字节是最小的单位。一个字节包含8个(Bit)二进制位,每个位可以是0或1,每位的不同排列组合可以表示不同的数据,所以一个字节能表示的范围是256个。

        综上,Byte-level BPE(BBPE)和Byte-Pair Encoding(BPE)区别为:BPE的的最小字符是字符级别,而BBPE是字节级别的。通过UTF-8的编码方式这一个字节的256的范围,理论上可以表示这个世界上所有的字符。所以实现的步骤和BPE就是实现的粒度不一样,其他都是一样的。

(1)计算初始词表:通过训练语料获得或者最初的英文中26个英文字母加上各种符号以及常见中文字符,这些作为初始词表。

(2)构建频率统计:统计所有子词单元对(两个连续的子词)在文本中出现的概率。

(3)合并频率最高的子词对:选择出现频率最高的子词对,将它们合并成一个新的子词单元,并更新词汇表。

(4)重复合并步骤:不断重复步骤(2)和步骤(3),直到达到预定的词汇表大小、合并次数,或者直到不再有有意义的合并(即进一步合并不会显著提高词汇表的效益)。

(5)分词:使用最终得到的词汇表对文本进行分词。

参考博文:NLP领域中的token和tokenization到底指的是什么? - 知乎

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值