Using Transformers – Tokenizers

分词器是我们在使用 transformers 时一个重要的核心组件,它的主要职责就是将输入的原始文本转换为模型需要的格式,例如:对输入的文本添加开始和结束符、填充、计算掩码、转换为索引等等。

1. Tokenizer 使用

一段输入的纯文本会经历如下的流程,最终变成模型要求的输入:

  1. 根据词表拆分输入内容
  2. 将拆分后的内容转换为数值表示

示例代码如下:

from transformers import BertTokenizer


if __name__ == '__main__':

    # 1. 加载预训练模型分词器
    tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')

    # 2. 对输入文本分词
    inputs = '我是中国人'
    inputs = tokenizer.tokenize(inputs)
    # 输出: ['我', '是', '中', '国', '人']
    print(inputs)

    # 3. 将分词后的内容转换为索引表示
    inputs = tokenizer.convert_tokens_to_ids(inputs)
    print(inputs)

程序输出结果:

['我', '是', '中', '国', '人']
[2769, 3221, 704, 1744, 782]

上面的输出内容中,并没有给输入添加开始和结束标记,也没有构造出 token_type_ids 和 attention_mask,如果我们希望构造完整的输入内容,可以使用 encode、encode_plus、batch_encode_plus 方法,它们的区别如下:

  1. encode 会给输入添加开始和结束标记,并转换为索引表示;
  2. encode_plus 不仅仅完成 encode 的功能,还会构建 token_type_ids 和 attention_mask;
  3. batch_encode_plus 适合对批量的输入进行 encode_plus 操作。

示例代码如下:

from transformers import BertTokenizer


if __name__ == '__main__':

    tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')

    inputs = '我是中国人'
    inputs = tokenizer.encode(inputs)
    print('encode:', inputs)

    inputs = tokenizer.encode_plus(inputs)
    print('encode_plus:', inputs)

    inputs = ['我是中国人', '我爱我的国家']
    inputs = tokenizer.batch_encode_plus(inputs)
    print('batch_encode_plus:', inputs)

程序输出内容如下:

encode: [101, 2769, 3221, 704, 1744, 782, 102]

encode_plus: {'input_ids': [101, 101, 2769, 3221, 704, 1744, 782, 102, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}

batch_encode_plus: {'input_ids': [[101, 2769, 3221, 704, 1744, 782, 102], [101, 2769, 4263, 2769, 4638, 1744, 2157, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]}

最后,简单一点的话,可以直接调用 tokenizer 对象的 __call__ 方法来完成转换,它会自动识别传入的是单个文本还是批量文本,并将其转化为模型需要的输出,示例代码如下:

from transformers import BertTokenizer

if __name__ == '__main__':

    tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')

    inputs = '我是中国人'
    inputs = tokenizer(inputs)
    print('tokenizer:', inputs)

    inputs = ['我是中国人', '我爱我的国家']
    inputs = tokenizer(inputs)
    print('tokenizer:', inputs)

程序输出内容如下:

tokenizer: {'input_ids': [101, 2769, 3221, 704, 1744, 782, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1]}

tokenizer: {'input_ids': [[101, 2769, 3221, 704, 1744, 782, 102], [101, 2769, 4263, 2769, 4638, 1744, 2157, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]}

最后,如果我们想把 ids 转换为文本表示,可以使用 decode 方法,如下代码所示:

from transformers import BertTokenizer

if __name__ == '__main__':

    tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')

    # 将某个 id 转换为 token
    print(tokenizer.convert_ids_to_tokens(2769))

    # 将 ids 转换为 tokens
    inputs = [101, 2769, 3221, 704, 1744, 782, 102]
    # skip_special_tokens=True 将会去除特殊标记,例如开始和结尾的特殊标记
    inputs = tokenizer.decode(inputs, skip_special_tokens=True)
    print(inputs, len(inputs))

程序输出结果如下:

我
我 是 中 国 人 9

注意:输出的结果带有空格,

2. Tokenizer 构建

我们可以预先构建自己一个 vocab file,然后构建包含自己词典的 Tokenizer,将并将其应用到后面的输入转换操作中。

# 1. 使用自己的词典文件
def test01():

    tokenizer = BertTokenizer(vocab_file='vocab.txt')
    inputs = ['不畏鸿门传汉祚', '新居落成创业始']

    outputs = tokenizer(inputs)
    print(outputs)

    # 存储 Tokenizer 对象
    tokenizer.save_pretrained('my-vocab')


# 2. 加载词典对象
def test02():

    # 指定词典路径
    tokenizer = BertTokenizer.from_pretrained('my-vocab')
    inputs = ['不畏鸿门传汉祚', '新居落成创业始']

    outputs = tokenizer(inputs)
    print(outputs)

    # 存储 Tokenizer 对象
    tokenizer.save_pretrained('my-vocab')


if __name__ == '__main__':

    test01()
    test02()

Tokenizer 存储到磁盘上有 3 个文件,分别是:

  1. special_tokens_map.json 存储一些特殊的 token,例如:[UNK]、[SEP] 等
  2. tokenizer_config.json 存储 Tokenizer 的配置信息
  3. vocab.txt 词典文件

当我们下次使用的时候,直接使用 from_pretrained 从指定路径下加载这三个文件即可,不需要重新使用词典构建对象。程序输出结果:

{'input_ids': [[5, 21, 1836, 606, 113, 135, 330, 2685, 4], [5, 35, 373, 96, 68, 545, 122, 737, 4]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1]]}

{'input_ids': [[5, 21, 1836, 606, 113, 135, 330, 2685, 4], [5, 35, 373, 96, 68, 545, 122, 737, 4]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1]]}
未经允许不得转载:一亩三分地 » Using Transformers – Tokenizers