在NLP领域,基于公开语料的预训练模型,在专业领域迁移时,会遇到专业领域词汇不在词汇表的问题,本文介绍如何添加专有名词到预训练模型。
例如,在bert预训练模型中,并不包含财经词汇,比如‘市盈率’等财务指标词汇,本文将介绍:
NLP的分词
NLP的处理流程:
其中,预训练模型是在公开语料上训练的,我们在做迁移学习,把模型迁移到财经领域时,会面临的一个问题,就是财经词汇不在词汇表,会被拆分成单个字,从而会导致专业名词的完整意思的破坏,或者让模型去学习时,不那么直观,比如:
from transformers import AutoTokenizer,AutoModel
PRE_TRAINED_MODEL_NAME='xlm-roberta-base'
tokenizer = AutoTokenizer.from_pretrained(PRE_TRAINED_MODEL_NAME)
e = tokenizer.encode('华为的市盈率较高')
s = [tokenizer.decode(i) for i in e]
print(s)
# ['<s>', '', '华为', '的', '市', '盈', '率', '较高', '</s>']
tokenizer.add_tokens(["市盈率"])
e2 = tokenizer.encode('华为的市盈率较高')
s2 = [tokenizer.decode(i) for i in e2]
print(s2)
# ['<s>', '', '华为', '的', '市盈率', '', '较高', '</s>']
有两种实现
import torch
from transformers import AutoTokenizer,AutoModel
PRE_TRAINED_MODEL_NAME='xlm-roberta-base'
tokenizer = AutoTokenizer.from_pretrained(PRE_TRAINED_MODEL_NAME)
model = AutoModel.from_pretrained(PRE_TRAINED_MODEL_NAME)
print(len(tokenizer)) # 250002
tokenizer.add_tokens(["NEW_TOKEN"])
print(len(tokenizer)) # 250003
新词汇的加入,势必会不适配原始embedding的维度,原始的embedding维度为[vocab_size,hidden_size],但是我们又不可能重新去训练整个embedding,我们想尽量保留原始embedding参数,因此,这里比较巧妙的运用了reshape技巧,人为添加新词汇的embedding(随机的,没学习),然后使用领域材料进行学习。
这种方法,因为是添加token,需要修改embedding matrix。
实验证明resize matrix不会打扰原始预训练的embeddings。
import torch
from transformers import AutoTokenizer,AutoModel
PRE_TRAINED_MODEL_NAME='xlm-roberta-base'
tokenizer = AutoTokenizer.from_pretrained(PRE_TRAINED_MODEL_NAME)
model = AutoModel.from_pretrained(PRE_TRAINED_MODEL_NAME)
print(len(tokenizer)) # 250002
tokenizer.add_tokens(["NEW_TOKEN"])
print(len(tokenizer)) # 250003
x = model.embeddings.word_embeddings.weight[-1, :] # 原始最后一个token的embedding
model.resize_token_embeddings(len(tokenizer)) # 调整embedding维度
# The new vector is added at the end of the embedding matrix
print(model.embeddings.word_embeddings.weight[-1, :])
# Randomly generated matrix 添加的embedding是随机值。
with torch.no_grad():
model.embeddings.word_embeddings.weight[-1, :] = torch.zeros([model.config.hidden_size])
# 人为设置新添加的embedding为0
print(model.embeddings.word_embeddings.weight[-1, :])
# outputs a vector of zeros of shape [768]
y = model.embeddings.word_embeddings.weight[-2, :] # 原始最后一个token变成倒数第二了,取其embedding
print(x == y) # 原始token的embedding会改变吗?原来embedding weight 不会变
e = tokenizer.encode('华为的市盈率较高')
s = [tokenizer.decode(i) for i in e]
print(s)
tokenizer.add_tokens(["市盈率"])
e2 = tokenizer.encode('华为的市盈率较高')
s2 = [tokenizer.decode(i) for i in e2]
print(s2)
有了这个初始embedding,经过MLM等任务,就可以训练新词汇的embedding表示了,通过下游任务来学习这个embedding。