基于bert-base-chinese的主题建模实战:从零构建文档聚类系统

1次阅读
没有评论

共计 2127 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

背景痛点

传统文本聚类方法如 TF-IDF 结合 KMeans,在处理中文时面临几个核心问题:

基于 bert-base-chinese 的主题建模实战:从零构建文档聚类系统

  • 无法有效处理一词多义(比如 ” 苹果 ” 公司 vs 水果)
  • 忽略词序信息(” 狗咬人 ” 和 ” 人咬狗 ” 得到相同表示)
  • 难以捕捉深层语义关系(近义词无法自动关联)

我曾用某电商评论数据集测试,TF-IDF 方法的轮廓系数仅有 0.2 左右,且人工检查发现大量误分类案例。

技术选型

比较主流语义嵌入方案:

  1. BERT 系列
  2. 优点:原生支持中文,12 层 Transformer 捕获深层语义
  3. 缺点:基础版生成 768 维向量计算开销大

  4. Sentence-BERT

  5. 优点:专门优化的句子嵌入,相似度计算更准
  6. 缺点:中文社区预训练模型较少

最终选择 bert-base-chinese 的原因:
– 官方提供的标准中文预训练模型
– 在 CLUE 基准测试中表现稳定
– HuggingFace 生态完善易于集成

实现细节

文本预处理

import re
def clean_text(text: str) -> str:
    """
    处理特殊字符和冗余空白
    :param text: 原始文本
    :return: 清洗后文本
    """text = re.sub(r'[\r\n\t\xa0]',' ', text)  # 替换制表符等
    text = re.sub(r'[^\w\s\u4e00-\u9fa5]', '', text)  # 去除非中英文符号
    return text.strip()

模型加载与推理

from transformers import BertTokenizer, BertModel
import torch

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(text: str) -> np.ndarray:
    inputs = tokenizer(
        text, 
        return_tensors="pt", 
        max_length=512, 
        truncation=True,
        padding='max_length'
    ).to(device)
    with torch.no_grad():
        outputs = model(**inputs)
    return outputs.last_hidden_state[:,0,:].cpu().numpy()  # 取 [CLS] 位置

聚类实现

推荐使用 UMAP+HDBSCAN 组合:

import umap
import hdbscan

# 降维
reducer = umap.UMAP(
    n_components=64,
    n_neighbors=15,
    min_dist=0.1,
    metric='cosine'
)
embed_2d = reducer.fit_transform(embeddings)

# 聚类
clusterer = hdbscan.HDBSCAN(
    min_cluster_size=10,
    min_samples=5,
    cluster_selection_epsilon=0.3
)
labels = clusterer.fit_predict(embed_2d)

性能优化

降维技术对比

方法 优点 缺点
PCA 线性变换速度快 无法保留非线性结构
t-SNE 可视化效果好 计算复杂度 O(n²)
UMAP 平衡速度与效果 需要调参

评估指标

  • 轮廓系数:范围[-1,1],>0.5 说明聚类良好
  • Davies-Bouldin 指数:越小越好
from sklearn.metrics import silhouette_score
score = silhouette_score(embeddings, labels)

避坑指南

  1. OOV 词处理
  2. BERT 的 WordPiece 分词本身能处理未登录词
  3. 对专业术语可在 tokenizer 中添加特殊 tokens

  4. 内存管理

  5. 批量处理时用生成器替代列表

    def batch_process(texts: List[str], batch_size=32):
        for i in range(0, len(texts), batch_size):
            yield get_embedding(texts[i:i+batch_size])

  6. 自动确定聚类数

  7. HDBSCAN 自动确定
  8. KMeans 可用肘部法则:
    from sklearn.cluster import KMeans
    distortions = []
    for k in range(2,10):
        km = KMeans(n_clusters=k)
        km.fit(embeddings)
        distortions.append(km.inertia_)
    # 选择拐点对应的 k 值

拓展思考

在实际业务中,可以考虑:

  1. 模型部署
  2. 使用 FastAPI 封装为 REST 服务
  3. 对嵌入结果建立 FAISS 索引加速相似度查询

  4. 增量更新

  5. 定期用新数据 fine-tune 模型
  6. 采用 online clustering 算法如 StreamKM++

这套方案在某新闻分类项目中,将准确率从传统方法的 62% 提升到了 89%。关键是要注意中文停用词处理(比如去除 ” 这个 ”、” 那个 ” 等无意义词)和领域适配微调。

正文完
 0
评论(没有评论)