【问题标题】:When doing pre-training of a transformer model, how can I add words to the vocabulary?在对 Transformer 模型进行预训练时,如何将单词添加到词汇表中?
【发布时间】:2022-02-11 18:07:21
【问题描述】:

鉴于给定语言的 DistilBERT 训练语言模型(取自 Huggingface 中心),我想在特定领域预训练模型,并且我想添加以下新词:

  • 在原始训练集中绝对不存在
  • 并且无法通过词片toeknization 处理 - 基本上您可以将这些词视为“代码”,它们是命名实体的规范化形式

考虑一下:

  • 我想避免学习 new 分词器:我可以添加新词,然后让模型通过预训练学习它们的嵌入
  • “单词”的数量远大于“库存”词汇表中“未使用”的标记

我发现的唯一建议是报告的here

将其附加到词汇的末尾,并编写一个脚本,该脚本生成一个与预训练的检查点相同的新检查点,但具有更大的词汇,其中新嵌入是随机初始化的(对于初始化,我们使用了 tf .truncated_normal_initializer(stddev=0.02))。这可能需要处理一些 tf.concat() 和 tf.assign() 调用。

你认为这是实现我目标的唯一途径吗?

如果是,我不知道如何编写这个“脚本”:有人对如何进行(示例代码、文档等)有一些提示吗?

【问题讨论】:

  • 您是在“从头开始”进行预训练吗?还是从现有检查点继续预训练?

标签: pytorch huggingface-transformers bert-language-model


【解决方案1】:

根据我的评论,我假设您使用预先训练的检查点,即使只是为了“避免 [学习] 新的标记器”。 此外,该解决方案适用于 PyTorch,它可能更适合此类更改。我还没有检查过 Tensorflow(在你的一个引用中提到过),所以不能保证它可以跨平台工作。
为了解决您的问题,让我们将其分为两个子问题:

  • 将新标记添加到标记器中,并且
  • 相应地重新调整模型的令牌嵌入矩阵的大小。

第一个实际上可以通过使用.add_tokens() 非常简单地实现。我引用了慢速分词器的实现(因为它是在 Python 中),但据我所知,这也适用于更快的基于 Rust 的分词器。

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
# Will return an integer corresponding to the number of added tokens
# The input could also be a list of strings instead of a single string
num_new_tokens = tokenizer.add_tokens("dennlinger")  

您可以通过查看编码的输入 id 来快速验证这是否有效:

print(tokenizer("This is dennlinger."))
# 'input_ids': [101, 2023, 2003, 30522, 1012, 102]

索引30522 现在与我的用户名对应的新令牌,所以我们可以检查第一部分。但是,如果我们查看.add_tokens() 的函数文档字符串,它也会说:

注意,当向词汇表添加新标记时,您应该确保还调整模型的标记嵌入矩阵的大小,使其嵌入矩阵与标记器匹配。
为此,请使用PreTrainedModel.resize_token_embeddings 方法。

this particular function,描述有点混乱,但是我们可以得到一个正确调整大小的矩阵(新令牌的权重随机初始化),只需传递之前的模型大小,加上新令牌的数量:

from transformers import AutoModel

model = AutoModel.from_pretrained("distilbert-base-uncased")
model.resize_token_embeddings(model.config.vocab_size + num_new_tokens)

# Test that everything worked correctly
model(**tokenizer("This is dennlinger", return_tensors="pt"))

编辑:值得注意的是,.resize_token_embeddings() 还负责处理任何相关的权重;这意味着,如果您正在进行预训练,它还将调整语言建模头的大小(应该具有相同数量的标记),或修复可能受标记数量增加影响的绑定权重。

【讨论】:

  • 我只想补充一点,如果使用 Huggingface repo (raw.githubusercontent.com/huggingface/transformers/master/…) 中的 run_mlm.py,则只需将语句 tokenizer.add_tokens() 添加到脚本中,假设 model.resize_token_embeddings() 已被脚本调用。
  • 我在使用通过上述方法添加的令牌训练的模型时遇到问题:一切似乎都很好,但是当我调用时:AutoTokenizer.from_pretrained 又在某些时候调用:fast_tokenizer = TokenizerFast。 from_file(fast_tokenizer_file) CPU 运行 100%,它似乎卡在了某个地方,几分钟后仍在运行。 fast_tokenizer_file 是 26MB,看起来是正确的 - @dennlinger 有什么提示吗?调试器无法介入(可能代码转入了某个 C 模块?我不是真正的 Python 专家……)
  • 写了上面的 issue,我也在 ? Github 上提交了一个 issue:github.com/huggingface/transformers/issues/15733
  • 我认为我找到了罪魁祸首 - 请参阅上面的 GitHub 问题。
猜你喜欢
  • 2022-11-02
  • 2019-01-16
  • 1970-01-01
  • 2019-09-25
  • 2020-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多