做作业批改这个项目之前,我一度以为最难的部分是“批改”本身——怎么判断对错、怎么给评语。结果真的上手写了一套系统之后才发现,小学生语文数学作业批改的瓶颈,几乎全卡在“文字录入”这一环。手写汉字、歪歪扭扭的数字、横式竖式混排、铅笔写得到处都是……识别都识别不出来,后面批改逻辑写得再漂亮也白搭。
这篇文章我把实际做这个项目踩过的路从头到尾拆一遍:从方案选型、OCR文字录入、语数两科的批改规则设计,到上线后各种翻车实录和排查思路。内容主要面向正在做课后辅导工具、作业批改软件、或者想给自家孩子做个批改小助手的开发者,产品经理也可以看看,到底哪些“需求”是技术层面不容易绕过去的坎。
1. 项目整体设计:一套作业批改系统到底要拆成哪几块
1.1 核心需求解析:批改的对象不止“对错”
小学生语文数学作业批改,表面上看是“对错判定”,但实际上纸面上呈现的是多种信息混在一起的复合场景:印刷体题目、手写答案、批改痕迹(红勾/红叉)、学习笔记、涂改液覆盖……如果你把这些信息全部当成同一种图像去处理,后面就会不断出问题。
我当时的做法是先做一个“分层拆解”,把需求拆成四层:
- 录入层:把作业照片变成可计算的文本或结构化数据。这就是标题里说的“文字录入”,也是全项目的地基。
- 判定层:根据录入的结构化数据,结合题目要求,判定对错。
- 反馈层:把错误结果转成红叉、解析、正确答案提示等可视化反馈。
- 管理层:错题归档、学情统计、家长端报告等。
批改的核心是判定层,但决定成败的是录入层。这个排序很多团队搞反了,一上来就研究怎么用大模型判应用题,结果是输入质量太差,模型再强也白搭。
1.2 方案选型:识别引擎与规则引擎的混合架构
我在技术选型时对比了三条路:纯规则、纯AI模型、规则+AI混合。最后选了第三条。
纯规则适合口算题、看拼音写词语这类答案相对固定、格式单一的题目。优点是速度快、可解释、不依赖GPU,但碰到手写潦草直接歇菜。纯AI模型靠大模型做端到端判断(输入图片,输出对错),看起来酷,但实测下来对“批改位置”“判断理由”要求高时经常胡说八道,而且一张图一次推理成本不低。混合架构是先用OCR类模型把文字录入成文本,再做结构化解析,最后判断题用规则引擎,开放题(造句、阅读理解)用大模型辅助。
最后落地的结构是:文字录入用专用OCR模型,对错判断分两级,一级规则、二级大模型兜底。这里“兜底”的意思是绝不把所有题都扔给大模型,只对规则判不了的主观题做补充。这样既能控制成本,又不至于被大模型幻觉带偏。
提示:不要试图用一个模型搞定所有科目。语文的手写汉字识别和数学的算式识别,是两类完全不同的任务,至少拆成两个识别通道,后面你会省很多事。
2. 文字录入环节:决定批改准确率上限的关卡
2.1 手写识别与印刷体识别要分开处理
小学生作业纸面上既有印刷体也有手写体。印刷体题目要的是“识别题目本身”,手写体要的是“识别学生答案”。这两个目标不同,处理方式也完全不同。
印刷体识别直接上通用OCR就行,识别率很高。但手写识别是另一个世界——小学低年级的字本来就写得东倒西歪,横竖不分、笔画粘连是常态。这个环节当时花的时间最多,也是“文字录入”最容易被低估的地方。
我的经验是:语文作业的手写识别不要用通用OCR,要用手写汉字识别模型,而且训练语料一定要用真实的低年级作业样本。通用OCR对手写体的识别率会让你怀疑人生,尤其是“我”和“找”、“大”和“太”这类形近字,错一个字整个答案就判错了。类似地,数学的手写数字识别也要单独做训练,而且要在数字层面兼容手写风格变化。
2.2 预处理:90%的批改错误出在这里
很多人以为OCR模型是万能的,其实它对输入图像的要求相当严苛。我后来统计过,线上批改错误里有六成以上不是模型不行,而是图像预处理没做好。核心是四件事:
- 倾斜校正:小学生拍照,作业本经常是歪的,页面边界都不是矩形。先用边缘检测把作业本轮廓找出来,做透视变换,把页面拉正。
- 光照均衡:夜间、灯光黄、手指阴影,都会让灰度分布不均。用自适应阈值二值化,而不是全局阈值。
- 去阴影/去手指:照片里经常有手指压着作业本,或者本子透出下一页的字迹。这些如果不处理,会把手指算成文字、把透字算成答案。
- 涂改覆盖识别:涂改液覆盖的地方是白色高亮区域,要识别出来,否则会把涂改痕迹当答案。
预处理那部分我建议单独做成一个服务,不要跟识别模型耦合在一起。因为不同来源的图质量差异太大了,你后面要不停地调整预处理参数,耦合在一起每次升级都牵一发动全身。
2.3 单字切分与整行识别:怎么选
文字录入的另一个大分歧点是切分策略。数学答案目前普遍是“检测框+单字识别”,语文答案则更推荐“整行识别”。
为什么这样分?数学答案大多是算式,数字和符号之间空白比较均匀,按检测框切单字很稳定。但语文答案连笔多、字间距不均匀,强行切单字会把一个字切成两半,或者把两个字贴成一个字形。整行识别则可以利用上下文信息,即使某个字识别模糊,也能靠相邻字判断出来。
实际过程中我采用了“双轨”策略:检测模块统一,识别模块分语文通道和数学通道。先检测出每个答案区域,语文区域走整行手写识别,数学区域走切字识别。这个策略比“所有答案都切单字”准确率高不少。
注意:检测框要按“答案区域”划分,而不是按“整页”划分。批改只需要针对学生填写答案的区域,把印刷体区域提前过滤掉,能减少一多半干扰。
3. 语文作业批改实操:从字词到阅读理解的规则设计
3.1 看拼音写词语与组词:比对逻辑的核心
看拼音写词语这类题,判定逻辑很直接:对录入的手写文本和标准答案做归一化比对。但这里的“归一化”走得比我预想的多——学生写错别字时,答案跟标准答案完全不同,需要“错字提示”而不只是“对/错”。
比对策略分三层:
- 完全一致:答案正确。
- 单字匹配:用编辑距离判断,识别出哪个字写错了,给出错字定位。
- 拼音匹配:把答案转成拼音,和题目标注的拼音做二次匹配,用于区分“音对字错”和“字音都对”。这样判卷时能区分是“真会写”还是“蒙了个音”。
批改结果里我最终加了三个状态:正确、错字(写出答案但写错了字)、未作答。别小看这个细分,家长反馈非常看重“错字定位”,他们要的是“孩子知道错在哪”,不是“一个大红叉”。
3.2 造句/写话的主观题:大模型兜底而非主力
造句、看图写话这类开放题,是最早想用大模型来判的题,但实际使用下来,大模型的角色需要非常克制。判开放题的关键是“不要与标准答案严格比对”,而是看“是否符合题目要求”。
我给大模型的Prompt规定了输出格式,要求它只输出四个字段:是否满足字数要求、是否存在明显语病、是否跑题、整体评价等级。其他东西不让它自由发挥。这样既保住了主观题批改的质量,也避免了模型给出“标准答案没有的和谐问题”。
3.3 语文批改的“容错策略”:老师的判法你学不来
我在项目里调得最久的一个模块,其实是“拼音字母的容错”。低年级学生写拼音,声调位置写得不标准、u和ü分不清、jqx后面的ü忘记去两点……你要是按严格标准判,整篇作业全是错的,家长直接心态崩掉。
后来参考了有经验老师的习惯做法,加了三条容错规则:
- 声调符号位置轻微偏移不判错。
- ü的书写在特定场景下允许以u代替(按阶段判断)。
- 字母方向性错误(b/d混淆)单独标记为“方向疑似混淆”,而不是直接判错。
这些规则的背后不是“放水”,而是为了符合教学阶段和年龄特点。好的批改不是机器式挑错,而是贴近真实教学评价。
4. 数学作业批改实操:算式、竖式、应用题的判断链路
4.1 口算题的识别与求值:先归一化再计算
数学题的判定链路和语文有本质区别。语文判的是“文本对不对”,数学判的是“数值和计算过程对不对”。
口算题我的做法是:
- 对录入的算式文本做归一化:全角转半角、去除空格和括号内多余符号、统一乘除号写法。
- 用表达式求值器计算左右两边,注意除法要用有理数运算,避免浮点误差。
- 比对答案和计算值,同时要识别“答案区域写对了但表达式里数字识别错了”的情况——这种要定位到具体数字,而不是笼统判错。
一个特别容易翻车的点:低年级的算式有的写“3 + 4 = 7”,有的写“3+4=7”,还有的答案只写“7”,前面根本没写等号。这种情况要做一个“结果表达式”结构化解析,否则会漏判。
4.2 竖式/脱式:结构信息比文字信息值钱
竖式是数学批改里最复杂的题型,因为它的判断不只看结果,还要看过程的对齐、进位、退位。竖式的特点是对齐关系本身就是信息——个位和个位对齐,十位和十位对齐。
我最后一版是这么处理的:把竖式图像按行切分,再按列对齐分析,得到“每一步的运算过程”,跟标准过程做步骤匹配。这个做起来费劲但收益很大——**它能告诉你“这题错在哪一步”,而不是“这题错了”。**家长和老师非常看重这一点,一道竖式题有七八个可错的步骤,笼统说“错”毫无意义。脱式(递等式)同理,关键在于每一步的中间结果是否截断、是否合理。
4.3 应用题的“解”与“答”双通道判断
应用题最怕的是学生列式和结果都对,但“答”里面没写单位,或者单位写错了。我之前统一用“解+答”双通道来判:
- 解通道:识别解题过程,判断列式、计算、单位是否正确。
- 答通道:识别“答:”后面的句子,判断是否回答了问题、是否带单位。
两个通道哪个错就标记哪个。这个设计的目的是让家长端的错题归因更精准——是“计算错”还是“没写答”,定位完全不同。另外一个经验是应用题的公式识别要跟“题目语义”联动。题干里的单位和问法,可以直接给识别模型提供“上下文候选词”,比如题干出现“只”,批改时就会更倾向把手写“支”归为笔误而非错误,正确率有肉眼可见的提升。
提示:数学文字录入一定要保留“数字+运算符+小数点”的格式信息,不要只保留文本。竖式的对齐位置、小数点位置一旦丢了结构信息,后面怎么判都判不对。
5. 常见问题冷知识与排查实录
5.1 手写数字串粘连的切分失败
最经典的翻车现场:学生写“56”,两个数字连在一起,检测框切出一个整体,识别直接变成“56”或者“S6”。折腾了很久之后我总结了一条规律:手写数字串切分不要靠传统的“空白间隔”来判断,要靠“笔画密度凹谷”。空白间隔在手写里不稳定,但笔画密度轮廓的凹谷位置一般就是两个数字的边界。
切分做完之后记得要验证:如果切出的所有单字宽度都明显过宽或过窄,宁可放弃切分,让识别模型处理整串,也不要强行切。强行切分导致识别错误会直接污染后续判定。
5.2 铅笔写的识别率为什么低
低年级学生几乎都用铅笔,而铅笔笔迹灰度浅、对比度低,扫描拍出来的笔画模糊,一是二值化阈值一调就断笔。这个问题的根源是质量,不是算法。
我的做法是二值化时加自适应卷积核,而不是全局阈值。另外在预处理阶段做一个“笔画加深”:对灰度图做形态学膨胀再与原图融合,让浅色笔画轮廓更明显。改完这一版,铅笔作业的识别率明显上来了。
5.3 批改结果的置信度:宁可漏判不要错判
所有识别模型都会给置信度,但批改场景里置信度阈值要设得多高,不是随便定的。我调了一个月之后的心得是:批改系统的红线是“错判”,不是“漏判”。
漏判(识别不出来,提示重新拍)最多就是体验差一点,用户可以重拍重传。错判(把对的判成错的,或者把错的判成对的)会造成家长对系统的信任崩塌,这种错误是不可挽回的。所以我宁可把阈值调高,识别不了就提示“请重拍”或者转人工复核,也不要强行给出可能错误的结果。
我给的阈值经验值:手写汉字整行识别的置信度低于0.75就标记为待确认;数学单字识别低于0.8就标记为待确认。低于阈值的作业不直接判对错,而是显示“无法识别区域”。
5.4 线上运维的一些避坑清单
最后分享几个我在项目运维期整理出来的小经验:
- 批改任务走异步队列,不要同步阻塞。小学生作业照片经常很大,同步处理会拖垮整个服务。
- 图像上传前压缩到长边1600px就足够,再大只是浪费带宽和算力。
- 同一个学生连拍多张作业,要做“题目相似度去重”,避免家长反复传同一张作业导致批改记录重复。
- 模型版本更新要灰度发布,老版本结果要保留至少一个月,方便回滚和溯源。
这些点单独看都很琐碎,但每一个上线后都真实救过我的命。
最后关于这个项目,我个人实际做下来的体会是:小学生作业批改的技术障碍不在“懂AI”,而在“懂场景”。文字录入环节的脏活累活,决定了后面所有环节的地基。如果一个团队能认认真真把手写识别、倾斜校正、容错规则打磨到位,哪怕判定部分用的是最简单规则引擎,做出来的产品体验也不会差。反过来,任何想跳过录入层面直接让大模型看图批改的想法,在真实作业本面前都会崩得很惨。先把手写文字录进系统这一关过了,后面的一切才谈得上。