NLP 模型的整体准确率不能说明它是否正确处理否定、无关人名或一句补充评价。行为测试先规定某一能力在特定输入上应该怎样表现,再检查原句与变换句的输出。CheckList 提供的三种测试思路是最小功能测试 MFT、不变性测试 INV、方向变化测试 DIR,可以把“感觉模型不稳定”变成可回查的失败样本。
本文以电影评论的正负分类为例,采用 CheckList 的测试类型,用 Python 标准库执行一份测试表。没有安装或运行 CheckList 库,也没有调用真实分类模型;句子与预测得分都是人为构造的演示,脚本实际验证的是判断逻辑和 CSV 输出。

三种测试分别检查什么
| 类型 | 要问的问题 | 电影评论示例 | 先人工确认什么 |
|---|---|---|---|
| MFT,最小功能 | 明确、简单的输入是否满足基本要求? | “这部电影非常精彩。”应识别为正面 | 输入没有反讽或其他改变标签的上下文 |
| INV,不变性 | 不应影响任务的修改,会不会改变结果? | 只追加一个无评价的导演姓名,类别应保持 | 新增内容确实不改变评价对象和态度 |
| DIR,方向变化 | 某种有意义的变化,是否带来约定方向的结果? | 加入“但结尾非常糟糕”,本例要求整体正面得分不增加 | 该变化适合你的任务口径,而非机械套用通用规则 |
CheckList 官方项目展示 MFT、INV、DIR 的测试接口及期望函数;CheckList 论文介绍按能力组织行为测试的思路。它们提供方法框架,实际测试的标签、变换和容错值仍需根据你的任务确定。
先冻结任务与人工期望
本例只判断“评论对整部电影的正负评价”,用一个正面得分和演示阈值 0.5 得到类别。真实模型可能有中性、多类或不同输出映射,接入时应先按模型配置转换,不能直接套这个阈值。
先列出原句、变换句、测试类型及期望,再获取模型输出;不要先看到结果,才把不符合的句子删掉。下表的得分是作者故意构造的失败例,并非模型推理概率。
| 编号 | 输入或原句→变换句 | 演示得分 | 人工期望 |
|---|---|---|---|
| m1 | 这部电影非常精彩。 | 0.90 | 正面 |
| m2 | 这部电影令人失望。 | 0.20 | 负面 |
| i1 | 这部电影很精彩。→这部电影很精彩。导演名叫李青。 | 0.90→0.35 | 类别保持;此演示输出违反期望 |
| d1 | 电影还可以。→电影还可以,但结尾非常糟糕。 | 0.60→0.80 | 加入负面评价后正面得分不增加;此演示输出违反期望 |
INV 检查类别不变,不要求两个分数完全相等。DIR 检查方向,不把变换句强制标成某个类别;这里容忍值为 0,实际可按预先定义的规则增加容忍值,并记录在报告中。分数变化只反映模型输出,不能直接解释为满意度改变多少。
运行一份完整的测试表
代码在 Windows、Python 3.11.15 执行通过,只用标准库。保存为 behavior_checks.py,在空目录运行 python behavior_checks.py。先保留演示数据确认报表;有真实预测入口后,替换 p、before_p 与 after_p,原句及人工期望另存为冻结版本。
import csv
import math
from pathlib import Path
# All sentences and scores are constructed examples, not model predictions.
minimum = [
{'id': 'm1', 'text': '这部电影非常精彩。', 'expected': 'positive', 'p': 0.9},
{'id': 'm2', 'text': '这部电影令人失望。', 'expected': 'negative', 'p': 0.2},
]
pairs = [
{'id': 'i1', 'type': 'INV', 'original': '这部电影很精彩。',
'changed': '这部电影很精彩。导演名叫李青。', 'before_p': 0.9, 'after_p': 0.35},
{'id': 'd1', 'type': 'DIR', 'original': '电影还可以。',
'changed': '电影还可以,但结尾非常糟糕。', 'before_p': 0.6, 'after_p': 0.8},
]
def label(p):
if not isinstance(p, (int, float)) or not math.isfinite(p) or not 0 <= p <= 1:
raise ValueError('正面得分必须是 0 到 1 的有限数')
return 'positive' if p >= 0.5 else 'negative'
report = []
for row in minimum:
actual = label(row['p'])
report.append({'id': row['id'], 'type': 'MFT', 'original': row['text'],
'changed': '', 'before_p': row['p'], 'after_p': '',
'passed': actual == row['expected'], 'expected': row['expected']})
for row in pairs:
before, after = label(row['before_p']), label(row['after_p'])
if row['type'] == 'INV':
passed = before == after
expectation = '类别不变'
elif row['type'] == 'DIR':
passed = row['after_p'] <= row['before_p']
expectation = '加入负面评价后正面得分不增加'
else:
raise ValueError('未知测试类型')
report.append({**row, 'passed': passed, 'expected': expectation})
with Path('behavior_report.csv').open('w', encoding='utf-8-sig', newline='') as file:
writer = csv.DictWriter(file, fieldnames=list(report[0]))
writer.writeheader()
writer.writerows(report)
for kind in ('MFT', 'INV', 'DIR'):
group = [r for r in report if r['type'] == kind]
print(kind, 'passed', sum(r['passed'] for r in group), 'total', len(group))
print('failed_ids', [r['id'] for r in report if not r['passed']])
本地执行输出:
MFT passed 2 total 2
INV passed 0 total 1
DIR passed 0 total 1
failed_ids ['i1', 'd1']
核对 behavior_report.csv 应有四行,i1 和 d1 的 passed 为 False。两个 MFT 通过,不代表 INV 和 DIR 也通过,更不能从四个样例推出总体分类准确率。
接真实模型时,先核对每条输入都有对应输出,再用同一模型版本与参数分别预测原句、变换句。下载或调用失败要记录为执行失败,不能把缺失得分填成 0 后当负面分类。多类别模型还应指定 DIR 检查哪一类的得分。
测试句本身也可能写错
不变性不能随意假设。换一个商品品牌、评价对象或身份,可能改变句子实际含义;把“今天”换成“明天”也可能改变要处理的任务。变换后先由人确认任务结果是否应该不变,再作为 INV。拼写扰动如果把关键否定词删掉,同样不再是保持语义的变化。
方向性也不是固定语言规律。“虽然结尾糟糕,但整体依然非常出色”包含更多上下文,不能仅数负面词就规定输出方向。本例只演示预先约定的一对短句;扩充时保存每对变化及人工理由,对含糊或争议句单独标记,不强制纳入通过率。
失败之后怎样改进并回归
- 先回看失败原句与预期,确认不是错误测试或结果映射问题。
- 把错误按能力归组,例如否定、无关实体、复合评价、指代等,不只记录一句“模型失败”。
- 调整数据、提示、模型或阈值时保留旧版;不能用同一批失败句既做训练又证明泛化改进。
- 复测原失败句及同能力的新句,同时重跑其他能力,检查修好一个边界是否破坏另一种。
下一步先为你的分类任务建立一小组经人审定的 MFT、INV、DIR 表,各类型分别记录通过与失败。再接已有预测入口填入真实结果,保留版本、期望与失败清单。本文官方方法资料核对日期为 2026 年 10 月 1 日,未做任何模型性能或产品效果评测。
如果还没有中文情感分类的预测入口,可阅读本站用 Transformers 加载中文评论模型并核对误判,按模型卡及自己的环境取得真实输出,再接入这份测试表。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/32884.html