一句话总结
文本分类 = 给一段文本贴一个标签.
阶段一把文本变成了向量,本篇讲如何定义"标签体系",如何衡量分类效果(Precision / Recall / F1),以及如何把"群聊对骂检测"建模为一个分类问题.
从向量到标签:分类器做什么
上一阶段结尾,我们得出一个关键结论:向量空间中同类文本自然聚集,但模型不知道哪个簇是"骂人"--这需要标注数据来定义.
分类器就是那条"国界线"的数学实现:
|
最简单的分类器(逻辑回归)的计算过程:
|
其中 W 是训练出来的权重向量(和输入同维度,768 维). 直觉理解:W 定义了空间中的一个"方向标杆"--点积运算的几何含义是计算输入向量在 W 方向上的投影长度. 投影越长(score 越大),越像对骂;投影越短甚至反方向(score 为负),越像正常.
逻辑回归 = Go 中的 feature flag 权重打分. 给每个维度一个权重("脏话词频"权重高,"日常用词"权重低),对输入向量加权求和得到总分. 分数高于阈值就触发干预. 逻辑回归就是自动学出这组权重.
W 指向哪个方向是约定,不是固定的
说"W 指向对骂方向"只是因为用 label=1 表示对骂. 如果反过来定义 label=1 为正常,训练出来的 W 就指向正常方向--score 大的反而是正常文本.
本质上 W 指向的是 "label=1 那一类" 的方向,具体是什么语义完全取决于标签定义. 不存在"默认指向哪边"--是标注规则决定的.
整个阶段二的核心就一件事:让这个函数从数据中学会怎么画线.
NLP 任务类型对比:分类在什么位置
NLP 中有很多不同的任务形态,分类只是其中一种. 先看全局:
| 任务类型 | 输入 | 输出 | 例子 |
|---|---|---|---|
| 文本分类 | 一段文本 | 一个/多个标签 | 对骂检测,情感分析,垃圾邮件过滤 |
| 序列标注 | 一段文本 | 每个 token 一个标签 | 命名实体识别("北京" → 地名),分词 |
| 文本生成 | 一段文本/指令 | 一段新文本 | 机器翻译,摘要,对话回复 |
| 文本匹配 | 两段文本 | 相似度/关系 | 语义搜索,问答匹配,重复问题检测 |
分类 = Go 的 switch-case, 输入一个请求,返回一个枚举值.
序列标注 = 逐字节解析协议, 输入流中每个字节都要标记类型.
生成 = io.Writer, 给定输入后不断产出输出.
对骂检测是典型的文本分类任务. 后面如果要做意图识别("禁言张三 10 分钟" → 提取动作+对象+时长),那会涉及序列标注--但现在先聚焦分类.
分类任务的三种变体
同样是"贴标签",根据标签体系的不同,分成三种建模方式:
二分类(Binary Classification)
只有两个类别:是/否,正面/负面,对骂/正常.
|
这是最简单的形态,也是起步选择--先把对骂检测建模为二分类.
多分类(Multi-class Classification)
多个类别,每条文本只属于一个类:
|
当需要区分对骂的子类型(人身攻击,地域歧视,性别歧视等),就需要多分类.
多标签(Multi-label Classification)
一条文本可以同时属于多个类别:
|
"你这种垃圾还好意思带节奏"既是对骂也是带节奏--多标签允许一条文本有多个标签.
建模决策:从简单开始
实战建议是先跑通二分类(对骂 vs 正常),验证可行后再扩展为多分类或多标签. 过早做细粒度分类会导致每个类别的标注数据太少,模型学不好.
评估指标:怎么衡量"分得准不准"
模型训练出来了,怎么知道效果好不好? 不能只看"猜对了几个"--需要更精确的指标.
混淆矩阵:一切指标的起点
假设对骂检测模型在 100 条测试数据上的预测结果:
|
四个格子的含义:
- TP(True Positive):模型说"对骂",确实是对骂 → 正确命中
- FN(False Negative):模型说"正常",实际是对骂 → 漏报(该管的没管)
- FP(False Positive):模型说"对骂",实际是正常 → 误报(误伤正常用户)
- TN(True Negative):模型说"正常",确实是正常 → 正确放行
TP = 真故障被监控发现了.
FN = 故障发生了但监控没报警(最危险).
FP = 没故障但监控乱报(狼来了).
TN = 没故障也没误报(正常状态).
精确率(Precision):模型说"对骂"时,有多少是真的
|
Precision 高 = 误报少. 如果系统对误报敏感(误禁言正常用户会引起投诉),需要追求高 Precision.
召回率(Recall):实际对骂中,模型抓住了多少
|
Recall 高 = 漏报少. 如果系统对漏报敏感(骂人消息没被拦截,群环境恶化),需要追求高 Recall.
F1 Score:Precision 和 Recall 的平衡
|
Precision 和 Recall 的跷跷板效应
两者几乎总是此消彼长:
阈值调高(只有 >95% 置信度才判为对骂) → Precision 上升,Recall 下降(谨慎了,误报少但漏报多).
阈值调低(>50% 就判为对骂) → Recall 上升,Precision 下降(激进了,漏报少但误报多).
没有"最优阈值"--取决于业务容忍度.
为什么不只看准确率(Accuracy)
Accuracy = 预测正确的总数 / 总样本数 = (TP + TN) / 全部 = (35 + 52) / 100 = 87%
87% 看起来不错? 但考虑一个极端情况:
|
这就是类别不平衡的陷阱--当某类数据量远大于另一类时,Accuracy 会骗人. 在对骂检测中,正常发言远多于对骂发言(可能 20:1),所以必须用 F1 作为主指标.
类比 Go 服务的监控:如果 99.9% 的请求都是成功的,一个"永远返回 success"的监控系统准确率是 99.9%--但它对发现问题毫无价值. 需要的是故障召回率,不是总体准确率.
把对骂检测建模为分类任务
现在用上面的框架来设计具体任务.
任务定义
| 要素 | 选择 | 理由 |
|---|---|---|
| 任务类型 | 二分类(起步) | 先跑通再扩展,避免标注数据分散 |
| 标签体系 | 对骂(1) / 正常(0) | 明确的二值判断,标注一致性高 |
| 输入粒度 | 单条消息 | 最简单;后续可扩展到上下文窗口 |
| 主指标 | F1-score | 对骂占比低,Accuracy 不可靠 |
| 辅助指标 | Precision, Recall 分别看 | 根据业务侧重调整阈值 |
业务权衡:Precision vs Recall 该偏向哪边
对骂检测的业务场景决定了指标偏好:
| 策略 | 偏向 | 后果 | 适用场景 |
|---|---|---|---|
| 宁可漏报,不可误报 | 高 Precision | 少数对骂溜走,但不会误伤正常用户 | 用户群体敏感,投诉成本高 |
| 宁可误报,不可漏报 | 高 Recall | 偶尔误判正常发言,但对骂一个不漏 | 强管控群,内容安全合规 |
| 平衡 | F1 最大化 | 两边都有少量错误,但整体最优 | 大多数通用场景 |
场景建议
群 AI 管理的干预动作(禁言)有较高成本--误禁正常用户会引起不满.
建议初期偏向高 Precision:宁可漏掉几条对骂(让大模型兜底二次判断),也不要误伤正常用户.
上线后再根据实际数据逐步调低阈值提升 Recall.
训练-评估循环:模型如何学会分类
有了标注数据和评估指标,训练过程就是一个迭代循环:
|
关键概念:
- 训练集:模型用来学习的数据,看过答案
- 测试集:模型没见过的数据,用来评估真实表现
- 为什么必须分开? 防止模型"背答案"--在训练集上全对,遇到新数据就不行(过拟合)
训练集 = 课后习题(可以看答案反复做).
测试集 = 期末考卷(做的时候不能看答案).
如果考卷题目和习题一模一样,100 分不代表真学会了.
跑通分类需要什么
把阶段二要做的事情拆成具体的输入输出:
| 需要什么 | 具体是什么 | 哪篇讲 |
|---|---|---|
| 标注数据 | 足够多的"文本+标签"对,标注一致 | 02 篇 |
| 特征提取 | 文本 → 向量(TF-IDF 或 BERT) | 阶段一已完成 |
| 分类模型 | 逻辑回归 / FastText / TextCNN / BERT | 03-04 篇 |
| 损失函数 | 告诉模型"预测错了多少"的数学公式 | 03-04 篇 |
| 评估流程 | P / R / F1 计算 + 错误分析 | 本篇 + 05 篇 |
其中标注数据是最关键的--模型效果的天花板取决于数据质量,不是模型复杂度. 下一篇会讲如何设计标注规范,如何从零构建数据集.
完整流水线预览
把分类任务的完整流水线画出来,后续每篇笔记聚焦其中一段:
|
蓝色部分(特征提取)阶段一已经搞定了;黄色部分(分类模型)是阶段二的核心内容.
阈值不是固定的
模型输出的是"属于对骂的概率"(如 0.73),阈值决定了"多高的概率才判定为对骂".
默认 0.5,但可以调:调高 → 更谨慎(Precision 高);调低 → 更激进(Recall 高).
这是一个不需要重新训练模型就能调节行为的旋钮.
从阶段一的种子数据出发
阶段一的练习中已经有了 40 条标注数据(20 对骂 + 20 正常)和对应的 TF-IDF 向量, BERT [CLS] 向量. 现在可以直接在上面跑一个最简单的分类器看看效果:
|
这段代码就是阶段二的第一个"能跑的 baseline"--用阶段一产出的向量 + 最简单的线性模型. 下一篇讲数据标注时,会正式写这段代码的完整练习版本.
40 条数据太少
40 条数据只能看趋势,不能得出可靠结论. 真正能训练出可用模型的最低门槛大约是每类 200-500 条. 阶段二的 02 篇会讲如何快速扩充数据集.
快速回顾
- 文本分类:输入一段文本,输出一个标签;对骂检测是最典型的二分类任务
- 三种变体:二分类(起步) → 多分类(细分类型) → 多标签(一条文本多个标签)
- 评估指标:Precision(误报率的反面),Recall(漏报率的反面),F1(平衡两者)
- 不看 Accuracy:类别不平衡时 Accuracy 骗人,用 F1 作为主指标
- 业务权衡:对骂检测初期应偏向高 Precision(不误伤正常用户),漏报由大模型兜底
- 训练循环:标注数据 → 划分集合 → 训练 → 评估 → 分析错误 → 迭代
动手练习
- 跑第一个分类器:加载阶段一保存的
cls_vectors.npz,用sklearn.linear_model.LogisticRegression跑一个二分类,打印classification_report - 观察小数据波动:调整测试集比例(0.2 / 0.3 / 0.5),观察 F1 的波动--体会小数据的不稳定性
- 对比表示方法:用 TF-IDF 向量替代 [CLS] 向量跑同一个分类器,对比 F1--验证哪种表示更适合分类