Huggingface | 修改模型的embedding

发布时间 2023-07-29 18:32:52作者: 张Zong在修行

目标:

在NLP领域,基于公开语料的预训练模型,在专业领域迁移时,会遇到专业领域词汇不在词汇表的问题,本文介绍如何添加专有名词到预训练模型。

NLP的处理流程:

  1. 对输入的句子进行分词,得到词语及下标
  2. 通过embedding层获得词语对应的embedding
  3. embedding送入到预训练模型,经过attention注意力机制,获得token在句子中的语义embedding向量
  4. 利用语义embedding向量,构建下游任务。

其中,预训练模型是在公开语料上训练的,我们在做迁移学习,把模型迁移到财经领域时,会面临的一个问题,就是财经词汇不在词汇表,会被拆分成单个字,从而会导致专业名词的完整意思的破坏,或者让模型去学习时,不那么直观

在tokenizer中添加新词汇

    1. 在vocab.txt中,利用前100里的[unused],将[unused]换成自己想要添加的。具体有多少个[unused]要看自己的预训练模型,可能100个,可能1000个,但都有限。如果要添加的词汇量小,并且预训练模型确实有vocab.txt文件,则可以,比如bert,目前看对于领域来说,量不够大。
    1. 更加通用的办法:通过tokenizer,向词汇表中追加新的专业词汇,没有不限。特别是现在比较复杂的模型,都没有单独的vocab.txt文件了,只能通过这种方式。
from transformers import AutoModel, AutoTokenizer

# 加载预训练模型和tokenizer
model = AutoModel.from_pretrained("./bert-base-uncased")
tokenizer = AutoTokenizer.from_pretrained("./bert-base-uncased")

# 添加新词汇到tokenizer的词汇表中
print(len(tokenizer)) # 30522
tokenizer.add_tokens(["newword","awdddd"])
print(len(tokenizer)) # 30524

新词汇embedding的生成

新词汇的加入,势必会不适配原始embedding的维度,原始的embedding维度为[vocab_size,hidden_size],但是我们又不可能重新去训练整个embedding,我们想尽量保留原始embedding参数,因此,这里比较巧妙的运用了reshape技巧,人为添加新词汇的embedding(随机的,没学习),然后使用领域材料进行学习。

这种方法,因为是添加token,需要修改embedding matrix。

实验证明resize matrix不会打扰原始预训练的embeddings。

from transformers import AutoModel, AutoTokenizer

# 加载预训练模型和tokenizer(重要)
model = AutoModel.from_pretrained("./bert-base-uncased")
tokenizer = AutoTokenizer.from_pretrained("./bert-base-uncased")

# 添加新词汇到tokenizer的词汇表中(重要)
print(len(tokenizer))
tokenizer.add_tokens(["newword","awdddd"])
print(len(tokenizer))

x = model.embeddings.word_embeddings.weight[-1, :]

# 扩展模型的嵌入矩阵,以包含新词汇的嵌入向量(重要)
model.resize_token_embeddings(len(tokenizer))

y = model.embeddings.word_embeddings.weight[-2, :]

z = model.embeddings.word_embeddings.weight[-1, :]

x1 = model.embeddings.word_embeddings.weight[-3, :]
 
print(y.shape) # torch.Size([768])
print(z.shape) # torch.Size([768])
print(x == x1) # 原始token的embedding会改变吗?原来embedding weight 不会变

# 使用新的嵌入向量初始化新词汇的嵌入向量
a = model.get_input_embeddings()
print(a) # Embedding(30524, 768)

tok = tokenizer.convert_tokens_to_ids(["newword"])
print(tok) # [30522]


# 保存微调后的模型和tokenizer(重要)
model.save_pretrained("./gaibian")
tokenizer.save_pretrained("./gaibian")

生成的文件,多了一个added_tokens.json

有了这个初始embedding,经过MLM等任务,就可以训练新词汇的embedding表示了,通过下游任务来学习这个embedding。

huggingface中的added_tokens.json文件

在 Hugging Face 中,added_tokens.json 文件是一个 JSON 格式的文件,用于存储添加到 tokenizer 中的特殊标记(special tokens)。

使用 Hugging Face 的 tokenizer 时,我们可以通过 tokenizer.add_tokens() 方法向 tokenizer 中添加自定义的特殊标记。添加的特殊标记会被保存在 added_tokens.json 文件中,以便在下次使用 tokenizer 时重新加载。

如果您想添加自定义的特殊标记,请确保将其添加到 added_tokens.json 文件中,并使用 tokenizer.add_tokens() 方法将其添加到 tokenizer 中。

加载训练后的模型

from transformers import AutoModel, AutoTokenizer

# 加载预训练模型和tokenizer
model = AutoModel.from_pretrained("./gaibian")
tokenizer = AutoTokenizer.from_pretrained("./gaibian")

print(model) # 模型的框架
print("=====================")
tok1 = tokenizer.convert_tokens_to_ids(["newword"])
tok2 = tokenizer.convert_tokens_to_ids(["awdddd"])
print(tok1) # [30522]
print(tok2) # [30523]

说明可以加载。