tokenizer
tokenizer 的主要功能可以用 tokenize 和 encode 来描述。tokenize 将文本切分为 token,encode 则将(已切分或未切分的) token 按照词表转换为(或者说映射到)数值 id。tokenizer 的tokenize
和 encode
分别实现上述功能, __call__
方法基本上是 encode,并返回包含更多的控制信息的 BatchEncoding
对象。
当 __call__
接收一个 List[str]
作为参数时,它默认会把参数理解为一个文本 batch,所以如果传入的是已切分的 token 序列,就需要设置 is_split_into_words
参数为真。但 encode
方法并不需要,它默认把 List[str]
理解为已切分的 token 序列。
encode
有一个反向的方法 decode
,可以把已编码的 id 序列还原为文本字符串。但注意 tokenize 本身不是可逆的,tokenize 包含大小写归一化、OOV 单词处理等过程。对于 BERT tokenizer 等会做 subword 切分的 tokenizer,decode
可以帮助省去 subword 拼接的过程;decode
也会对标点进行合适的处理。
由于 token 既可以抽象地指代文本处理中的最小对象(对于 tokenizer 来说,一般就是 word 或 subword),也可以指代具体的字符串形式的(采用字符编码的) 最小对象,本文可能会在这两种意义上模糊地使用 token 这个词,但请留意这一概念上的分歧。
BatchEncoding
对象继承自 python dict。
tokenizer 是支持对一个 batch 的文本做 tokenize 和 encode 的。
attention_mask
是一个对文本做 batching 时会用到的参数。假设有若干长度不同的序列要放入同一个 batch,并且不做截断,那么只能对短的序列做 padding。但对于 BERT 这样的基于 attention 的非自回归的模型而言,输入连同 padding 会一起被 attention 机制作用,而我们不期望 padding 信息造成干扰。通过 attention mask 可以告诉模型,输入数据的哪些部分不需要参与 attention 计算(例如 padding 的部分)。直接调用 tokenizer 得到的 BatchEncoding
里默认包含 attention_mask
。它是一个和产生的 id 序列长度相同的 01 序列,1 代表对应位置的 token 将参与 attention 运算,0 代表相反。
如果希望把序列 padding 到一个统一的长度,可以考虑 padding
参数。它不仅支持把一个 batch 内的 sequence 补齐到 batch 内最长 sequence 的长度,也支持补齐到一个给定的任意长度。通过 truncation
参数则可以执行截断到给定长度。这个给定长度由参数 max_length
控制。
直接调用 tokenizer 返回的 id 序列默认是一个 python list。通过 return_tensors
参数可以要求 tokenizer 返回特定数值框架中的数值类型。pt
代表 PyTorch 的 Tensor
,tf
代表 TensorFlow 的 Tensor
,np
代表 NumPy 的 ndarray
。
# load a tokenizer
global_tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
hamlet = "To be or not to be, that is the question."
# tokenize, encode and decode
result = global_tokenizer.tokenize(hamlet)
print(result)
['to', 'be', 'or', 'not', 'to', 'be', ',', 'that', 'is', 'the', 'question', '.']
result = global_tokenizer.encode(result)
print(result)
[101, 2000, 2022, 2030, 2025, 2000, 2022, 1010, 2008, 2003,
1996, 3160, 1012, 102]
result = global_tokenizer.decode(result)
print(repr(result))
'[CLS] to be or not to be, that is the question. [SEP]'
# subword example
hysteria = "a psychoneurosis marked by emotional excitability and disturbances " \
"of the psychogenic, sensory, vasomotor, and visceral functions"
result = global_tokenizer.tokenize(hysteria)
print(result)
['a', 'psycho', '##ne', '##uro', '##sis', 'marked', 'by', 'emotional',
'ex', '##cit', '##ability', 'and', 'disturbances', 'of', 'the', 'psycho',
'##genic', ',', 'sensory', ',', 'va', '##som', '##oto', '##r', ',', 'and',
'vis', '##cera', '##l', 'functions']
# directly encoding is supported
result = global_tokenizer.encode(hamlet)
print(result)
[101, 2000, 2022, 2030, 2025, 2000, 2022, 1010, 2008, 2003,
1996, 3160, 1012, 102]
result = global_tokenizer(hamlet)
print(result)
{'input_ids': [101, 2000, 2022, 2030, 2025, 2000, 2022, 1010, 2008, 2003,
1996, 3160, 1012, 102],
'token_type_ids': [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]}
# Tokenized text is not properly encoded.
# Every token is treated as an individual sentence.
result = global_tokenizer.tokenize(hamlet)
result = global_tokenizer(result)
print(result)
{'input_ids': [[101, 2000, 102], [101, 2022, 102], [101, 2030, 102], ...
result = global_tokenizer.tokenize(hamlet)
# use of is_split_into_words
result = global_tokenizer(result, is_split_into_words=True)
print(result)
{'input_ids': [101, 2000, 2022, 2030, 2025, 2000, 2022, 1010, 2008, 2003,
1996, 3160, 1012, 102],
'token_type_ids': [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]}
# use of padding, truncation and max_length
result = global_tokenizer(hamlet,padding='max_length', max_length=20)
print(result)
{'input_ids': [101, 2000, 2022, 2030, 2025, 2000, 2022, 1010, 2008, 2003,
1996, 3160, 1012, 102, 0, 0, 0, 0, 0, 0],
'token_type_ids': [0, 0, 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, 0, 0, 0, 0, 0, 0]}
result = global_tokenizer(hamlet, truncation=True, max_length=10)
print(result)
{'input_ids': [101, 2000, 2022, 2030, 2025, 2000, 2022, 1010, 2008, 102],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
# use of return_tensors
result = global_tokenizer(hamlet, return_tensors="pt")
print(result)
{'input_ids': tensor([[ 101, 2000, 2022, 2030, 2025, 2000, 2022, 1010,
2008, 2003, 1996, 3160, 1012, 102]]),
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
# additional flags to control returned value
result = global_tokenizer(hamlet,
return_token_type_ids=False,
return_attention_mask=False)
print(result)
{'input_ids': [101, 2000, 2022, 2030, 2025, 2000, 2022, 1010, 2008, 2003,
1996, 3160, 1012, 102]}
Reference
[1] https://huggingface.co/transformers/main_classes/tokenizer.html
Last updated
Was this helpful?