以小模型gpt2为例:
先查看tokenizer从配置文件中加载的属性:
vocab_size【50256】
: Tokenizer 的词汇表大小pad_token【空】
,unk_token【
<|endoftext|>】
,bos_token【
<|endoftext|>】
,eos_token【
<|endoftext|>】
,mask_token【空】
: 分别对应填充标记、未知标记、序列开头标记、序列结尾标记、掩码标记。special_tokens_map
【
{'bos_token': '<|endoftext|>', 'eos_token': '<|endoftext|>', 'unk_token': '<|endoftext|>'}】
: 特殊标记到 token ID 的映射。added_tokens_encoder【
{'<|endoftext|>': 50256}】
,added_tokens_decoder【
{50256: AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=True, special=True)}】
: 添加的特殊标记到 token ID 和 token 的映射model_max_length【1024】
: Tokenizer 接受的最大长度。padding_side【
'right'】
,pad_token_id【空】
: 填充的位置、填充的 token ID、填充的 token。
# <|endoftext|>是词汇表中50256对应的token,同时也是初始化中唯一的默认特殊字符【added_tokens_decoder属性用于存储特殊字符】
---------------------------------- 验证 ------------------------------------
gpt2词汇表的总数是50257,里面对应token字符--id,下标从0-50256
tokenizer.vocab_size ----- 50257 # 词汇表数
len(tokenizer) ------ 50257 # 词汇数+额外添加的特殊字符【不包括默认|endoftext|】
tokenizer.convert_ids_to_tokens(50256) ------ <|endoftext|> # 50256对应的字符
# config.json配置文件包含两个特殊字符的属性eos_token和bos_token,并且将它们的特殊字符对应id设置为50256。
这里只是将两个属性的token_id值设置为50256,访问属性返回对应id映射的token,但这并没添加额外的特殊字符,没太大意义
# tokenizer加载的属性/根据id返回token
tokenizer.eos_token = '<|endoftext|>'
tokenizer.bos_token = '<|endoftext|>'
#
tokenizer.convert_tokens_to_ids('<|endoftext|>') # 50256
tokenizer.convert_ids_to_tokens(50256) # '<|endoftext|>'
# 字符表以外的token,转换成id返回的值均为50256。
tokenizer.convert_tokens_to_ids('<|EOS|>') # 50256
tokenizer.convert_tokens_to_ids('<|BOS|>') # 50256
tokenizer.convert_tokens_to_ids('<|KKKKK|>') # 50256
tokenizer.convert_tokens_to_ids('<|ABCDEF|>') # 50256
tokenizer.convert_tokens_to_ids('v50KFCGOOD') # 50256
# 当然,像上面这种如果没有添加新的token为 <|EOS|> 时,在tokenize()分词时就会被分割成 < | E O S | >,更不会进行到后面的convert_tokens_to_id这一步!
------------------------------------ 如何添加特殊字符避免被拆分? ---------------------------------
tokenizer.add_special_tokens({'pad_token':"<|PAD|>"})
tokenizer.add_special_tokens({'eos_token':"<|EOS|>"})
tokenizer.add_special_tokens({'bos_token':"<|BOS|>"})
add_special_toen添加特殊字符,key值是上面tokenizer的特殊字符属性,value是分割成整体的字符
添加的字符添加到属性added_tokens_encoder中,并扩充特殊字符词汇表,当然,原词汇表大小不会变
len(tokenizer) = tokenizer.vocab_size + special_token【添加的特殊字符】
----------------------------------------------------------------------------------------------------------------
解释一下 为什么有时候分词器会要求设置tokenizer.pad_token = tokenizer.eos_token?
这样处理往往是针对batch操作的,generate是允许batch处理,因此无论如何必须先设置好pad_token,默认pad_toekn是空,那怎么填充?往往会将属性pad_toekn设置成eos_token同一token,填充成<|endoftext|>,在convert_tokens_to_ids再转化成50256
-------------------------------------------------------------------------------------------------------------
添加的特殊字符有什么用?
在问答训练时会添加特殊字符框架协同训练,经过微调模型就知道这样的框架是在做问答,在推理时,只要将文本加入特殊字符组成的问答框架就可以进行回答。
同时,使用特殊字符,也能帮助模型训练时区分每一个句子间的层次关系。