TensorFlow中固定预训练嵌入仅学新词嵌入却出现预定义词嵌入变更问题
嘿,我来帮你搞定这个问题!这种情况我之前也碰到过,大概率是你在冻结预训练嵌入的环节没做周全,或者模型的某个小细节让预训练参数偷偷被更新了。下面给你拆解几个常见的坑和对应的解决办法:
可能的原因及解决办法
1. 确认嵌入层参数的requires_grad设置精准
你不能直接把整个嵌入层设为不可训练,因为新词汇需要学习嵌入。正确的做法是拆分预训练参数和新词汇参数,只冻结前者:
比如在PyTorch里,假设你的嵌入矩阵是由预训练部分和新词汇部分拼接而成,pretrained_size是预训练词汇的总数:
# 初始化嵌入层 embedding = nn.Embedding(total_vocab_size, embed_dim) # 加载预训练嵌入到前半部分 embedding.weight.data[:pretrained_size] = torch.tensor(pretrained_embeddings) # 冻结预训练参数 embedding.weight[:pretrained_size].requires_grad = False # 确保新词汇参数可训练(默认就是True,这里可以显式声明) embedding.weight[pretrained_size:].requires_grad = True # 优化器只传入可训练的参数! optimizer = torch.optim.Adam([embedding.weight[pretrained_size:]], lr=1e-3)
这里的关键是优化器只接收可训练的参数组,避免冻结的参数被意外传入。
2. 排查是否在训练中意外修改了预训练嵌入
有时候可能是数据预处理出了问题:比如你的词汇表映射错误,把预训练词汇的索引当成了新词汇,导致模型在更新时误碰了预训练嵌入。
你可以做个小测试:训练前先保存几个预训练词汇的嵌入向量,训练2-3个epoch后再提取这些向量对比。如果确实变了,就去检查你的词汇表映射、数据加载环节,看看是不是索引对应关系出了错。
3. 框架特定的小坑(以TensorFlow/Keras为例)
如果用的是Keras,默认的Embedding层trainable参数是针对整个层的,没法单独冻结部分参数。这时候你需要自定义一个嵌入层,把预训练部分和新词汇部分彻底分开:
import tensorflow as tf from tensorflow.keras.layers import Layer class SplitEmbedding(Layer): def __init__(self, pretrained_embeds, num_new_tokens, **kwargs): super().__init__(**kwargs) # 预训练嵌入设为不可训练的常量 self.pretrained = tf.convert_to_tensor(pretrained_embeds, dtype=tf.float32) # 新词汇嵌入设为可训练参数 self.new_embeds = self.add_weight( shape=(num_new_tokens, pretrained_embeds.shape[1]), initializer="random_normal", trainable=True ) def call(self, inputs): # 区分预训练词汇和新词汇的索引 pretrained_mask = inputs < self.pretrained.shape[0] # 分别获取对应嵌入 pretrained_out = tf.nn.embedding_lookup(self.pretrained, inputs * tf.cast(pretrained_mask, tf.int32)) new_out = tf.nn.embedding_lookup(self.new_embeds, (inputs - self.pretrained.shape[0]) * tf.cast(~pretrained_mask, tf.int32)) # 合并结果:预训练词汇用预训练嵌入,新词汇用新嵌入 return tf.where(tf.expand_dims(pretrained_mask, -1), pretrained_out, new_out)
这样预训练部分是固定的常量张量,完全不会被更新,只有新词汇的嵌入会参与训练。
4. 检查优化器的参数分组是否正确
有些时候,即使你设置了requires_grad=False,如果不小心把冻结的参数也加入了优化器,部分框架可能会出现异常(虽然理论上不会更新,但保险起见还是要避免)。一定要确保优化器的参数列表里只有那些requires_grad=True的参数。
内容的提问来源于stack exchange,提问作者Prakhar Agarwal
相关产品推荐
相关产品推荐

