我们在使用 transformers 时,需要使用自己的数据来构建 tokenizer。这里我们使用 tokenizer 库,该库可以帮我们更加轻松的构建不同类型的 Tokenizer。安装命令如下:
pip install tokenizer
训练一个分词器,我们需要经过以下几个步骤的工作:
- normalization
- pre-tokenization
- model
- 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 主要有以下几类:
- models.BPE
- models.Unigram
- models.WordLevel
- 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)], )