我們現在需要對一句話做分詞,然后轉換成index,作為Bert的輸入
tokenizer = BertTokenizer.from_pretrained(config.PRE_TRAINED_MODEL_NAME) text = '天空下着暴雨,o2正在給c1穿雨衣,他自己卻只穿着單薄的軍裝,完全暴露在大雨之中。角色: o2' input_ids =tokenizer.encode(text) output = tokenizer.decode(input_ids)
輸出的output長這樣,長度45:
'[CLS] 天 空 下 着 暴 雨 , o2 正 在 給 c1 穿 雨 衣 , 他 自 己 卻 只 穿 着 單 薄 的 軍 裝 , 完 全 暴 露 在 大 雨 之 中 。 角 色 : o2 [SEP]'
但是input_ids的長度是47,??難道不是一一對應
確實不是,一一與詞匯表對比,發現 'o2' 對應了兩個 id,'o'對應一個, '##2'對應一個(在bert的詞匯表里'##x'通常表示以'x'為后綴)
神奇的是 'c1' 詞匯表中是有的
也就是說,為了讓token和id一一對應起來,我們需要將不存在的token添加到vocab中,網上查到兩種方法:
1. 利用前100里的[unused]
https://github.com/google-research/bert/issues/396
將[unused]換成自己想要添加的
具體有多少個[unused]要看自己的預訓練模型,可能100個,可能1000個,但都有限
2. 利用transformers庫里有個 add_tokens 方法
https://github.com/huggingface/transformers/issues/1413
https://blog.csdn.net/qq_23944915/article/details/102781463
import torch from transformers import BertTokenizer, BertModel import ipdb import config tokenizer = BertTokenizer.from_pretrained(config.PRE_TRAINED_MODEL_NAME) model = BertModel.from_pretrained(config.PRE_TRAINED_MODEL_NAME) x = model.embeddings.word_embeddings.weight[-1, :] print(len(tokenizer)) # 28996 tokenizer.add_tokens(["NEW_TOKEN"]) print(len(tokenizer)) # 28997 model.resize_token_embeddings(len(tokenizer)) # The new vector is added at the end of the embedding matrix print(model.embeddings.word_embeddings.weight[-1, :]) # Randomly generated matrix with torch.no_grad(): model.embeddings.word_embeddings.weight[-1, :] = torch.zeros([model.config.hidden_size]) print(model.embeddings.word_embeddings.weight[-1, :]) # outputs a vector of zeros of shape [768] y = model.embeddings.word_embeddings.weight[-2, :] print(x == y) # 會改變原來embedding weight 嗎? 不會 ipdb.set_trace()
這種方法,因為是添加token,需要修改embedding matrix
不知道它這個resize matrix會不會打擾預訓練的embeddings? 經測,不會
感覺兩種都行,待測
也有可能都沒必要,Bert可能能夠學會新的組合詞匯