Tokenization Pipeline

我们在使用 transformers 时,需要使用自己的数据来构建 tokenizer。这里我们使用 tokenizer 库,该库可以帮我们更加轻松的构建不同类型的 Tokenizer。安装命令如下:

pip install tokenizer

训练一个分词器,我们需要经过以下几个步骤的工作:

  1. normalization
  2. pre-tokenization
  3. model
  4. post-processing

1. normalization

Normalization 对输入的字符串进行一些去除空格、像法语这种语言的重音符号、小写转换等等,当然也可以有其他的一些规范化操作。目的就是使文本不那么随机或更干净。下面为使用的示例代码:

from tokenizers import Tokenizer
from tokenizers.normalizers import BertNormalizer
from tokenizers.normalizers import Lowercase
from tokenizers.normalizers import Strip
from tokenizers.normalizers import Replace
from tokenizers import Regex
from tokenizers.normalizers import Sequence

def test():

    # clean_text 设置 True 时,会去除输入文本中的控制字符(\t \n \t 等)
    # handle_chinese_chars 设置 True 时,会在每一个汉字两侧添加空格
    # strip_accents 去除重音符号,中文中不包含重音符号,像法语中就有重音符号
    # lowercase 将字母小写
    normalizer = BertNormalizer(clean_text=True, handle_chinese_chars=True, strip_accents=None, lowercase=True)
    my_str = ' ABCd    我  \t 是\t中国      \n    人 Entrées et sorties'
    my_str = normalizer.normalize_str(my_str)
    # 输出: " abcd     我      是   中  国             人  entrees et sorties"
    print(my_str)

    # 将字符转换为小写
    normalizer = Lowercase()
    my_str = normalizer.normalize_str('Hello World')
    # 输出: "hello world"
    print(my_str)

    # 去除字符串两侧空格
    normalizer = Strip(left=True, right=True)
    my_str = normalizer.normalize_str('  我爱你  ')
    # 输出: "我爱你"
    print(my_str)

    # 替换某些字符, 支持正则表达式
    normalizer = Replace(pattern=Regex('[^\u4e00-\u9fa5]'), content='A')
    my_str = normalizer.normalize_str('我爱你 I Love You!')
    # 输出: "我爱你AAAAAAAAAAAA"
    print(my_str)

    # 使用多个标准化流程
    norm1 = BertNormalizer()
    norm2 = Lowercase()
    norm3 = Strip()
    norm4 = Replace(pattern=Regex('[ ]+'), content=' ')

    normalizer = Sequence([norm1, norm2, norm3, norm4])
    my_str = normalizer.normalize_str(' ABCd    我  \t 是\t中国      \n    人 Entrées et sorties     ')
    # 输出: "abcd 我 是 中 国 人 entrees et sorties"
    print(my_str)


if __name__ == '__main__':
    test()

2. pre-tokenization

Pre-Tokenization 预标记的目的就是将输入的文本拆分为更小的对象,其实就是分词了,对于中文的话,可以以词的粒度拆分,也可以以字的粒度拆分,对于英文的话,也可以以词的粒度、字符的粒度拆分。总之,就是通过不同的方法将文本进行分词。

from tokenizers.pre_tokenizers import BertPreTokenizer
from tokenizers.pre_tokenizers import ByteLevel
from tokenizers.pre_tokenizers import CharDelimiterSplit
from tokenizers.pre_tokenizers import Digits
from tokenizers.pre_tokenizers import Punctuation
from tokenizers.pre_tokenizers import Split
from tokenizers.pre_tokenizers import PreTokenizer
from tokenizers.pre_tokenizers import Sequence


def test():

    # 1. 以空格和标点符号来分词
    tokenizer = BertPreTokenizer()
    my_str = tokenizer.pre_tokenize_str('我是,中国 I Love You!')
    # 输出: [('我是', (0, 2)), (',', (2, 3)), ('中国', (3, 5)), ('I', (6, 7)), ('Love', (8, 12)), ('You', (13, 16)), ('!', (16, 17))]
    print(my_str)

    # 2. 字节级分词
    # 默认以空格、标点符号来分词,如果也会表示出空格,以Ġ作为空格
    # add_prefix_space 设置为 True, 如果第一个字符前面没有空格的话,额外添加 Ġ 表示
    tokenizer = ByteLevel(add_prefix_space=True)
    my_str = tokenizer.pre_tokenize_str('我是,中国')
    # 输出: [('ĠæĪijæĺ¯', (0, 2)), ('ï¼Į', (2, 3)), ('ä¸ŃåĽ½', (3, 5))]
    # 中文的是多字节,以字节分词的话会出现乱码,比较适合英文字符
    print(my_str)
    my_str = tokenizer.pre_tokenize_str('I Love You!')
    # 输出: [('ĠI', (0, 1)), ('ĠLove', (1, 6)), ('ĠYou', (6, 10)), ('!', (10, 11))]
    print(my_str)

    # 3. 以自己提供的字符作为分割符进行分词
    tokenizer = CharDelimiterSplit(delimiter='|')
    my_str = tokenizer.pre_tokenize_str('我|是|中国人')
    # 输出: [('我', (0, 1)), ('是', (2, 3)), ('中国人', (4, 7))]
    print(my_str)

    # 4. 以数字作为分割符进行分词
    # 如果 individual_digits 设置 True, 则以单个数字为分隔符
    tokenizer = Digits(individual_digits=False)
    my_str = tokenizer.pre_tokenize_str('我123是中456国人')
    # 输出: [('我', (0, 1)), ('123', (1, 4)), ('是中', (4, 6)), ('456', (6, 9)), ('国人', (9, 11))]
    print(my_str)

    # 5. 以标点符号作为分隔符进行分词
    tokenizer = Punctuation()
    my_str = tokenizer.pre_tokenize_str('我,是中!国人')
    # 输出: [('我', (0, 1)), (',', (1, 2)), ('是中', (2, 4)), ('!', (4, 5)), ('国人', (5, 7))]
    print(my_str)

    # 6. 其他
    # Whitespace
    # Split
    # 等等,也可以自定义

if __name__ == '__main__':
    test()


3. model

当对输入的文本进行 normalization 和 pre-tokenization 之后,将会使用不同的算法的分词器,并对我们自己的语料进行训练, 给每个 Token 分配一个唯一的数字 ID,Model 主要有以下几类:

  1. models.BPE
  2. models.Unigram
  3. models.WordLevel
  4. models.WordPiece

4. post-processing

Post-Processing 常见的操作就是添加一些特殊的 Token。例如对于 Bert 模型,如果输入的是单个句子,我们希望在句子开头和结束添加 [CLS] 和 [SEP] 特殊标记,如果输入的是句子对儿的话,我们希望在两个句子之间添加 [SEP] 在两侧提那家 [CLS] 和 [SEP] 等,这些可以由下面的代码来定义。

from tokenizers.processors import TemplateProcessing
tokenizer.post_processor = TemplateProcessing(
    single="[CLS] $A [SEP]",
    pair="[CLS] $A [SEP] $B:1 [SEP]:1",
    special_tokens=[("[CLS]", 1), ("[SEP]", 2)],
)
未经允许不得转载:一亩三分地 » Tokenization Pipeline