拓冰建站拓冰建站
首页 / 资讯中心 / 正文

自动作文评分AES全链路:特征、QWK、大模型评分与部署避坑

给一篇高中英语作文打分十位老师坐在一起同一篇作文的分数能差出三分——这不是老师不负责而是文章好不好这件事本身就没有唯一答案。自动作文评分Automated Essay Scoring简称 AES要处理的就是把这个模糊的人类判断压缩成一个稳定、可复现、还能扛住几万份试卷的数字。它既不神秘也不玄学本质上是把评分老师的潜意识经验拆成可计算的信号再用统计或神经网络把这些信号拼回一个分数。这篇文章想聊的不是某某模型在ASAP数据集上刷到了0.8的QWK这类排行榜式内容而是把AES这套系统从特征、模型、指标、踩坑到落地部署完整拆一遍。如果你正在做教育类产品的评分模块、在为学校搭建作文批改工具、或者单纯想搞清楚机器怎么给作文打分下面的内容可以直接当施工图看。里面既有传统特征工程那套老办法也有预训练语言模型和大模型评分的新玩法还包括我自己在复现过程中被坑过好几次的地方。1. 机器眼里的作文评分对象到底是什么1.1 它不是文本分类而是把能力证据映射成序数很多人第一次接触AES会本能地把它当成一个文本分类任务——输入作文输出1到6分。这个类比只对了一半而且是有害的那一半。标准文本分类里类别之间是并列的猫和狗没有大小关系但作文分数是序数ordinal4分和5分的距离不等于1分和2分的距离更不等于0分和5分的距离。你在建模时如果按多分类处理交叉熵会平等地惩罚把6分判成5分和把6分判成1分这显然不符合教学现实。更准确的心智模型是作文是学生写作能力的一组间接证据评分模型做的事情是估计这个潜在能力值。证据包括词汇掌握程度、句法复杂度、论证组织、衔接连贯、语法准确性、是否切题等等。这些证据没有一项是直接可读的全靠文本特征去逼近。比如词汇丰富度这个抽象概念落地时会变成TTRType-Token Ratio、MTLDMeasure of Textual Lexical Diversity、AWL学术词表覆盖率这些具体数字论证组织可能被拆成段落数、段落间LSA余弦相似度、连接词密度。我做项目时习惯先画一张表左边写评分老师大脑里在看的维度右边写我准备用什么特征去代理它。这张表画不出来的话后面模型再花哨也是空转。而且这张表有个隐性作用——它逼你承认哪些维度代理不了。比如观点是否有新意这个维度用传统特征几乎无从下手硬凑出来的特征只会变成噪声。提示如果你的评分标准里有一条内容切题请务必确认模型真的有prompt相关的输入信号否则它只能靠训练集里同题作文的统计规律去猜跨题就崩。1.2 一致性、可解释、抗博弈三个不能同时最大化真实场景里AES系统要同时满足三个约束而它们互相拉扯。一致性指同一篇作文重复评分结果稳定这是机器相对人类最大的优势。人类老师会疲劳、会被前一篇作文锚定、会因为字迹影响判断机器不会。但一致性高不代表正确一个恒定偏低的模型一致性满分。可解释指分数要能被老师、学生、家长接受。教务主任问你为什么这篇只给3分你回答神经网络输出的这个项目基本就结束了。所以实际系统里即使主模型是BERT也常常要挂一个解释层输出该生词汇维度得分偏低主要因为基础词汇重复率过高这类话术。抗博弈指学生不能通过堆字数、背模板、故意写长句来骗分。这一点被低估得最严重。我见过一个学生发现写满800词必得高分之后每次都在结尾疯狂重复观点分数确实上去了。后来我们不得不在特征里加有效信息密度这类惩罚项还在训练时做了长度与分数的去相关处理。这三者的冲突在于越追求可解释就越依赖人工设计的浅层特征抗博弈能力越弱越追求抗博弈模型越黑盒可解释性越差。工程上的常见解法是双轨制主模型负责分数解释模块单独训练两者不共享参数只在输出层做一致性约束。1.3 评分粒度整体打分还是分维度打分还有一个必须先定下来的问题输出一个总分还是输出多个维度分再加权整体打分holistic scoring训练简单、标注便宜老师只需要给一个总分。分维度打分analytic scoring信息量大能给学生具体反馈但标注成本成倍增长而且维度之间的标注一致性通常比总分更低——让十位老师对组织这个维度打分分歧比打总分大得多。我个人的判断标准是看产品形态如果最终只呈现一个分数和一句评语做整体打分就够了用维度分反而增加噪声。如果要生成详细的批改报告那维度分是绕不过去的这时候我一般会建议采用PERSUADE 2.0那套七维度标注体系观点、组织、风格、词汇、句子流畅度、语法、拼写因为它的标注指南公开且相对成熟省去自己从零定义标准的麻烦。2. 特征、模型、校准一套评分系统的三段式拆解2.1 传统特征清单以及它们的失效边界在预训练模型普及之前AES的主流做法是把作文变成几百维特征向量再喂给线性回归或SVR。这套方法今天依然有价值因为它的推理成本极低、可解释性强而且在小样本场景下经常比深度模型更稳。下面是我实际用过、按类别整理的一份清单。特征类别具体指标主要代理的评分维度典型失效情形长度类词数、句数、平均句长、段落数内容充分性、展开程度学生堆砌废话时失效词汇类TTR、MTLD、AWL覆盖率、CEFR分级分布词汇丰富度短文本TTR不稳定句法类依存树深度、从句密度、被动语态比例、POS n-gram句法复杂度长难句不等于好文章连贯类段落间LSA相似度、实体网格、连接词密度组织与衔接模板化连接词可被刷错误类拼写错误率、语法错误密度、标点异常语言准确性依赖纠错工具召回率可读性Flesch-Kincaid、Gunning Fog语言难度匹配与分数非单调这张表里最值得说的是长度类特征和连贯类特征因为它们是两个极端。长度特征几乎是所有AES模型里单变量相关性最高的在很多数据集上光用词数去预测分数Pearson相关系数就能到0.6以上。这听起来是好事其实是个陷阱——模型很容易学会长的分高然后在遇到一个写得精炼但内容扎实的短作文时给出低分。我处理这个问题的办法是在特征层做长度归一化比如词汇丰富度按长度做滑动窗口同时在损失函数里加一项惩罚模型对长度特征的过大权重。连贯类特征的问题相反它们很容易被模板作文攻破。LSA段落相似度、连接词密度这些指标只要学生背几个Firstly / Moreover / In conclusion数值就上去了。所以我在用连贯特征时会额外统计连接词多样性和连接词与相邻句语义的一致度把单纯堆砌的情况筛出来。2.2 从LSTM到BERT范式转移发生在哪一步深度模型进入AES大概是从2016年前后开始的。早期做法是词向量加LSTM或CNN把作文编码成一个向量再回归。这一代模型相对传统特征有提升但提升有限核心原因是长文本编码困难——一篇500词的作文LSTM读到后面已经忘了前面。真正的转折点是层次化建模的思路先把每个句子编码成句向量再把句向量序列编码成篇章向量。这个结构对应了作文本身的组织层次效果比扁平编码好一截。再往后是注意力机制的引入让模型在打分时能指出我对这几个句子关注度高顺带解决了部分可解释性问题。2019年之后预训练语言模型成为默认选择但带来了一个新麻烦512 token的输入限制。一篇作文轻松超过这个长度直接截断会丢掉结尾段落——而结尾恰恰是很多评分标准里结论完整性的证据所在。工程上的解法有几种我按实际体验排个序滑动窗口加池化把长文本切成多个512片段分别编码再对片段向量做平均或注意力池化。实现简单效果稳定是我最常用的方案。层次式BERT句子级BERT加篇章级Transformer。效果通常最好但推理成本高显存吃紧。长文本模型Longformer、BigBird这类支持长上下文的架构。理论优雅实际微调时对显存和训练技巧要求高小数据集上容易过拟合。还有一个容易被忽略的点prompt感知。同一篇作文放在不同题目下分数可能完全不同。所以模型输入里应该显式拼接prompt的编码或者用多任务学习给每个prompt学一个偏置项。我做过对比实验加了prompt编码之后跨题泛化能力提升明显而只靠作文文本的模型在遇到新题目时分数分布会整体漂移。2.3 数据集与标注口径先搞清楚你在学什么公开数据集是入门AES最快的路径但每个数据集的标注口径不一样混用会出大问题。ASAP-AES是最常被引用的一个8个题目、约1.3万篇作文分数是整数档。它的优点是免费、社区baseline多缺点是标注质量参差而且不同题目的分数范围不同有的2-12有的0-3直接合并训练会让模型困惑。我做实验时通常会按题目做归一化或者干脆用多任务结构给每个题目独立输出头。TOEFL11更偏语言背景研究标注的是母语背景而不是写作质量用它做评分任务需要自己重新标注。PERSUADE 2.0是目前标注最细致的两万多篇作文带七个维度分数和语篇标注适合做分维度评分研究。ELLIPSE是近几年出现的样本在人口统计学上更均衡如果你的项目对公平性有要求这个数据集值得优先考虑。需要特别提醒的是这些数据集都是人写的作文。如果你打算把模型用到真实产品里而产品面对的是可能使用生成式工具的学生那训练分布和线上分布会出现严重偏移。这个问题我在第5节会展开说。3. QWKAES的默认指标以及它的三个陷阱3.1 二次加权Kappa到底在算什么只要你看AES论文几乎一定会遇到QWKQuadratic Weighted Kappa。它的定义是QWK 1 - (1 - po) / (1 - pe)其中po是观测到的一致率pe是随机猜测下的期望一致率。关键在二次加权这四个字——它给不同距离的错分赋不同权重把6分判成5分只算小错判成1分算大错。权重矩阵通常是 (i-j)² / (N-1)²。这个设计正好对应了作文分数的序数性质比单纯的准确率合理得多。但QWK有个反直觉的特性它衡量的是比随机好多少而不是有多准。举个例子如果测试集里90%的作文都是3分那么一个把所有作文都判3分的傻瓜模型准确率有90%QWK却是0。反过来如果分数分布很分散模型只要稍微抓住一点规律QWK就能冲到0.7以上。3.2 分数集中导致的虚高以及怎么破我在复现某篇论文时踩过一个典型的坑论文报告QWK 0.78我复现出来只有0.65反复调参都追不上。后来仔细看数据集才发现那篇论文用的测试集里分数高度集中在中间档而我的划分方式让分布更均匀。同样的模型仅仅因为测试集分数分布的差异QWK就能差出0.13。应对办法有两个。一是在划分数据时做分层抽样保证训练集和测试集的分数分布、prompt分布都一致并且把划分方式固定下来方便对比。二是同时报告多个指标别只盯着QWK。我通常会给出一组指标衡量什么适用场景QWK与人工评分的一致性扣除随机论文对比、主指标RMSE分数预测的绝对误差连续分数、回归模型MAE平均绝对误差比RMSE抗异常值存在极端误判时参考Pearson / Spearman线性/秩相关快速看趋势一致性分档准确率精确命中档位的比例产品侧直观汇报相邻档准确率误差在一档以内的比例教学场景更合理相邻档准确率这个指标被提得少但我觉得它在教育产品里最有用。老师能接受这篇5分判成了4分不能接受5分判成2分。产品验收时用相邻档准确率谈比谈QWK更容易达成共识。3.3 什么时候该放弃QWKQWK不是万能的。如果分数是连续值比如0到100的百分制QWK需要先做离散化而分箱方式会显著影响结果这时候直接用RMSE或MAE更干净。如果做的是排序式评分比如两篇作文比较优劣那应该用pairwise accuracy或Kendalls tau。还有一种情况是分数分布严重长尾。比如某次考试里90%的作文集中在两个档QWK的期望一致率pe会很高导致分母很小指标方差极大。这时候我会改用分档的宏平均F1或者直接报告混淆矩阵。4. 复现路上的坑从数据划分到分数截断4.1 prompt泄漏最隐蔽也最致命这是我踩过最贵的一个坑。当时模型在验证集上QWK到了0.82兴冲冲部署到测试环境结果真实数据上只有0.5出头。排查了两天最后发现问题出在数据划分上——同一道题目的作文一部分在训练集一部分在测试集。模型在这种情况下学到的不是怎么写好作文而是这道题的作文通常长什么样、分数多少。同一题目的学生作文在词汇、句式、话题词上有大量共性模型只要记住这些模式就能在测试集上作弊。这类泄漏在文献里叫prompt leakage在ASAP这类只有8个题目的数据集上尤其严重。正确的划分方式是留一题交叉验证leave-one-prompt-out每次拿一道题的作文当测试集其余题目训练。这样测出来的分数才是真实的跨题泛化能力。代价是指标会难看得多我从0.82掉到0.55的那次才是这个模型真实水平。还有两种更隐蔽的泄漏。一是同一学生的多篇作文被分到不同集合学生的个人写作风格会被模型记住。二是重复文本数据清洗不彻底时同一篇作文可能以不同版本出现两次。这两种我都在实际数据里遇到过清洗时务必按学生ID和文本指纹去重。4.2 分数截断与中间档堆积ASAP这类数据集的分数是整数但回归模型输出的是连续值必须做截断或取整。这里有个细节很容易做错。假设分数范围是2到12模型输出9.6你会怎么处理直接四舍五入到10还是按训练集的分位数映射到具体档位我一开始用四舍五入结果发现分数分布和人工评分对不上——模型在中间档堆积严重两头很少。原因是回归模型倾向于预测均值附近的值这是平方损失的固有特性。后来我改成了阈值优化不固定用四舍五入而是在验证集上搜索一组阈值让预测分数的分布尽可能接近人工评分的分布同时最大化QWK。这一步通常能带来0.03到0.05的QWK提升成本几乎为零属于必做的后处理。注意阈值必须在验证集上搜不能在测试集上搜。我见过有人拿测试集调阈值然后报告结果那是另一种形式的数据泄漏。还有一种做法是干脆放弃回归改成有序回归ordinal regression或者累积链接模型直接建模P(y ≥ k)。这类模型天然适合序数标签预测分布也更合理。缺点是实现比普通回归麻烦需要自定义损失。4.3 长度特征的正则化以及一次翻车经历前面提到长度特征是双刃剑。我具体说说怎么处理。最直接的做法是在训练时对样本做长度分层采样保证每个分数档里的作文长度分布大致一致。这样模型学不到长高分这个捷径。第二个做法是在特征层做长度归一化比如把词数替换成相对于该prompt平均长度的比值或者把词汇丰富度改成MTLD这种对长度不敏感的口径。我翻车的那次是偷懒没做这两步。模型上线后一个老师反馈说我们班一个学生写得特别精炼内容也好但系统给了低分。我调出那篇作文一看确实只有180词内容和结构都不错模型给了2分满分6分。而同题一篇450词、但逻辑混乱的作文得了4分。这就是长度偏见被彻底放大的结果。修复方案是在损失函数里加了一个长度去相关项计算batch内预测分数与词数的相关系数把它作为惩罚项加到总损失里权重设0.1左右。训练两个epoch后长度与预测分数的相关系数从0.71降到0.34而QWK只掉了0.02。这笔交易很划算。4.4 语法纠错工具的召回率会拖累你很多AES系统会用外部工具统计语法错误密度。这里有个隐藏问题纠错工具的召回率直接决定了这个特征的可信度。如果工具只能抓出60%的错误那么错误密度低既可能是学生写得好也可能是工具没抓到。我的处理方式是不把错误数量当绝对特征而是做成相对特征同一篇作文内部的错误密度、以及相对于该prompt全体样本的错误率分位数。同时在评估模型时单独看语法维度上的表现如果这个维度的误差明显高于其他维度那多半是纠错工具在拖后腿。5. 大模型来了之后重新审视评分这件事5.1 zero-shot打分为什么不靠谱直接用提示词让大模型给作文打分听起来很美实测下来问题不少。第一是分数分布塌缩。你让模型打1到6分它可能90%的情况都给4分因为它倾向于输出安全的答案。这直接导致QWK接近0虽然准确率看着还行。第二是位置与长度偏见。有研究做过实验把同一篇作文放在提示词里的不同位置模型给出的分数会变把作文人为加长重复句子之后分数往往会上升。这些都是评分系统不能接受的。第三是自我偏好。如果作文本身是模型生成的它给自己的作品打分往往偏高。这个现象在学术上叫self-preference bias如果你打算用大模型同时做作文生成和评分务必小心。第四是不可复现。温度参数不为0时同一篇作文两次调用结果可能不同。就算把温度设成0不同批次的模型版本更新也会导致分数漂移。评分系统的分数稳定性是底线这一点很难妥协。5.2 那大模型到底该用在哪我的实践结论是大模型做预标注、做解释、做质检可以做最终判决要慎重。具体来说有几个用法是靠谱的。一是rubric注入把评分标准rubric结构化地写进提示词给每个维度定义清楚0到3分的含义再附上几个标注范例让模型输出维度分。这种few-shot设置下模型与人工的一致性会明显高于zero-shot。二是理由生成让模型输出为什么给这个分这些文本可以作为教师端的参考人工确认后再展示给学生。三是预标注加速让模型先给一遍分数人工标注员在此基础上修改标注效率能提升不少。还可以把大模型的输出作为额外特征喂给传统的评分模型。比如用大模型给每篇作文生成一段质量评价把这段评价的embedding拼到特征向量里。这种做法在样本量少的场景下效果不错因为它引入了额外的语义先验。5.3 生成式工具带来的分布偏移才是真正的挑战这是目前最棘手的问题且没有成熟解法。当学生可以用生成工具写作文时线上作文的质量分布和训练集里的人类作文分布会出现系统性偏移。模型可能给出普遍偏高的分数也可能因为文本过于完美而给出偏低的分因为它和训练集里的学生文本差异太大。我目前的思路是双模型并行一个模型做质量评分另一个模型做人机判别前者分数只有在后者判定为人类写作时才完全生效。人机判别本身可以用文本统计特征困惑度、突发性、词汇分布加上分类器来做但不能保证万无一失。所以产品层面的兜底是人工抽检把模型判定置信度低的样本挑出来送人工复核。还有一点值得提醒不要把检测AI写作当成一个纯粹的准确率问题。误判一个人写的作文为AI生成对学生的伤害远大于漏判。所以阈值应该设得保守一些宁可放过不可错杀。6. 从模型到产品部署时那些没人告诉你的细节6.1 推理链路拆分与批处理实验室里的模型和线上跑的服务是两回事。我在实际部署时把评分链路拆成了三段第一段是特征提取与预处理包括文本清洗、分句、外部工具调用语法纠错、可读性计算。这一段是CPU密集型的而且外部工具调用往往是瓶颈。我的处理是把特征结果缓存下来键是文本的哈希值同一篇作文重复评分时直接命中缓存。作文批改场景里学生反复修改再提交是常态缓存命中率能到30%以上。第二段是模型推理GPU密集型。关键是动态padding加批处理把长度接近的样本放在一起避免为了一篇800词的作文把整个batch都padding到800。显存占用能降一半以上。批次大小要根据显存动态调整并发高的时候宁可排队也别OOM。第三段是后处理与校准包括分数截断、维度分加权、解释文本拼装。这一段最轻但最容易出bug建议单独写单元测试。6.2 版本管理与分数漂移监控评分系统有个特殊性分数必须可比。如果模型这个月更新了学生上个月得到的4分和这个月的4分含义不同那整个评价体系就乱了。我的做法是给每个模型版本打上唯一编号所有评分记录里都存这个编号。模型升级时先在一个固定的人工标注集上跑一遍确认新旧版本的分数分布差异在可接受范围内再全量切换。如果差异过大就要做分数校准用一个保序回归把新模型的输出映射到旧模型的分数空间上。线上还要有漂移监控。我会定期统计几个指标预测分数的均值与方差、各分数档的占比、长度与预测分数的相关系数、以及人工抽检样本上的QWK。任意一项出现明显偏离就触发告警。这套监控帮我们抓到过一次问题——某个prompt的作文分数整体上移了0.4分最后发现是该题目被换成了另一套题库题目难度变了。6.3 公平性审计不是走过场如果你的系统面向不同地区、不同语言背景的学生公平性审计必须做。具体做法是把测试集按学生背景分组分别计算每组的QWK、RMSE和分数均值看组间差异是否显著。如果某组的平均误差比其他组高说明模型在这个群体上表现更差。我在一次审计中发现模型对某类语言背景学生的语法维度打分系统性偏低原因是训练数据里这类样本少而且语法错误模式和其他群体不同。修复方式是在训练时做样本重加权把欠代表群体的样本权重调高。这个改动没有降低整体QWK但把组间误差差距缩小了一半多。公平性还有一个维度是题目。不同题目的分数分布本来就不同如果你的系统对所有题目用同一套绝对标准那在难题上会普遍给低分。处理方式有两种一是模型感知prompt让输出分布随题目自适应二是输出相对于该题目的分位数让老师自己决定怎么解释。我倾向后者因为它把最终判断权留给了人。最后分享一个我在实际项目里总结的小习惯每次模型迭代我都会保留一个极端案例集里面全是模型判得离谱的样本——长度极短的满分作文、堆砌长句的低分作文、模板化的中庸作文。每次更新模型先跑这个集合看新版本有没有在这些边界情况上改善。这个集合不大也就几十篇但它比任何指标都更能告诉我模型到底学到了什么。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门