拿字面相似度当分数,六条答案里偏差最大的是「加了否定」:相似度 0.889、本该 0 分, 折算成 8.9 分。而「同义替换」相似度只有 0.593、本该满分,被扣 4.1 分。 两类错方向完全相反:一个把对的判错,一个把错的判对。
上一节估的是「学生水平」,这一节处理的是「这一道题答得对不对」。 看着比估能力简单,实际上更容易出事故——因为它直接把分数给了学生。
一、把「像不像」当「对不对」,先把错的方向算清楚
最省事的自动批改做法: 把学生答案和标准答案都切成字词,算一个相似度,乘 10 当分数。代码不超过二十行,跑得也快。 Demo 用六条真实形态的答案试了一遍,标准答案是「光合作用需要光照、水和二氧化碳」:
| 情形 | 相似度 | 人工分 | 折算差 |
|---|---|---|---|
| 标准答案 | 1.000 | 10 | +0.0 |
| 删掉一个词 | 0.647 | 8 | −1.5 |
| 同义替换 | 0.593 | 10 | −4.1 |
| 加了否定 | 0.889 | 0 | +8.9 |
| 只答一半 | 0.734 | 5 | +2.3 |
| 完全跑题 | 0.000 | 0 | +0.0 |
| 「折算差」= 相似度 × 10 减人工分,它衡量的是「如果拿相似度当分数,会偏多少」。 | |||
| 看最扎眼的两行:「加了否定」差 +8.9,「同义替换」差 −4.1。一个多给了近 9 分,一个白扣了 4 分。 |
二、两类错,方向相反
① 低估同义改写。「光照」换成「日光」、 「水」换成「水分」——意思一字不差,字面上一个二元组都对不上。所以相似度掉到 0.593, 学生答对了却被判错。 ② 高估否定与颠倒。「不需要」只多了一个「不」字,字面几乎没变(0.889),意思完全反了。 这是自动批改最危险的一类错——它给零分的答案打 8.9 分。 为什么说「方向相反」很要命?因为如果两类错方向一致(都偏低),你还能加一个统一补偿; 方向相反就说明这个指标本身和「对错」不是同一个维度,调参数救不了。 而且这两类错是可以提前预判的。任何一次教研组抽样都能发现: 改写多的题(语文、政治、历史)会系统性地低估,判断句多的题(物理、 化学的是非判断)会系统性地高估。 先按题型分开看,就能在上线前把风险点找出来。
三、判「对不对」和判「像不像」是两件事
判对错需要语义表示, 而且必须专门处理否定词和量词。「不」「没有」「未」「禁止」这几个词出现时, 必须单独判断它修饰的是哪个谓语。 判「像不像」可以用字面。作文、开放问答、方案设计这类, 你只需要一个粗排序(哪几篇更接近参考答案),不需要判对错——这种场景下字面相似度是够用的。 所以关键的分界线不是「用什么算法」,是「这个任务要不要判对错」。判对错 → 必须语义 + 否定处理; 判相似 → 字面就能起步。 把这两件事混在一起,才会出现「上线了才发现作文批改还行、选择题批改乱套」这种事。 顺便说一个实操细节:客观题(选择、填空、计算)根本不需要大模型。 答案有限、比对精确,用规则就够,而且可解释、可审计。上大模型反而引入不确定性—— 同一份卷子批两次给两个分,家长第一个不答应。
四、落地:人机协同的分层
第一层,规则批。 客观题、填空题、公式题, 用标准答案精确匹配加等价式化简。这一层要覆盖大部分题量,因为它零成本、零争议。 第二层,模型批 + 置信度。 简答题用模型打分,但必须让它同时输出置信度。 高置信度的直接过,低置信度的转人工。 这就是「人机协同」的真实含义——不是人审全部,是只审模型没把握的。 第三层,人工抽检。 按比例随机抽,重点抽查「模型分和人工分差异大」的那部分。 抽检不是为了改分,是为了发现模型在什么题型上系统性偏。 最后一条最容易被忽略:分数必须能解释。学生问「我为什么被扣 2 分」, 系统得能指出「这几个要点没写到」,而不是「模型觉得 8 分」。 不能解释的自动批改,教研组一定不会真用。
五、动手 Demo:自动批改:字面相似度把「加否定」判成 8.9 分
取六条形态不同的学生答案(标准、删词、同义替换、加否定、只答一半、跑题), 和标准答案一起切字二元组、算余弦相似度,再把相似度乘 10 当分数,和人工分相减,看每一类错偏多少。
def big(s):
cs = [c for c in s if u"\u4e00" <= c <= u"\u9fff"]
return [cs[i] + cs[i + 1] for i in range(len(cs) - 1)]
def cos(a, b): # 字二元组余弦
va, vb = {}, {}
for w in big(a):
va[w] = va.get(w, 0) + 1
for w in big(b):
vb[w] = vb.get(w, 0) + 1
keys = set(va) | set(vb)
num = sum(va.get(k, 0) * vb.get(k, 0) for k in keys)
da = sum(v * v for v in va.values()) ** 0.5
db = sum(v * v for v in vb.values()) ** 0.5
return num / (da * db) if da and db else 0.0
diff = sim * 10.0 - hum # 折算差
实跑输出:
--------------------------------------------------------------------
自动批改:字面相似度把「加否定」判成 8.9 分
--------------------------------------------------------------------
情形 相似度 人工分 折算差
------------------------------------
标准答案 1.000 10 +0.0
删掉一个词 0.647 8 -1.5
同义替换 0.593 10 -4.1
加了否定 0.889 0 +8.9
只答一半 0.734 5 +2.3
完全跑题 0.000 0 +0.0
折算差 = 相似度 × 10 减人工分。**它模拟「拿相似度当分数」的偏差。**
偏差最大的是「加了否定」那行,差 **+8.9 分**。
两类错方向完全相反,而且都能提前预判:
**① 低估同义改写。**「同义替换」相似度只有 0.593,人工是满分。
因为「日光/光照」「水分/水」字面上一个二元组都对不上。
**② 高估否定与颠倒。**「加了否定」相似度 0.889,人工是零分。
只多了一个「不」字,**字面几乎没变,意思反了。**
所以用字面相似度做自动批改,**错的方向是可预测的**,而这恰恰最该警惕:
它会把「同义改写」判成错、把「意思反了」判成对,**两种错都指向「答对了被判错、答错了被判对」。**
结论很直接:**判「对不对」必须用语义表示,并且专门检查否定词。**
判「像不像」(作文、开放问答)才可以用字面,**因为那时你只需要一个粗排序,不需要判对错。**
标准答案那行相似度 1.000、偏差 0——这是唯一天然对齐的一行,也是唯一不能拿来验证方法的一行。 「加了否定」:相似度 0.889,人工 0 分,折算成 8.9 分,偏差 +8.9。 只多了一个「不」字,字面几乎没变。 「同义替换」:相似度 0.593,人工满分,偏差 −4.1。「光照/日光」「水/水分」字面对不上, 所以这个指标对「换了说法的正确答案」系统性偏低。 「只答一半」偏差 +2.3,说明它对「答得不全」也不敏感——少一半内容,相似度还有 0.734。 因为余弦只算共有的词,不惩罚缺失。 要说明:只有 6 条样本, 这些数不能当统计结论用,它们只是把「错的方向」演示清楚。真实评估要几百份人工标注的卷子, 并且按题型分开算偏差。
30 秒实验
- 把「加了否定」那条的「不」字删掉,重算相似度和偏差。 相似度几乎不变(0.889 上下),人工分从 0 变 10——偏差从 +8.9 变成 −1.1,方向直接翻转。
- 把切词从字二元组换成单字,看「同义替换」的相似度。 会略涨(「光」「水」对上了),但仍然远低于满分。
- 把「只答一半」那条再删掉一个要点,看相似度。 会掉到 0.6 附近,偏差收窄——内容越少越接近「明显不全」。
常见误区
- 「相似度高的答案就是对的」。加了否定相似度 0.889,意思完全反了。字面和语义不是一回事。
- 「调一下阈值就能解决」。 两类错方向相反,一个偏高一个偏低,调阈值只会把一头压下去、另一头更糟。
- 「自动批改要一次做到位,全交给模型」。 正确做法是分层:规则批客观题、模型批简答加置信度、人工抽检。
自测三问(点开对答案)
1. 为什么字面相似度会把「加否定」判成高分? —— 加一个「不」字对字二元组的总体重合度影响很小,相似度仍在 0.889; 但语义完全反了,人工是 0 分。 2. 哪类任务可以放心用字面相似度? —— 只判「像不像」、只需要粗排序的任务,比如作文或开放问答的初筛。要判对错就必须用语义表示。 3. 为什么客观题不该交给大模型批? —— 答案有限、比对精确,规则就够,而且可解释、可审计。 大模型会引入不确定性,同一份卷子两次给两个分。软件层面的三件事讲完了:估水平、批作业、 还有一件事最容易被忽略——这些功能要跑在什么设备上,跑不跑得动。
- ☐ (本节完,下一节 9.3.3 教育硬件赋能)