BERT文本相似度计算:嵌入层获取方式的选择困惑求解
基于微调BERT情感分类器的文本相似度计算:池化方式选择问题
问题背景
我想用基于BERT架构微调得到的情感分类器评估两段文本的相似度,代码如下:
from transformers import AutoTokenizer, AutoModel import torch import torch.nn.functional as F import numpy as np tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese") model = AutoModel.from_pretrained("bert-classifier") def calc_similarity(s1, s2): inputs = tokenizer(s1, s2, return_tensors='pt', padding=True, truncation=True) with torch.no_grad(): outputs = model(**inputs) embeddings = outputs.last_hidden_state[:, 0, :].cpu().numpy() cosine_similarity = F.cosine_similarity(embeddings[0], embeddings[1]) return cosine_similarity
我的疑问集中在embeddings = outputs.last_hidden_state[:, 0, :].cpu().numpy()这行代码。目前发现三种将last_hidden_state(形状为9768的张量)转化为1768向量的实现方式:
- 取<[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]>token对应的向量
embeddings = outputs.last_hidden_state[:, 0, :].cpu().numpy() - 对所有token向量做简单平均
embeddings = outputs.last_hidden_state.mean(axis=1).cpu().numpy() - 使用模型自带的池化层
embeddings = model.bert.pooler(outputs.last_hidden_state.cpu().numpy())
我认为第一种取<[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]>的方式不太适合分类任务的语义空间,因为我们的目标不是预测下一个词。现在纠结的是第二种和第三种方法该怎么选——用简单平均还是模型自带的池化层?
分析与建议
两种池化方式的本质区别
- 简单平均池化:对所有token的隐藏状态做算术平均,给每个token的语义贡献赋予相同权重,属于无参数的通用池化方式,没有引入任务相关的微调参数。
- BERT自带池化层:由线性层加Tanh激活函数组成,这个层已经在你微调情感分类器的过程中,用任务数据更新过参数。它的核心作用就是为分类任务学习最优的序列语义压缩方式,产出最贴合当前任务的句向量。
针对场景的选择建议
因为你使用的是已在情感分类任务上完成微调的BERT模型,优先选择第三种方式——模型自带的池化层,理由如下:
- 该池化层的参数是在情感分类任务的语义空间中训练得到的,比无参数的平均池化更懂任务需要捕捉的核心语义特征。比如在情感分类场景中,它会自动给情感关键词分配更高权重,而平均池化会拉平停用词等无关token的影响。
- 使用平均池化相当于放弃了模型在微调阶段学到的任务专属语义压缩能力,浪费了微调的成果。
特殊情况补充
如果你的情感分类数据集规模极小,池化层可能没有学到足够有效的参数,此时平均池化的表现可能和池化层接近甚至略好——但这种情况比较少见。绝大多数场景下,用微调后的自带池化层生成的句向量,计算出的文本相似度更贴合任务的语义需求。
内容的提问来源于stack exchange,提问作者Beitian Ma
相关产品推荐
相关产品推荐

