我们在使用 transformers 时,需要使用自己的数据来构建 tokenizer。这里我们使用 tokenizer 库,该库可以帮我们更加轻松的构建不同类型的 Tokenizer。安装命令如下:
pip install tokenizer
训练一个分词器,我们需要经过以下几个步骤的工作:
-
normalization
-
pre-tokenization
-
model
-
post-processing
-
normalization {#title-0} ===========================
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()
- pre-tokenization {#title-1} ==============================
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()
- model {#title-2} ===================
当对输入的文本进行 normalization 和 pre-tokenization 之后,将会使用不同的算法的分词器,并对我们自己的语料进行训练, 给每个 Token 分配一个唯一的数字 ID,Model 主要有以下几类:
-
models.BPE
-
models.Unigram
-
models.WordLevel
-
models.WordPiece
-
post-processing {#title-3} =============================
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)],
)