共计 2369 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点:为什么需要 BERT 做中文文档聚类?
在中文文档处理中,传统 TF-IDF 方法有三个致命伤:

- 无法解决一词多义问题(比如 ” 苹果 ” 在水果和科技领域的含义不同)
- 忽略词序信息(” 狗咬人 ” 和 ” 人咬狗 ” 会被视为相同)
- 难以处理未登录词(尤其是网络新词和领域术语)
而 bert-base-chinese 通过以下特性完美解决了这些问题:
- 基于 Transformer 的深度双向编码,能够根据上下文动态调整词向量
- 在中文维基、新闻、问答等多样语料上预训练,覆盖广泛语义
- 最大支持 512token 的输入长度,适合处理常见中文文档
技术方案设计
文档向量生成策略
处理长文本时需要注意:
- 对超过 510token 的文档(需保留 [CLS] 和[SEP]位置):
- 方案一:滑动窗口分段编码后取均值(推荐窗口 256/ 重叠 64)
-
方案二:仅截取首尾各 256token(适合新闻类头尾关键信息集中的文本)
-
向量提取方式:
- [CLS]向量:适合分类任务
- 均值池化:实践中文档聚类效果最好
- 最大池化:会丢失太多细节信息
降维方法对比
对 768 维的 BERT 向量,我们测试了三种方法:
- PCA:
- 优点:计算速度快
- 缺点:线性方法难以保持复杂语义关系
-
建议保留维度:通常 128-256 维足够
-
UMAP:
- 优点:能保持局部和全局结构
- 缺点:耗时是 PCA 的 3 - 5 倍
-
参数建议:n_neighbors=15, min_dist=0.1
-
t-SNE:
- 仅建议用于可视化(降到 2 / 3 维)
- 需设置较高 perplexity(30-50)
聚类算法选型
通过千万级中文语料测试得出建议:
- K-Means:
- 适合已知明确类别数的场景
- 需配合 Elbow Method 确定 K 值
-
对向量归一化敏感(必须做 L2 标准化)
-
DBSCAN:
- 适合自动发现类别数
- 关键参数 eps=0.3~0.6(需网格搜索)
-
处理噪声能力强但可能分裂大簇
-
层次聚类:
- 可视化友好(树状图)
- 计算复杂度 O(n^3)只适合小数据集
完整代码实现
import torch
from transformers import BertTokenizer, BertModel
from sklearn.cluster import KMeans
import umap
# 初始化模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertModel.from_pretrained('bert-base-chinese').to(device)
def get_embedding(texts, batch_size=32):
"""批量生成文档向量"""
embeddings = []
for i in range(0, len(texts), batch_size):
# 智能截断:保留头尾信息
batch = texts[i:i+batch_size]
inputs = tokenizer(batch,
padding=True,
truncation=True,
max_length=510,
return_tensors="pt").to(device)
with torch.no_grad():
outputs = model(**inputs)
# 均值池化
batch_embeddings = outputs.last_hidden_state.mean(dim=1).cpu()
embeddings.append(batch_embeddings)
return torch.cat(embeddings, dim=0)
# 示例流程
docs = ["这是一篇关于人工智能的文章", "新冠疫情最新报道", ...] # 你的文档列表
embeddings = get_embedding(docs)
# 降维
reducer = umap.UMAP(n_components=128, random_state=42)
embedding_2d = reducer.fit_transform(embeddings)
# 聚类
kmeans = KMeans(n_clusters=5, random_state=42)
clusters = kmeans.fit_predict(embedding_2d)
生产环境优化技巧
GPU 内存管理
- 批处理大小调整:
- 3090 显卡建议 batch_size=16(无 OOM)
-
可自动检测显存:
free_mem = torch.cuda.get_device_properties(0).total_memory - torch.cuda.memory_allocated(0) batch_size = int(free_mem / (max_len * 768 * 4 * 2)) # 安全系数 2 -
梯度清零:即使不训练也要执行
torch.cuda.empty_cache()
增量聚类方案
- 保存所有文档向量和聚类中心
- 对新文档:
- 计算与各中心的余弦相似度
- 低于阈值则视为新类(阈值建议 0.75-0.85)
- 每周全量重新聚类(避免概念漂移)
避坑指南
中文特有问题
- 停用词表需要补充:
- 网络用语(” 绝绝子 ”、”yyds”)
- 领域相关词(金融领域的 ” 同比 ”、” 环比 ”)
- 标点符号(中文顿号、书名号等)
OOM 排查路径
- 检查 GPU 监控:
nvidia-smi -l 1 - 逐步调小 batch_size 直到稳定
- 使用混合精度:
from torch.cuda.amp import autocast with autocast(): outputs = model(**inputs)
开放性问题
在实际项目中,我们发现这些评估难题:
– 如何量化评估无监督聚类质量?
– 当出现 ” 体育新闻 ” 和 ” 运动健康 ” 这类语义相近簇时,该合并还是保持分离?
– 对于时效性强的新闻聚类,应该如何设置重新训练的周期?
欢迎在评论区分享你的实战经验!
正文完
