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

SBERT无论输入何种句子输出结果均一致,求问题排查

SBERT测试脚本聚类结果异常问题排查

问题描述

我编写了一个测试SBERT的脚本,代码如下:

import torch
from transformers import BertTokenizer, BertModel
from sklearn.cluster import KMeans

# 1. Use SBERT to compare two sentences for semantic similarity.
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

input_ids_1 = torch.tensor(tokenizer.encode("Hello, my dog is cute", add_special_tokens=True)).unsqueeze(0)  # Batch size 1
input_ids_2 = torch.tensor(tokenizer.encode("Hello, my cat is cute", add_special_tokens=True)).unsqueeze(0)  # Batch size 1
outputs_1 = model(input_ids_1)
outputs_2 = model(input_ids_2)
last_hidden_states_1 = outputs_1[0]  # The last hidden-state is the first element of the output tuple
last_hidden_states_2 = outputs_2[0]  # The last hidden-state is the first element of the output tuple

# 2. Take SBERT embeddings for both sentences and cluster them.
kmeans = KMeans(n_clusters=2, random_state=0).fit(last_hidden_states_1.detach().numpy()[0], last_hidden_states_2.detach().numpy()[0])

# 3. Print the clusters.
print(kmeans.labels_)
print(kmeans.cluster_centers_)

输出结果为:

[0 0 0 0 0 0 0 1]
[[-0.2281394   0.29968688  0.3390873  ... -0.40648264  0.2930719
   0.41721284]
 [ 0.6079925   0.26097086 -0.3130729  ...  0.03109726 -0.6282735
  -0.19942412]]

无论将第二个句子更换为任何内容(例如"The capital of France is Paris"),输出结果始终相同。显然在数据传递或转换环节存在错误,请问问题出在哪里?


问题原因及解决方法

1. KMeans参数传递错误(核心问题)

KMeans.fit()的参数定义为fit(X, y=None):

  • X是必需的特征矩阵,形状为[n_samples, n_features]
  • y是可选的样本标签,仅用于监督学习变种,KMeans作为无监督算法会直接忽略该参数

你将第二句的token embedding作为y传入,导致KMeans完全没有用到第二句的数据,仅对第一句的8个token embedding进行聚类,因此更换第二句内容不会改变结果。

2. 混淆了BERT与SBERT

当前使用的是原生BertModel,并非SBERT。SBERT是针对语义相似度任务微调后的BERT变种,通常需通过sentence-transformers库加载,原生BERT的输出并不适合直接用于语义相似度或聚类任务。

修正方案

方案一:对两个句子的所有token embedding进行聚类

将两个句子的token embedding合并为一个特征矩阵后传入KMeans:

import torch
import numpy as np
from transformers import BertTokenizer, BertModel
from sklearn.cluster import KMeans

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

input_ids_1 = torch.tensor(tokenizer.encode("Hello, my dog is cute", add_special_tokens=True)).unsqueeze(0)
input_ids_2 = torch.tensor(tokenizer.encode("Hello, my cat is cute", add_special_tokens=True)).unsqueeze(0)

outputs_1 = model(input_ids_1)
outputs_2 = model(input_ids_2)

last_hidden_states_1 = outputs_1[0]
last_hidden_states_2 = outputs_2[0]

# 提取并合并两个句子的token embedding,形状为[总token数, 隐藏层维度]
emb1 = last_hidden_states_1.detach().numpy()[0]
emb2 = last_hidden_states_2.detach().numpy()[0]
combined_embeddings = np.vstack([emb1, emb2])

# 对合并后的embedding聚类
kmeans = KMeans(n_clusters=2, random_state=0).fit(combined_embeddings)
print(kmeans.labels_)
print(kmeans.cluster_centers_)

方案二:使用SBERT生成句子级embedding并聚类

这是更贴合语义相似度需求的做法,直接生成句子向量后聚类:

from sentence_transformers import SentenceTransformer
from sklearn.cluster import KMeans

# 加载预训练SBERT模型
model = SentenceTransformer('all-MiniLM-L6-v2')
sentences = ["Hello, my dog is cute", "Hello, my cat is cute"]

# 生成句子级embedding
sentence_embeddings = model.encode(sentences)

# 对句子向量聚类
kmeans = KMeans(n_clusters=2, random_state=0).fit(sentence_embeddings)
print(kmeans.labels_)
print(kmeans.cluster_centers_)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 01:15:53