如何从自定义BertForMaskedLM加载无随机初始化权重的BertModel
从训练好的BertForMaskedLM提取有效嵌入向量做对比的操作方法
你遇到的警告和随机初始化的pooler参数是架构加载不匹配导致的:BertForMaskedLM训练阶段不会用到pooler层,直接用BertModel.from_pretrained()加载MLM权重时,BertModel自带的pooler层找不到对应训练权重,就会随机初始化参数并弹出未训练提示,这部分参数确实没有参考价值,按以下步骤操作即可拿到有效向量做对比:
操作步骤
- 第一步:不要直接把MLM权重加载到独立的BertModel实例里,先完整加载训练好的MLM模型,直接提取其内部已经训练好的Bert编码器主体,这部分权重100%是你多日训练得到的参数,不存在随机初始化问题:
import torch from transformers import BertForMaskedLM, BertModel, BertTokenizer # 加载你训练完成的MLM模型 mlm_model = BertForMaskedLM.from_pretrained("path/to/new_BertForMaskedLM_model") # 提取内置的编码器主体,无额外随机参数 trained_encoder = mlm_model.bert # 加载你要对比的基准BertModel base_bert = BertModel.from_pretrained("path/to/base_bert_model")
第二步:明确要对比的向量类型,全程避开无效的pooler输出:
- 对比token级上下文嵌入:直接取两个模型输出的
last_hidden_state即可,这个输出是编码器最后一层的隐状态,完全由训练好的权重计算得到,和pooler层无关。 - 对比句级嵌入:不要使用模型默认返回的
pooler_output,这个值就是随机初始化的pooler层计算的无效结果。你可以直接基于last_hidden_state做无参数池化得到有效句向量,比如取<[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]>token对应位置的隐状态、对所有token的隐状态做平均池化/最大池化都可以。
- 对比token级上下文嵌入:直接取两个模型输出的
第三步:统一设置推理模式,前向传播时关闭梯度计算,保证输出稳定可对比:
# 两个模型都切到评估模式,关闭dropout等训练专属层 trained_encoder.eval() base_bert.eval() # 加载对应分词器 tokenizer = BertTokenizer.from_pretrained("path/to/new_BertForMaskedLM_model") # 构造测试输入 test_inputs = tokenizer("需要做向量对比的测试文本", return_tensors="pt") with torch.no_grad(): # 训练后编码器的输出 trained_out = trained_encoder(**test_inputs) trained_token_emb = trained_out.last_hidden_state # 示例:取<[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]>位作为句向量 trained_sent_emb = trained_token_emb[:, 0, :] # 基准Bert的输出 base_out = base_bert(**test_inputs) base_token_emb = base_out.last_hidden_state base_sent_emb = base_token_emb[:, 0, :]
拿到上述trained_*和base_*的向量后,就可以直接做余弦相似度、分布差异、下游任务效果等维度的对比,所有向量都是基于训练好的有效权重计算的,不存在随机参数干扰。
补充说明:如果你一定要用
BertModel.from_pretrained()直接加载MLM路径也可以,加载后忽略弹出的警告,不要使用返回的pooler_output,只用last_hidden_state做后续计算,结果和上面提取内置编码器的方式完全一致,那个警告只是提示你pooler层参数未训练,不影响编码器主体的权重有效性。
内容的提问来源于stack exchange,提问作者jordan
相关产品推荐
相关产品推荐

