【大模型应用开发】第二阶段:语义理解应用:文本分类与聚类 (Text Classification & Clustering)

第二阶段:语义理解应用:文本分类与聚类 (Text Classification & Clustering)

"The best classifier is the one you understand." - Andrew Ng

即使在生成式 AI 时代,经典的分类和聚类依然是业务系统的基石。本章将从零样本到微调,从K-Means到BERTopic,构建完整的语义理解工程体系。

难度级别:⭐⭐(应用实战)


目录

  • [一、判别式 AI vs. 生成式 AI](#一、判别式 AI vs. 生成式 AI)
    • [1. 核心差异](#1. 核心差异)
    • [2. 为什么判别式模型仍然重要?](#2. 为什么判别式模型仍然重要?)
  • [二、Embedding 的本质与可视化](#二、Embedding 的本质与可视化)
    • [1. 什么是 Embedding?](#1. 什么是 Embedding?)
    • [2. 流行的 Embedding 模型(2025版)](#2. 流行的 Embedding 模型(2025版))
    • [3. Embedding 可视化:t-SNE 与 UMAP](#3. Embedding 可视化:t-SNE 与 UMAP)
    • [4. 完整代码:Embedding 可视化实战](#4. 完整代码:Embedding 可视化实战)
  • 三、零样本分类 (Zero-Shot Classification)
    • [1. 基于 NLI 的零样本分类](#1. 基于 NLI 的零样本分类)
    • [2. 英文零样本分类实现](#2. 英文零样本分类实现)
    • [3. 中文零样本分类(基于 Sentence-BERT)](#3. 中文零样本分类(基于 Sentence-BERT))
    • [4. 2025年最佳实践:使用 LLM API](#4. 2025年最佳实践:使用 LLM API)
    • [5. 高级策略:Embedding 粗筛 + LLM 精排](#5. 高级策略:Embedding 粗筛 + LLM 精排)
  • 四、少样本分类 (Few-Shot Classification)
    • [1. SetFit 框架深度解析](#1. SetFit 框架深度解析)
    • [2. 完整代码实现](#2. 完整代码实现)
    • [3. Few-Shot Prompt(使用 LLM)](#3. Few-Shot Prompt(使用 LLM))
  • [五、全量微调 vs PEFT (LoRA) 在分类任务中的对比](#五、全量微调 vs PEFT (LoRA) 在分类任务中的对比)
    • [1. 全量微调 (Full Fine-Tuning)](#1. 全量微调 (Full Fine-Tuning))
    • [2. PEFT: LoRA (Low-Rank Adaptation)](#2. PEFT: LoRA (Low-Rank Adaptation))
    • [3. 完整对比代码](#3. 完整对比代码)
    • [4. 对比总结](#4. 对比总结)
  • 六、文本聚类算法
    • [1. K-Means 聚类](#1. K-Means 聚类)
    • [2. DBSCAN (密度聚类)](#2. DBSCAN (密度聚类))
    • [3. HDBSCAN (层次化 DBSCAN)](#3. HDBSCAN (层次化 DBSCAN))
    • [4. 算法对比](#4. 算法对比)
  • [七、BERTopic 原理与实战](#七、BERTopic 原理与实战)
    • [1. BERTopic 流程](#1. BERTopic 流程)
    • [2. 完整代码实战](#2. 完整代码实战)
    • [3. 中文 BERTopic](#3. 中文 BERTopic)
    • [4. 动态主题建模(时间维度)](#4. 动态主题建模(时间维度))
  • 八、层次化聚类实战
    • [1. 凝聚层次聚类 (Agglomerative)](#1. 凝聚层次聚类 (Agglomerative))
    • [2. 完整代码](#2. 完整代码)
    • [3. 层次化主题建模](#3. 层次化主题建模)
  • 九、分类与聚类评估指标
    • [1. 分类评估指标](#1. 分类评估指标)
    • [2. 聚类评估指标](#2. 聚类评估指标)
    • [3. 完整评估代码](#3. 完整评估代码)
  • 十、实战案例:客服工单自动分类系统
    • [1. 系统架构](#1. 系统架构)
    • [2. 完整实现代码](#2. 完整实现代码)
    • [3. 生产环境部署建议](#3. 生产环境部署建议)
  • 十一、本章小结

一、判别式 AI vs. 生成式 AI

在 LLM 出现之前,NLP 的半壁江山是 BERT 打下的。BERT 是典型的判别式模型(Encoder-only)。

1. 核心差异

维度 判别式模型 (Discriminative) 生成式模型 (Generative)
目标 P ( Y ∣ X ) P(Y \mid X) P(Y∣X) - 给定输入判断类别 P ( X , Y ) P(X, Y) P(X,Y) 或 P ( Y ∣ X ) P(Y \mid X) P(Y∣X) 自回归
代表 BERT, RoBERTa, DeBERTa GPT-4, LLaMA, ChatGPT
优势 准确率高、速度快、成本低 灵活性强、零样本能力
劣势 需要标注数据、类别固定 推理慢、成本高、易幻觉

2. 为什么判别式模型仍然重要?

虽然现在可以用 GPT-4 做分类(Prompt: "Is this positive? answer yes/no"),但在高并发、低延迟、高性价比 场景下,基于 Embedding 的分类器依然是最优选择。

典型场景:

  • 客服工单自动分类(每秒数千请求)
  • 垃圾邮件过滤(毫秒级响应)
  • 舆情分析(固定类别:正/中/负)
  • 内容审核(违规分类)

二、Embedding 的本质与可视化

1. 什么是 Embedding?

Embedding 是将文本映射到高维向量空间的过程,使得语义相似的文本在空间中彼此接近

数学表示:
Encoder : T → R d \text{Encoder}: \mathbb{T} \rightarrow \mathbb{R}^d Encoder:T→Rd

其中:

  • T \mathbb{T} T: 文本空间
  • d d d: Embedding 维度(通常 768/1024/1536)

2. 流行的 Embedding 模型(2025版)

模型 维度 特点 使用场景 发布时间
BGE-M3 1024 多语言、多粒度 跨语言检索 2024年
bge-large-zh-v1.5 1024 中文SOTA 中文检索/分类 2023年
M3E-base 768 中文轻量级 资源受限场景 2023年
text-embedding-3-large 3072 OpenAI最新 英文任务 2024年
GTE-Qwen2-7B 3584 大模型级别 高精度场景 2024年
jina-embeddings-v3 1024 多任务优化 通用场景 2024年

2025年推荐选择:

  • 中文场景bge-large-zh-v1.5BGE-M3
  • 英文场景text-embedding-3-large (API) 或 GTE-Qwen2-7B (本地)
  • 资源受限M3E-basebge-small-zh-v1.5
  • 多语言BGE-M3jina-embeddings-v3

3. Embedding 可视化:t-SNE 与 UMAP

高维向量无法直接观察,需要降维到 2D/3D 空间。

3.1 t-SNE (t-Distributed Stochastic Neighbor Embedding)

原理:

  • 在高维空间中,计算点对之间的相似度(高斯分布)
  • 在低维空间中,保持相似度分布(t-分布)
  • 通过梯度下降最小化 KL 散度

优点:

  • 擅长保持局部结构
  • 可视化效果好

缺点:

  • 慢( O ( N 2 ) O(N^2) O(N2))
  • 随机性强(每次结果不同)
  • 不保持全局结构
3.2 UMAP (Uniform Manifold Approximation and Projection)

原理:

  • 基于流形学习和拓扑数据分析
  • 构建高维空间的拓扑图(k-近邻图)
  • 在低维空间重建该拓扑结构

优点:

  • 速度快(支持大规模数据)
  • 保持全局结构
  • 确定性强

缺点:

  • 参数敏感(n_neighbors, min_dist

4. 完整代码:Embedding 可视化实战

python 复制代码
"""
功能:Embedding可视化(t-SNE vs UMAP对比)
依赖:sentence-transformers>=2.3.0, scikit-learn>=1.3.0, umap-learn>=0.5.5
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
import umap
from sentence_transformers import SentenceTransformer

# 1. 准备数据
texts = [
    # 科技类
    "人工智能正在改变世界",
    "深度学习模型训练需要大量数据",
    "Transformer架构是现代AI的基石",
    "GPT-4展现了强大的语言理解能力",

    # 体育类
    "梅西带领阿根廷夺得世界杯冠军",
    "NBA总决赛即将开打",
    "马拉松比赛吸引数万名跑者参与",
    "中国队在奥运会上取得佳绩",

    # 美食类
    "这家餐厅的烤鸭非常美味",
    "川菜以麻辣著称",
    "意大利面配番茄酱很经典",
    "日本寿司讲究食材新鲜",

    # 旅游类
    "长城是中国的标志性景点",
    "巴黎的埃菲尔铁塔令人震撼",
    "马尔代夫的海滩美不胜收",
    "京都的古寺展现日本文化"
]

labels = ["科技"]*4 + ["体育"]*4 + ["美食"]*4 + ["旅游"]*4

# 2. 生成 Embedding(使用2025年推荐模型)
print("加载 Embedding 模型...")
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
embeddings = model.encode(texts, normalize_embeddings=True)

print(f"Embedding 形状: {embeddings.shape}")  # (16, 1024)

# 3. t-SNE 降维
print("执行 t-SNE 降维...")
tsne = TSNE(
    n_components=2,
    random_state=42,
    perplexity=5,  # 对于小数据集使用较小的perplexity
    n_iter=1000
)
tsne_results = tsne.fit_transform(embeddings)

# 4. UMAP 降维
print("执行 UMAP 降维...")
umap_reducer = umap.UMAP(
    n_components=2,
    random_state=42,
    n_neighbors=5,
    min_dist=0.1,
    metric='cosine'
)
umap_results = umap_reducer.fit_transform(embeddings)

# 5. 可视化对比
fig, axes = plt.subplots(1, 2, figsize=(16, 7))

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 定义颜色映射
colors = {'科技': '#FF6B6B', '体育': '#4ECDC4', '美食': '#FFD93D', '旅游': '#95E1D3'}

# t-SNE 可视化
for label in set(labels):
    mask = np.array(labels) == label
    axes[0].scatter(
        tsne_results[mask, 0],
        tsne_results[mask, 1],
        label=label,
        color=colors[label],
        s=150,
        alpha=0.7,
        edgecolors='black',
        linewidths=1
    )
axes[0].set_title("t-SNE 可视化", fontsize=16, fontweight='bold')
axes[0].legend(fontsize=12)
axes[0].grid(True, alpha=0.3, linestyle='--')

# UMAP 可视化
for label in set(labels):
    mask = np.array(labels) == label
    axes[1].scatter(
        umap_results[mask, 0],
        umap_results[mask, 1],
        label=label,
        color=colors[label],
        s=150,
        alpha=0.7,
        edgecolors='black',
        linewidths=1
    )
axes[1].set_title("UMAP 可视化", fontsize=16, fontweight='bold')
axes[1].legend(fontsize=12)
axes[1].grid(True, alpha=0.3, linestyle='--')

plt.tight_layout()
plt.savefig('embedding_visualization.png', dpi=150, bbox_inches='tight')
plt.show()

print("\n✅ 可视化完成!")
print("💡 观察要点:")
print("   - 同类别文本是否聚在一起(类内紧密度)")
print("   - 不同类别是否分离明显(类间距离)")
print("   - t-SNE更关注局部结构,UMAP保留全局结构")

运行结果分析:

  • 同类别聚集:相同主题的文本在空间中彼此接近
  • 类别分离:不同主题之间有明显边界
  • t-SNE vs UMAP
    • t-SNE:更紧凑的聚类,适合展示局部关系
    • UMAP:保持全局结构,类别间距离更有意义

⚠️ 注意事项:

  • 降维会损失信息,可视化仅供参考
  • 小数据集(<50样本)降维效果不稳定
  • 生产环境直接用高维向量计算相似度,无需降维

三、零样本分类 (Zero-Shot Classification)

零样本分类不需要训练数据,直接利用预训练模型的语义理解能力。

1. 基于 NLI 的零样本分类

核心思想:

将分类问题转化为**文本蕴含(Entailment)**问题。

公式:
P ( label ∣ text ) = P ( entailment ∣ premise=text , hypothesis="This text is about [label]" ) P(\text{label} \mid \text{text}) = P(\text{entailment} \mid \text{premise=text}, \text{hypothesis="This text is about [label]"}) P(label∣text)=P(entailment∣premise=text,hypothesis="This text is about [label]")

其中:

  • Premise(前提):待分类文本
  • Hypothesis(假设):构造的分类假设,如"This text is about 科技"
  • Entailment(蕴含):前提是否支持假设

2. 英文零样本分类实现

python 复制代码
"""
功能:基于NLI的零样本分类(英文)
模型:facebook/bart-large-mnli(2019年,依然有效)
依赖:transformers>=4.36.0
"""
from transformers import pipeline

# 1. 加载零样本分类器
classifier = pipeline(
    "zero-shot-classification",
    model="facebook/bart-large-mnli",
    device=0  # 使用GPU加速(如果有)
)

# 2. 定义候选类别
candidate_labels = ["technology", "sports", "politics", "entertainment", "finance"]

# 3. 待分类文本
text = "Apple announced the new iPhone 15 series with improved cameras"

# 4. 进行分类
result = classifier(text, candidate_labels)

print("零样本分类结果:")
for label, score in zip(result['labels'], result['scores']):
    print(f"  {label}: {score:.3f}")

# 输出示例:
# technology: 0.952
# finance: 0.028
# entertainment: 0.012
# sports: 0.005
# politics: 0.003

3. 中文零样本分类(基于 Sentence-BERT)

python 复制代码
"""
功能:基于语义相似度的中文零样本分类
模型:BAAI/bge-large-zh-v1.5(2025年推荐)
依赖:sentence-transformers>=2.3.0
"""
import numpy as np
from sentence_transformers import SentenceTransformer, util

# 1. 加载中文 Embedding 模型
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')

# 2. 定义类别描述(使用完整描述而非单个词)
category_descriptions = {
    "科技": "这段文本讨论的是人工智能、计算机、互联网、科学技术或IT行业相关的内容",
    "体育": "这段文本讨论的是足球、篮球、比赛、运动员或体育赛事相关的内容",
    "美食": "这段文本讨论的是餐饮、菜品、烹饪、美味或饮食文化相关的内容",
    "旅游": "这段文本讨论的是景点、旅行、度假、观光或旅游体验相关的内容"
}

# 3. 待分类文本
text = "长城是中国古代的伟大建筑,吸引了无数游客前来参观"

# 4. 计算相似度
text_embedding = model.encode(text, convert_to_tensor=True, normalize_embeddings=True)
category_embeddings = model.encode(
    list(category_descriptions.values()),
    convert_to_tensor=True,
    normalize_embeddings=True
)

# 5. 余弦相似度(归一化后等价于点积)
similarities = util.cos_sim(text_embedding, category_embeddings)[0]

# 6. 获取排序结果
scores = similarities.cpu().numpy()
ranked_categories = sorted(
    zip(category_descriptions.keys(), scores),
    key=lambda x: x[1],
    reverse=True
)

print("中文零样本分类结果:")
for category, score in ranked_categories:
    confidence = "高" if score > 0.6 else "中" if score > 0.4 else "低"
    print(f"  {category}: {score:.3f} [{confidence}置信度]")

# 输出示例:
# 旅游: 0.742 [高置信度]
# 科技: 0.312 [低置信度]
# 美食: 0.289 [低置信度]
# 体育: 0.254 [低置信度]

💡 关键技巧:

  • ✅ 用完整描述代替单个类别词(准确率提升10-15%)
  • ✅ 使用normalize_embeddings=True确保余弦相似度一致性
  • ✅ 选择专门的中文Embedding模型
  • ⚠️ 类别描述的质量直接影响分类效果

4. 2025年最佳实践:使用 LLM API

现代LLM(如GPT-4、Claude、Qwen)具备强大的零样本分类能力,适合快速原型复杂分类场景。

python 复制代码
"""
功能:使用OpenAI API进行零样本分类
模型:gpt-4o-mini(性价比最高,2024年发布)
依赖:openai>=1.10.0
"""
from openai import OpenAI
import json

client = OpenAI()

def classify_text_with_llm(text: str, categories: list[str]) -> dict:
    """
    使用LLM进行零样本分类

    参数:
        text: 待分类文本
        categories: 候选类别列表

    返回:
        {"category": str, "confidence": float, "reasoning": str}
    """
    prompt = f"""你是一个专业的文本分类助手。请将以下文本分类到给定类别中。

候选类别:{', '.join(categories)}

文本:{text}

请以JSON格式返回结果,包含以下字段:
- category: 最匹配的类别
- confidence: 置信度(0-1之间的浮点数)
- reasoning: 简短的分类理由(20字以内)

示例输出:
{{"category": "科技", "confidence": 0.95, "reasoning": "讨论了AI技术发展"}}
"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",  # 2025年推荐:快速且便宜
        messages=[{"role": "user", "content": prompt}],
        temperature=0,  # 确保结果稳定
        response_format={"type": "json_object"}  # 强制JSON输出
    )

    result = json.loads(response.choices[0].message.content)
    return result

# 测试
categories = ["科技", "体育", "美食", "旅游"]
text = "OpenAI发布了最新的GPT-5模型,性能大幅提升"

result = classify_text_with_llm(text, categories)
print(f"分类结果: {result['category']}")
print(f"置信度: {result['confidence']}")
print(f"理由: {result['reasoning']}")

# 输出示例:
# 分类结果: 科技
# 置信度: 0.98
# 理由: 讨论了AI模型发布

对比:Embedding vs LLM

维度 Embedding方法 LLM方法
准确率 中(75-85%) 高(85-95%)
速度 快(10ms) 慢(500-1000ms)
成本 低(免费/自部署) 高($0.0001-0.001/次)
解释性 有(可提供理由)
复杂场景 强(可处理多层次分类)
适用场景 高并发、固定类别 灵活分类、需要解释

2025年推荐策略:

  1. 原型阶段:使用LLM快速验证
  2. 生产环境
    • 高并发(>100 QPS)→ Embedding + 分类器
    • 低并发、高准确率要求 → LLM API
    • 成本敏感 → 自部署开源模型(Qwen2.5-14B)

5. 高级策略:Embedding 粗筛 + LLM 精排 (Hybrid Classification)

当类别数量非常多(例如 1000+ 电商类目)时,我们无法将所有类别都放入 LLM 的 Prompt 中(上下文长度限制且费用昂贵)。此时,"粗筛 + 精排" 是标准解决方案。

流程图:

复制代码
用户输入 → Embedding 检索(向量库) → Top-K 候选类别(如 Top 10)
                                        ↓
                                    构建 Prompt (包含 Top-K 候选)
                                        ↓
                                     LLM 最终裁决

代码实现:

python 复制代码
"""
功能:Embedding 粗筛 + LLM 精排处理海量类别
"""
from sentence_transformers import SentenceTransformer, util
from openai import OpenAI
import torch

# 1. 模拟海量类别库 (实际场景可能有数千个)
all_categories = [
    # 电子产品
    "智能手机", "笔记本电脑", "平板电脑", "智能手表", "蓝牙耳机",
    "单反相机", "无人机", "移动电源", "数据线", "手机壳",
    # 家居用品
    "沙发", "床垫", "衣柜", "餐桌", "办公椅",
    "台灯", "窗帘", "地毯", "装饰画", "花瓶",
    # ... 假设有很多 ...
]

# 2. 初始化模型
embedder = SentenceTransformer('BAAI/bge-large-zh-v1.5')
client = OpenAI()

# 3. 预计算类别 Embeddings (只需做一次)
category_embeddings = embedder.encode(all_categories, convert_to_tensor=True)

def hybrid_classify(text, top_k=5):
    # Step 1: 粗筛 (Embedding Retrieval)
    query_embedding = embedder.encode(text, convert_to_tensor=True)

    # 计算相似度并获取 Top-K
    cos_scores = util.cos_sim(query_embedding, category_embeddings)[0]
    top_results = torch.topk(cos_scores, k=top_k)

    candidate_indices = top_results.indices.tolist()
    candidates = [all_categories[i] for i in candidate_indices]

    print(f"Embedding 粗筛结果: {candidates}")

    # Step 2: 精排 (LLM Decision)
    prompt = f"""请从以下候选类别中,为文本选择最准确的一个类别。

文本:{text}

候选列表:
{', '.join(candidates)}

如果都不合适,请返回"其他"。只返回类别名称,不要解释。"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0
    )

    final_category = response.choices[0].message.content
    return final_category

# 测试
text = "我想买个大疆用来航拍婚礼"
result = hybrid_classify(text)
print(f"最终分类: {result}")

# 输出示例:
# Embedding 粗筛结果: ['无人机', '单反相机', '智能手机', '移动电源', '蓝牙耳机']
# 最终分类: 无人机

方案优势:

  • 突破上下文限制:支持无限多的类别(只要向量检索能支持)。
  • 降低成本:Prompt 中只需包含 5-10 个即使候选,大大减少 Token 消耗。
  • 准确率高:LLM 擅长处理歧义,比单纯 Embedding 匹配更准(例如区分"苹果手机"和"苹果(水果)")。

四、少样本分类 (Few-Shot Classification)

只有少量标注样本(每类 5-50 个)时,使用少样本学习框架。

1. SetFit 框架深度解析

SetFit (Sentence Transformer Fine-Tuning) 是 HuggingFace 推出的少样本分类利器,专门解决标注数据稀缺问题。

1.1 为什么 SetFit 如此高效?

传统微调的困境:

  • BERT 微调需要数千样本才能避免过拟合
  • Prompt-based 方法需要精心设计提示词,效果不稳定
  • 少样本场景下,全量微调容易"记住"训练样本(过拟合)

SetFit 的创新:

  1. 对比学习阶段:从每类 8 个样本中生成配对数据

    • 同类样本对(正样本对):"text_A" 与 "text_B" 相似
    • 异类样本对(负样本对):"text_A" 与 "text_C" 不相似
    • 通过对比学习微调 Sentence Transformer,拉近同类,推远异类
  2. 分类头训练:在微调后的 Embedding 之上训练轻量级分类器

    • 使用简单的 Logistic Regression 或浅层 MLP
    • 因为 Embedding 已经学到了任务相关的语义,分类器只需学习决策边界

数学原理:

对比学习损失函数:
L = − log ⁡ exp ⁡ ( sim ( h i , h j + ) / τ ) exp ⁡ ( sim ( h i , h j + ) / τ ) + ∑ k = 1 N exp ⁡ ( sim ( h i , h k − ) / τ ) \mathcal{L} = -\log \frac{\exp(\text{sim}(h_i, h_j^+) / \tau)}{\exp(\text{sim}(h_i, h_j^+) / \tau) + \sum_{k=1}^{N} \exp(\text{sim}(h_i, h_k^-) / \tau)} L=−logexp(sim(hi,hj+)/τ)+∑k=1Nexp(sim(hi,hk−)/τ)exp(sim(hi,hj+)/τ)

其中:

  • h i h_i hi: 锚点样本的 Embedding
  • h j + h_j^+ hj+: 正样本(同类)的 Embedding
  • h k − h_k^- hk−: 负样本(异类)的 Embedding
  • τ \tau τ: 温度参数(通常 0.05)
  • sim ( ⋅ , ⋅ ) \text{sim}(\cdot, \cdot) sim(⋅,⋅): 余弦相似度

优势总结:

  • 数据高效:每类只需 8-16 个样本,在某些数据集上超过 3000 样本的全量微调
  • 训练快速:对比学习+分类头训练通常 < 1 分钟(CPU)
  • 效果接近全量微调:在 20 个数据集上平均准确率仅低 1-2%
  • 多语言支持:可使用任何 Sentence Transformer 作为基座
  • 无需 Prompt 工程:不像 GPT-3 需要精心设计提示词

2. 完整代码实现

python 复制代码
from setfit import SetFitModel, SetFitTrainer
from datasets import Dataset

# 1. 准备少样本数据(每类只有 8 个样本)
train_data = {
    "text": [
        # 科技类(8个)
        "OpenAI发布GPT-4模型", "量子计算机取得突破",
        "芯片技术进入3nm时代", "人工智能辅助医疗诊断",
        "5G网络覆盖全国", "云计算市场快速增长",
        "机器人技术日新月异", "区块链应用场景扩大",

        # 体育类(8个)
        "中国队夺得奥运金牌", "世界杯决赛激动人心",
        "NBA季后赛进入白热化", "马拉松赛事圆满结束",
        "足球转会窗口关闭", "运动员打破世界纪录",
        "羽毛球公开赛开幕", "网球大满贯赛事精彩"
    ],
    "label": [0]*8 + [1]*8  # 0: 科技, 1: 体育
}

train_dataset = Dataset.from_dict(train_data)

# 2. 加载预训练模型并创建 SetFit 模型
model = SetFitModel.from_pretrained("moka-ai/m3e-base")

# 3. 创建训练器
trainer = SetFitTrainer(
    model=model,
    train_dataset=train_dataset,
    num_iterations=20,  # 对比学习的轮数
    num_epochs=1,       # 分类头训练的轮数
    column_mapping={"text": "text", "label": "label"}
)

# 4. 训练模型
trainer.train()

# 5. 测试模型
test_texts = [
    "谷歌推出新款智能手机",
    "篮球比赛进入加时赛"
]

predictions = model(test_texts)
print("少样本分类结果:")
for text, pred in zip(test_texts, predictions):
    label_name = "科技" if pred == 0 else "体育"
    print(f"  {text} -> {label_name}")

3. Few-Shot Prompt(使用 LLM)

对于更少的样本(每类 1-3 个),可以用 In-Context Learning:

python 复制代码
from openai import OpenAI

client = OpenAI()

# Few-shot examples
examples = """
示例1:
文本: 人工智能改变世界
类别: 科技

示例2:
文本: 梅西带领球队夺冠
类别: 体育

示例3:
文本: 这道菜非常美味
类别: 美食
"""

# 待分类文本
text = "苹果发布新款笔记本电脑"

# 构建 Prompt
prompt = f"""{examples}

现在请分类以下文本:
文本: {text}
类别: """

response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": prompt}],
    temperature=0
)

print(f"分类结果: {response.choices[0].message.content}")
# 输出: 科技

五、全量微调 vs PEFT (LoRA) 在分类任务中的对比

当有足够数据时,可以选择微调模型。

1. 全量微调 (Full Fine-Tuning)

定义: 更新模型的所有参数。

优点:

  • 效果最好(充分学习任务特征)

缺点:

  • 显存占用大(需要存储全部梯度)
  • 训练慢
  • 容易过拟合(小数据集)

2. PEFT: LoRA (Low-Rank Adaptation)

核心思想:

冻结预训练权重 W 0 W_0 W0,只训练低秩分解矩阵 A , B A, B A,B:

W = W 0 + Δ W = W 0 + B A W = W_0 + \Delta W = W_0 + BA W=W0+ΔW=W0+BA

其中:

  • W 0 ∈ R d × d W_0 \in \mathbb{R}^{d \times d} W0∈Rd×d: 冻结的原始权重
  • B ∈ R d × r B \in \mathbb{R}^{d \times r} B∈Rd×r, A ∈ R r × d A \in \mathbb{R}^{r \times d} A∈Rr×d: 可训练的低秩矩阵
  • r ≪ d r \ll d r≪d: 秩(通常 8/16/32)

参数量对比:

  • 原始: d 2 d^2 d2
  • LoRA: 2 d r 2dr 2dr (当 r = 8 r=8 r=8, d = 1024 d=1024 d=1024 时,减少 99%)

3. 完整对比代码

python 复制代码
from transformers import AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments
from peft import LoraConfig, get_peft_model
from datasets import load_dataset

# 准备数据
dataset = load_dataset("SetFit/amazon_counterfactual_en")
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

def tokenize(batch):
    return tokenizer(batch["text"], padding=True, truncation=True)

dataset = dataset.map(tokenize, batched=True)

# ========== 方法1: 全量微调 ==========
model_full = AutoModelForSequenceClassification.from_pretrained(
    "bert-base-uncased",
    num_labels=2
)

print(f"全量微调参数量: {model_full.num_parameters():,}")

trainer_full = Trainer(
    model=model_full,
    args=TrainingArguments(
        output_dir="./full_finetune",
        num_train_epochs=3,
        per_device_train_batch_size=16,
        logging_steps=100
    ),
    train_dataset=dataset["train"]
)

trainer_full.train()

# ========== 方法2: LoRA 微调 ==========
model_lora = AutoModelForSequenceClassification.from_pretrained(
    "bert-base-uncased",
    num_labels=2
)

# 配置 LoRA
lora_config = LoraConfig(
    r=8,                    # 秩
    lora_alpha=32,          # 缩放系数
    target_modules=["query", "value"],  # 只作用于 Q,V 矩阵
    lora_dropout=0.1,
    bias="none"
)

model_lora = get_peft_model(model_lora, lora_config)
model_lora.print_trainable_parameters()
# 输出: trainable params: 294,912 || all params: 109,483,778 || trainable%: 0.27%

trainer_lora = Trainer(
    model=model_lora,
    args=TrainingArguments(
        output_dir="./lora_finetune",
        num_train_epochs=3,
        per_device_train_batch_size=16,
        logging_steps=100
    ),
    train_dataset=dataset["train"]
)

trainer_lora.train()

# ========== 性能对比 ==========
print("\n性能对比:")
print(f"全量微调 - 参数量: {model_full.num_parameters():,}, 显存: ~4.2GB")
print(f"LoRA微调 - 参数量: {model_lora.get_nb_trainable_parameters():,}, 显存: ~1.5GB")

4. 对比总结

维度 全量微调 LoRA 微调
参数量 100% 0.1%-1%
显存占用 低(可用更大 Batch Size)
训练速度 快 1.5-2倍
效果 最优 接近全量(损失<1%)
适用场景 大数据集 中小数据集

建议:

  • 数据 < 10k:用 SetFitFew-Shot
  • 数据 10k-100k:用 LoRA
  • 数据 > 100k:考虑 全量微调

六、文本聚类算法

当没有标注数据时,聚类可以自动发现文本的内在结构。

1. K-Means 聚类

原理:

  1. 随机初始化 k k k 个聚类中心
  2. 将每个样本分配到最近的中心
  3. 重新计算每个簇的中心
  4. 重复 2-3 直到收敛

代码实现:

python 复制代码
from sklearn.cluster import KMeans
from sentence_transformers import SentenceTransformer
import matplotlib.pyplot as plt

# 1. 准备数据
texts = [
    "人工智能改变世界", "深度学习突破", "神经网络应用",
    "篮球比赛精彩", "足球世界杯", "奥运会开幕",
    "美味的川菜", "意大利面很好吃", "日本料理精致",
    "长城景色壮观", "巴黎铁塔美丽", "马尔代夫海滩"
]

# 2. 生成 Embedding
model = SentenceTransformer('moka-ai/m3e-base')
embeddings = model.encode(texts)

# 3. K-Means 聚类
kmeans = KMeans(n_clusters=4, random_state=42)
labels = kmeans.fit_predict(embeddings)

# 4. 输出结果
for i, (text, label) in enumerate(zip(texts, labels)):
    print(f"簇{label}: {text}")

# 输出示例:
# 簇0: 人工智能改变世界
# 簇0: 深度学习突破
# 簇1: 篮球比赛精彩
# ...

问题:

  • 需要预先指定 k k k(簇数)
  • 对初始化敏感
  • 高维空间效果下降(维度灾难)

2. DBSCAN (密度聚类)

优势:

  • 自动发现簇数
  • 可识别噪声点
  • 能发现任意形状的簇

核心参数:

  • eps: 邻域半径
  • min_samples: 核心点的最小邻居数

代码实现:

python 复制代码
from sklearn.cluster import DBSCAN
from sklearn.decomposition import PCA

# 1. 降维(高维空间DBSCAN效果差)
pca = PCA(n_components=50)
embeddings_reduced = pca.fit_transform(embeddings)

# 2. DBSCAN 聚类
dbscan = DBSCAN(eps=0.5, min_samples=2)
labels = dbscan.fit_predict(embeddings_reduced)

print(f"发现 {len(set(labels)) - (1 if -1 in labels else 0)} 个簇")
print(f"噪声点数量: {list(labels).count(-1)}")

# 3. 输出结果
for i, (text, label) in enumerate(zip(texts, labels)):
    cluster_name = f"簇{label}" if label != -1 else "噪声"
    print(f"{cluster_name}: {text}")

3. HDBSCAN (层次化 DBSCAN)

改进:

  • 自动确定 eps 参数
  • 更稳定的聚类结果
  • 适合不同密度的簇

代码实现:

python 复制代码
import hdbscan

# 1. HDBSCAN 聚类
clusterer = hdbscan.HDBSCAN(
    min_cluster_size=2,
    min_samples=1,
    metric='euclidean'
)

labels = clusterer.fit_predict(embeddings_reduced)

print(f"HDBSCAN 发现 {len(set(labels)) - (1 if -1 in labels else 0)} 个簇")

# 2. 聚类概率(置信度)
probabilities = clusterer.probabilities_

for text, label, prob in zip(texts, labels, probabilities):
    print(f"簇{label} ({prob:.2f}): {text}")

4. 算法对比

算法 需要指定簇数 噪声识别 高维适应 速度
K-Means
DBSCAN
HDBSCAN

推荐策略:

  1. 先用 UMAP/PCA 降维到 50-100 维
  2. HDBSCAN 自动发现簇
  3. 轮廓系数 (Silhouette Score) 评估聚类质量

七、BERTopic 原理与实战

BERTopic 是现代主题建模的标准工具,集成了 Embedding + 降维 + 聚类 + 关键词提取。

1. BERTopic 流程

复制代码
文本 → Embedding → UMAP降维 → HDBSCAN聚类 → c-TF-IDF提取主题词

核心创新:c-TF-IDF (Class-based TF-IDF)

传统 TF-IDF 是文档级别,c-TF-IDF 是簇级别

c-TF-IDF ( w , c ) = TF ( w , c ) × log ⁡ N ∑ c ′ TF ( w , c ′ ) \text{c-TF-IDF}(w, c) = \text{TF}(w, c) \times \log \frac{N}{\sum_{c'} \text{TF}(w, c')} c-TF-IDF(w,c)=TF(w,c)×log∑c′TF(w,c′)N

其中:

  • w w w: 词
  • c c c: 簇
  • TF ( w , c ) \text{TF}(w, c) TF(w,c): 词 w w w 在簇 c c c 中的频率

2. 完整代码实战

python 复制代码
from bertopic import BERTopic
from sklearn.datasets import fetch_20newsgroups

# 1. 准备数据(20newsgroups 数据集)
docs = fetch_20newsgroups(subset='all', remove=('headers', 'footers', 'quotes'))['data'][:1000]

# 2. 创建 BERTopic 模型
topic_model = BERTopic(
    language="english",
    calculate_probabilities=True,
    verbose=True
)

# 3. 训练模型(自动完成 Embedding + 降维 + 聚类)
topics, probs = topic_model.fit_transform(docs)

# 4. 查看主题
print(f"发现 {len(set(topics))} 个主题")
print("\n主题及其关键词:")
for topic_id in set(topics):
    if topic_id == -1:  # 噪声主题
        continue
    print(f"\n主题 {topic_id}:")
    print(topic_model.get_topic(topic_id)[:5])  # 前5个关键词

# 输出示例:
# 主题 0:
# [('space', 0.012), ('nasa', 0.011), ('launch', 0.009), ...]

# 5. 可视化主题
topic_model.visualize_topics().show()

# 6. 查看文档属于哪个主题
sample_doc = "NASA launches new space mission"
topic, prob = topic_model.transform([sample_doc])
print(f"\n文档主题: {topic[0]}, 置信度: {prob[0]:.3f}")

3. 中文 BERTopic

python 复制代码
from bertopic import BERTopic
from sentence_transformers import SentenceTransformer

# 1. 中文文档
docs = [
    "人工智能技术快速发展",
    "深度学习在图像识别中应用广泛",
    "自然语言处理是AI的重要分支",
    "篮球比赛非常激烈",
    "足球世界杯吸引全球关注",
    "奥运会运动员奋勇拼搏",
    "川菜麻辣鲜香",
    "粤菜清淡爽口",
    "日本料理精致美观"
]

# 2. 使用中文 Embedding 模型
embedding_model = SentenceTransformer("moka-ai/m3e-base")

# 3. 创建模型
topic_model = BERTopic(
    embedding_model=embedding_model,
    language="chinese (simplified)",
    min_topic_size=2  # 最小主题大小
)

# 4. 训练
topics, probs = topic_model.fit_transform(docs)

# 5. 查看主题
for topic_id in set(topics):
    if topic_id != -1:
        print(f"主题 {topic_id}: {topic_model.get_topic(topic_id)[:3]}")

4. 动态主题建模(时间维度)

跟踪主题随时间的演变:

python 复制代码
from bertopic import BERTopic
import pandas as pd

# 1. 准备带时间戳的数据
docs = [...]
timestamps = ["2023-01-01", "2023-02-01", ...]  # 对应每个文档的时间

# 2. 训练模型
topic_model = BERTopic()
topics, probs = topic_model.fit_transform(docs)

# 3. 随时间演变分析
topics_over_time = topic_model.topics_over_time(docs, timestamps)

# 4. 可视化
topic_model.visualize_topics_over_time(topics_over_time).show()

八、层次化聚类实战

层次化聚类构建树状结构(Dendrogram),适合发现嵌套的主题层次。

1. 凝聚层次聚类 (Agglomerative)

原理:

  1. 每个样本初始为一个簇
  2. 不断合并最相似的两个簇
  3. 直到所有样本合并为一个簇

2. 完整代码

python 复制代码
from scipy.cluster.hierarchy import dendrogram, linkage
from sklearn.cluster import AgglomerativeClustering
import matplotlib.pyplot as plt

# 1. 准备数据
texts = [
    "深度学习", "神经网络", "机器学习",
    "足球", "篮球", "排球",
    "川菜", "粤菜", "湘菜"
]

model = SentenceTransformer('moka-ai/m3e-base')
embeddings = model.encode(texts)

# 2. 计算层次聚类链接矩阵
linkage_matrix = linkage(embeddings, method='ward')

# 3. 绘制树状图
plt.figure(figsize=(10, 6))
dendrogram(linkage_matrix, labels=texts, leaf_font_size=12)
plt.title("层次化聚类树状图")
plt.xlabel("样本")
plt.ylabel("距离")
plt.tight_layout()
plt.savefig('hierarchical_clustering.png', dpi=150)
plt.show()

# 4. 切分为指定数量的簇
agg_clustering = AgglomerativeClustering(n_clusters=3)
labels = agg_clustering.fit_predict(embeddings)

for text, label in zip(texts, labels):
    print(f"簇{label}: {text}")

3. 层次化主题建模

结合 BERTopic 实现层次化主题:

python 复制代码
from bertopic import BERTopic
from sklearn.cluster import AgglomerativeClustering

# 1. 训练 BERTopic
topic_model = BERTopic(hdbscan_model=AgglomerativeClustering(n_clusters=10))
topics, probs = topic_model.fit_transform(docs)

# 2. 构建主题层次
hierarchical_topics = topic_model.hierarchical_topics(docs)

# 3. 可视化层次结构
topic_model.visualize_hierarchy(hierarchical_topics=hierarchical_topics).show()

九、分类与聚类评估指标

评估是机器学习的生命线,没有评估就无法优化。

1. 分类评估指标

混淆矩阵与核心指标

准确率 (Accuracy) :整体正确率
Accuracy = T P + T N T P + T N + F P + F N \text{Accuracy} = \frac{TP + TN}{TP + TN + FP + FN} Accuracy=TP+TN+FP+FNTP+TN

精确率 (Precision) :预测为正的样本中,真正为正的比例
Precision = T P T P + F P \text{Precision} = \frac{TP}{TP + FP} Precision=TP+FPTP

召回率 (Recall) :实际为正的样本中,被正确预测的比例
Recall = T P T P + F N \text{Recall} = \frac{TP}{TP + FN} Recall=TP+FNTP

F1分数 :精确率和召回率的调和平均
F 1 = 2 × Precision × Recall Precision + Recall F1 = 2 \times \frac{\text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}} F1=2×Precision+RecallPrecision×Recall

指标选择指南:

场景 推荐指标 原因
数据平衡 Accuracy 简单直观
数据不平衡 F1-score 综合考虑
垃圾邮件检测 Precision 避免误杀
疾病诊断 Recall 避免漏诊
多分类 Macro-F1 平等对待各类

2. 聚类评估指标

有标签评估

调整兰德指数 (ARI):范围[-1, 1],1表示完美聚类

归一化互信息 (NMI):范围[0, 1],1表示完美聚类

无标签评估

轮廓系数 (Silhouette Score)
s ( i ) = b ( i ) − a ( i ) max ⁡ ( a ( i ) , b ( i ) ) s(i) = \frac{b(i) - a(i)}{\max(a(i), b(i))} s(i)=max(a(i),b(i))b(i)−a(i)

  • 范围:[-1, 1],越接近1越好
  • a ( i ) a(i) a(i):样本到同簇其他点的平均距离
  • b ( i ) b(i) b(i):样本到最近其他簇的平均距离

3. 完整评估代码

python 复制代码
"""
功能:分类与聚类评估工具
依赖:scikit-learn>=1.3.0, seaborn>=0.12.0
"""
from sklearn.metrics import (
    classification_report, confusion_matrix,
    silhouette_score, davies_bouldin_score
)
import seaborn as sns
import matplotlib.pyplot as plt

def evaluate_classification(y_true, y_pred, labels=None):
    """分类评估"""
    print("=" * 50)
    print("分类评估结果")
    print("=" * 50)
    print(classification_report(y_true, y_pred, target_names=labels))

    # 混淆矩阵可视化
    cm = confusion_matrix(y_true, y_pred)
    plt.figure(figsize=(8, 6))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
                xticklabels=labels, yticklabels=labels)
    plt.title('混淆矩阵')
    plt.xlabel('预测标签')
    plt.ylabel('真实标签')
    plt.tight_layout()
    plt.savefig('confusion_matrix.png', dpi=150)
    plt.show()

def evaluate_clustering(X, labels_pred):
    """聚类评估"""
    silhouette = silhouette_score(X, labels_pred)
    db_index = davies_bouldin_score(X, labels_pred)

    print("=" * 50)
    print("聚类评估结果")
    print("=" * 50)
    print(f"轮廓系数 (Silhouette): {silhouette:.3f} [越接近1越好]")
    print(f"DB指数: {db_index:.3f} [越小越好]")

# 使用示例
y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 1, 2, 0, 2, 2]
evaluate_classification(y_true, y_pred, ['科技', '体育', '美食'])

十、实战案例:客服工单自动分类系统

1. 系统架构

复制代码
用户工单 → 文本清洗 → Embedding → 分类器 → 路由到对应客服组
                              ↓
                          定期重训练(数据漂移检测)

2. 完整实现代码

python 复制代码
"""
功能:客服工单自动分类系统(完整生产级实现)
特点:数据增强、模型持久化、性能监控
依赖:sentence-transformers>=2.3.0, scikit-learn>=1.3.0, joblib>=1.3.0
"""
import pandas as pd
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
import joblib
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')

# ============ 1. 数据准备与增强 ============
def prepare_data():
    """准备训练数据(实际应从数据库读取)"""
    data = pd.DataFrame({
        'text': [
            # 账号问题(扩展样本)
            "账号无法登录", "密码忘记了", "登录失败", "账号被锁定",
            "验证码收不到", "无法注册新账号", "账号异常",

            # 商品问题
            "商品质量差", "收到破损商品", "产品有瑕疵", "商品与描述不符",
            "想要退货", "商品功能故障", "产品尺寸不对",

            # 物流问题
            "物流太慢", "快递丢失", "配送延迟", "快递还没到",
            "物流信息不更新", "配送地址错误", "快递被签收但未收到",

            # 售后问题
            "如何申请退款", "退款进度查询", "发票开具", "保修期多久",
            "售后服务态度差", "维修申请", "换货流程"
        ],
        'label': [0]*7 + [1]*7 + [2]*7 + [3]*7  # 0:账号 1:商品 2:物流 3:售后
    })
    return data

# ============ 2. 模型训练 ============
def train_classifier():
    """训练分类器"""
    print("=" * 60)
    print("客服工单分类系统 - 模型训练")
    print("=" * 60)

    # 加载数据
    data = prepare_data()
    print(f"数据集大小: {len(data)} 条")
    print(f"类别分布:\n{data['label'].value_counts()}\n")

    # 生成 Embedding
    print("加载 Embedding 模型...")
    model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
    embeddings = model.encode(
        data['text'].tolist(),
        normalize_embeddings=True,
        show_progress_bar=True
    )

    # 划分数据集
    X_train, X_test, y_train, y_test = train_test_split(
        embeddings, data['label'],
        test_size=0.2,
        random_state=42,
        stratify=data['label']  # 保持类别比例
    )

    # 训练分类器
    print("\n训练逻辑回归分类器...")
    classifier = LogisticRegression(
        max_iter=1000,
        class_weight='balanced',  # 处理类别不平衡
        random_state=42
    )
    classifier.fit(X_train, y_train)

    # 交叉验证
    cv_scores = cross_val_score(classifier, X_train, y_train, cv=3)
    print(f"交叉验证准确率: {cv_scores.mean():.3f} (+/- {cv_scores.std():.3f})")

    # 测试集评估
    y_pred = classifier.predict(X_test)
    category_names = ['账号问题', '商品问题', '物流问题', '售后问题']

    print("\n测试集评估:")
    print(classification_report(y_test, y_pred, target_names=category_names))

    # 保存模型
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    model_path = f"ticket_classifier_{timestamp}.pkl"
    joblib.dump({
        'classifier': classifier,
        'embedding_model_name': 'BAAI/bge-large-zh-v1.5',
        'categories': category_names
    }, model_path)
    print(f"\n✅ 模型已保存: {model_path}")

    return model, classifier, category_names

# ============ 3. 在线推理 ============
class TicketClassifier:
    """工单分类器(生产环境)"""

    def __init__(self, model_path):
        """加载模型"""
        checkpoint = joblib.load(model_path)
        self.classifier = checkpoint['classifier']
        self.categories = checkpoint['categories']
        self.embedding_model = SentenceTransformer(
            checkpoint['embedding_model_name']
        )
        print(f"✅ 模型已加载,支持类别: {self.categories}")

    def predict(self, text: str) -> dict:
        """
        分类单条工单

        返回:
            {
                'category': str,  # 类别名称
                'label': int,     # 类别ID
                'confidence': float,  # 置信度
                'all_probs': dict  # 所有类别的概率
            }
        """
        # 生成 Embedding
        embedding = self.embedding_model.encode(
            [text],
            normalize_embeddings=True
        )

        # 预测
        label = self.classifier.predict(embedding)[0]
        probs = self.classifier.predict_proba(embedding)[0]

        return {
            'category': self.categories[label],
            'label': int(label),
            'confidence': float(probs[label]),
            'all_probs': {
                cat: float(prob)
                for cat, prob in zip(self.categories, probs)
            }
        }

    def predict_batch(self, texts: list[str]) -> list[dict]:
        """批量分类(提升吞吐量)"""
        embeddings = self.embedding_model.encode(
            texts,
            normalize_embeddings=True,
            batch_size=32,
            show_progress_bar=True
        )

        labels = self.classifier.predict(embeddings)
        probs = self.classifier.predict_proba(embeddings)

        results = []
        for label, prob in zip(labels, probs):
            results.append({
                'category': self.categories[label],
                'label': int(label),
                'confidence': float(prob[label])
            })
        return results

# ============ 4. 使用示例 ============
if __name__ == "__main__":
    # 训练模型
    emb_model, clf, categories = train_classifier()

    # 模拟推理
    print("\n" + "=" * 60)
    print("推理测试")
    print("=" * 60)

    test_tickets = [
        "我的账号登不上去了",
        "收到的商品有划痕",
        "快递三天了还没送到",
        "退款什么时候到账"
    ]

    for ticket in test_tickets:
        embedding = emb_model.encode([ticket], normalize_embeddings=True)
        label = clf.predict(embedding)[0]
        prob = clf.predict_proba(embedding)[0][label]
        print(f"工单: {ticket}")
        print(f"  → {categories[label]} (置信度: {prob:.3f})\n")

3. 生产环境部署建议

3.1 性能优化
python 复制代码
"""
优化策略:
1. 模型预加载(避免每次请求都加载)
2. Batch推理(累积请求批量处理)
3. GPU加速(sentence-transformers支持CUDA)
"""

# 使用 ONNX 加速推理(可选)
from optimum.onnxruntime import ORTModelForFeatureExtraction
model = ORTModelForFeatureExtraction.from_pretrained(
    'BAAI/bge-large-zh-v1.5',
    export=True,
    provider='CUDAExecutionProvider'  # GPU加速
)
3.2 监控与告警
python 复制代码
"""
关键监控指标:
1. 分类准确率(定期人工标注验证)
2. 置信度分布(低置信度样本需人工复核)
3. 数据漂移检测(类别分布变化)
4. 推理延迟(P50/P95/P99)
"""

# 置信度阈值策略
def classify_with_threshold(text, threshold=0.7):
    result = classifier.predict(text)
    if result['confidence'] < threshold:
        return {'category': '转人工', 'reason': '低置信度'}
    return result
3.3 持续优化
时间节点 优化策略
上线第1周 每天人工标注100条,快速修正明显错误
第1个月 收集1000+标注数据,重新训练
第3个月 引入主动学习,优先标注困难样本
第6个月 评估是否需要更换Embedding模型

十一、本章小结

1. 核心要点回顾

场景 推荐方案 核心优势 成本/复杂度
探索分析 BERTopic / HDBSCAN 自动发现主题,无需标注
少量数据 (<100) Few-Shot / LLM API 启动快,无需训练 中/高(API费用)
中等数据 (1k-10k) SetFit / LoRA 效果好,训练快
生产系统 (>10k) Embedding + LR / MLP 推理极快,并发高 低(推理成本)

2. 2025年技术趋势

  1. RAG与分类融合:分类不再是终点,而是RAG的路由节点(语义路由)。
  2. 小模型崛起:对于特定分类任务,微调后的0.5B模型(如Qwen2-0.5B)效果由于通用大模型。
  3. 合成数据增强:使用GPT-4生成困难样本(Hard Negatives)来训练小模型,成为标准范式。

3. 应用建议

  • 从简单开始:先用 LLM API 跑通流程,验证价值。
  • 数据为王:与其纠结模型架构,不如清洗几百条高质量数据。
  • 持续监控:分类模型会随时间退化,必须建立 Human-in-the-loop 机制。

4. 延伸阅读


下一章预告: 第2章:检索增强生成 (RAG) 原理

在下一章中,我们将深入 RAG 架构,探讨如何让 LLM "外挂大脑",解决幻觉和知识时效性问题。

相关推荐
XX風2 小时前
3.2K-means
人工智能·算法·kmeans
feasibility.2 小时前
在OpenCode使用skills搭建基于LLM的dify工作流
人工智能·低代码·docker·ollama·skills·opencode·智能体/工作流
进击monkey2 小时前
PandaWiki:开源企业级AI知识库工具,基于RAG架构的私有化部署方案
人工智能·开源
zy_destiny3 小时前
【工业场景】用YOLOv26实现桥梁检测
人工智能·深度学习·yolo·机器学习·计算机视觉·目标跟踪
2501_941837263 小时前
蘑菇可食用性分类识别_YOLO11分割模型实现与优化_1
人工智能·数据挖掘
2501_941837263 小时前
基于YOLO11-Aux改进的圣女果目标检测实现
人工智能·目标检测·计算机视觉
莫有杯子的龙潭峡谷3 小时前
在 Windows 系统上安装 OpenClaw
人工智能·node.js·安装教程·openclaw
Funny_AI_LAB3 小时前
AI Agent最新重磅综述:迈向高效智能体,记忆、工具学习和规划综述
人工智能·学习·算法·语言模型·agi
zhangshuang-peta3 小时前
超越Composio:ContextForge与Peta作为集成平台的替代方案
人工智能·ai agent·mcp·peta