You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从BERT下一句预测模块提取合理的句间跟随概率?

BERT下一句预测输出概率过于极端的解决方法

问题描述

使用BertForNextSentencePrediction模块获取句子B跟随句子A的概率时,输出数值总是接近1或0,呈现布尔逻辑特性,不符合对连续概率的需求。例如:

  • 句子对“我今天可能去商店。”和“你喜欢吐司抹黄油吗?”输出0.9999562501907349(预期概率应较低)
  • 句子对“我今天可能去商店。”和“那个男人咳嗽了。”输出4.5933375076856464e-05(预期概率虽低但不应趋近于0)

当前使用的代码如下:

from transformers import BertTokenizer, BertForNextSentencePrediction
from torch.nn import functional as F

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
nsp_model = BertForNextSentencePrediction.from_pretrained('bert-base-uncased')

def next_sentence_prediction(sentence_1, sentence_2):
  encoding = tokenizer.encode_plus(sentence_1, sentence_2, return_tensors='pt')
  outputs = nsp_model(**encoding)[0]
  softmax = F.softmax(outputs, dim = 1)
  print(softmax[0][0].item())

原因分析

预训练阶段的BERT NSP任务是二分类任务:正例是文档中真实连续的句子对,负例是随机抽取的无关句子对。这种极端的训练数据分布导致模型学会输出极端概率,而非连续的关联度概率——它的目标是区分“是/不是下一句”,而非量化“有多可能是下一句”。

解决方案

1. 微调模型(最优方案)

用包含不同关联程度句子对的自定义数据集微调模型,让模型学习连续的概率分布:

  • 准备数据集:收集句子对,并标注它们的真实跟随概率(比如从0到1的连续值),或者按关联度划分多个等级(如“强关联”“中等关联”“弱关联”)。
  • 修改模型输出层:如果是回归任务,将原模型的二分类输出层替换为单神经元的线性层(输出连续值);如果是多分类任务,调整输出层神经元数量对应等级数。
  • 训练模型:使用MSE损失(回归)或交叉熵损失(多分类)进行训练,让模型学会输出符合预期的连续概率。

示例微调框架(回归任务):

import torch
import torch.nn as nn
from transformers import BertTokenizer, BertForNextSentencePrediction

# 加载预训练模型并替换输出层
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
nsp_model = BertForNextSentencePrediction.from_pretrained('bert-base-uncased')
# 替换原二分类层为回归层
nsp_model.classifier = nn.Linear(nsp_model.classifier.in_features, 1)
nsp_model.config.num_labels = 1

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = torch.optim.AdamW(nsp_model.parameters(), lr=5e-5)

# 示例训练循环(需替换为自定义数据集)
for epoch in range(3):
    nsp_model.train()
    # 假设train_dataloader是自定义数据集的加载器,包含sentence1, sentence2, label(0-1的概率)
    for batch in train_dataloader:
        encoding = tokenizer(batch['sentence1'], batch['sentence2'], return_tensors='pt', padding=True, truncation=True)
        labels = batch['label'].unsqueeze(1).float()
        
        outputs = nsp_model(**encoding)[0]
        loss = criterion(outputs, labels)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

2. 用句子相似度模型替代

如果无法微调,可使用Sentence-BERT计算句子对的语义相似度,以此作为跟随概率的近似——语义越相似的句子,越有可能是连续的下一句:

from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer('all-MiniLM-L6-v2')

def get_next_sentence_prob(sentence1, sentence2):
    emb1 = model.encode(sentence1, convert_to_tensor=True)
    emb2 = model.encode(sentence2, convert_to_tensor=True)
    # 计算余弦相似度,归一化到0-1区间作为概率近似
    cos_sim = util.cos_sim(emb1, emb2).item()
    return (cos_sim + 1) / 2  # 从[-1,1]映射到[0,1]

3. 温度缩放(临时调整方案)

如果必须使用原预训练NSP模型,可通过温度缩放软化概率分布,但这只是人为调整,并未改变模型的认知,效果有限:

from transformers import BertTokenizer, BertForNextSentencePrediction
from torch.nn import functional as F

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
nsp_model = BertForNextSentencePrediction.from_pretrained('bert-base-uncased')

def next_sentence_prediction(sentence_1, sentence_2, temperature=5):
  encoding = tokenizer.encode_plus(sentence_1, sentence_2, return_tensors='pt')
  outputs = nsp_model(**encoding)[0]
  # 除以温度系数后再做softmax,温度越高概率分布越平缓
  softmax = F.softmax(outputs / temperature, dim = 1)
  print(softmax[0][0].item())

内容的提问来源于stack exchange,提问作者TheGoldenPatrik1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 13:35:09