2025期末大作业实验报告¶
- 实验要求
这个任务概括来说就是给定中文手写作文图片,输出纠正后的文本。但是还要考虑纠错的位置对应图片上的所有边界框位置。
- 整体架构
我尝试过端到端的模型——直接把手写图片交给GPT-4这样的大模型,然后在提示词中命令它首先提取出图片中的文字,然后纠错,最后再找到纠错的位置标出来.但是实际结果来看效果并不尽如人意,一方面文字提取出来的并不是很精准,另一方面找框框的过程容易出错.
既然通用模型并不是那么可靠,我所采用的还是baseline所介绍的那样两阶段流程——先OCR,再纠错.
经过不断尝试,我发现即便没有微调,纠错这个过程依赖现行的通用大模型(我选择的是Google gemini-2.5-flash-preview-05-20 )效果不错.辅助 few-shot learning, 让大模型看到几个样例, 可以在纠错效果上有一定的提升. 为了更好地适配该实验的情景, 我使用LoRA微调了一个已经预训练过的、参数量7B、保持更新且表现优秀的模型(https://github.com/TW-NLP/ChineseErrorCorrector) 通过反复的实验, 我发现通用大模型的纠错后再交给微调后的模型二次纠错, 最后将二次纠错后的文本和原始文本对照, 寻找其中的差异并将差异标出来, 是能很好的完成任务的, 最终取得了0.7947的测试集分数, 相对于baseline有着十分显著的提升.
- OCR
我发现paddleocr系列的模型,无论是v3,v4还是v5,虽然识别的准确性不错,但是有一个至关重要的问题没解决——识别出来的文字区域粒度是以行为单位的, 而不是以字符为粒度的,这就给我们第二个任务带来了巨大的挑战性(因为很容易出现修改文字跨行问题)
经过一番调研,我发现百度官方提供了文字识别的接口 (https://ai.baidu.com/tech/ocr),这个也适配手写文字识别的场景. 只要开启recognize_granularity选项, 返回的结果还能将文字区域按字为单位 (https://ai.baidu.com/ai-doc/OCR/gkibizxd5) 这完美地适配了我的需求,我们一方面得到了较为可靠的文字扫描结果, 另一方面在OCR阶段就把每个字所在的位置给标了出来. 随后的标文本框过程, 只需要将纠错文本和原始文本对比,寻找其中的差异字符串 (difflib库的SequenceMatcher) 并将差异位置标出来即可.
交给API图片转文字之前,我的训练集进行了一定的预处理——增强对比度、转灰度. 这样做其实是把黑的变得更黑,白的变得更白,方便OCR的识别.
- 预训练模型+微调 尝试:T5
我微调了T5-based文本纠错模型 (https://huggingface.co/shibing624/mengzi-t5-base-chinese-correction) 但是效果并不算理想, 最终的结果为0.6177左右
- 预训练模型+直接推理 尝试:TWNLP-7B
没有经过任何微调, 我直接交给TWNLP-7B进行推理,令其生成预测的纠错文本, 效果有一定提升, 达到了0.677. 这让我看到了, 微调并不一定是必须的, zero-shot learning是可行的,可能更先进的模型效果更好.
- 预训练模型+直接推理+few-shot learning 尝试:gemini-2.5-flash
既然zero-shot learning是可能的, 那么理论上来说few-shot learning会更好一些. 先进的通用模型在很多场景下是能打败专有模型的, 所以我选择了一个更通用、更强大的模型: gemini-2.5-flash. 以API的形式调用, 并且在prompt中明确化要求, 并且给它看几个样例(来自训练集) 我发现表现确实不错, 多次尝试大概有0.7212-0.7435这样的得分
- 预训练模型+微调进行二次纠错:TWNLP-7B
为了更好地适配该实验的情景, 我使用LoRA微调将TWNLP-7B进行了微调, 将来自gemini的一次纠错结果进行二次纠错, 将二次纠错的文本作为最终的纠错文本. 事实证明这样做确实有一定的提升, 多次尝试大概有0.7599-0.7947这样的得分
- 文本框怎么标
有先前的OCR基础, 我们的char_bounding_box_list已经保存了所有识别出来的文字单子所在位置的坐标. 接下来只需要使用SequenceMatcher找字符串的不同即可,详情如下:
matcher = SequenceMatcher(None, source_text, corrected_text)
bounding_box_list = []
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
if tag != 'equal':
for i in range(i1, i2):
if i < len(char_bounding_box_list):
box = char_bounding_box_list[i]["box"]
bounding_box_list.append(box)
- 实验总结
经过困难的调试,在2025年6月9日下午终于完成了实验,最终取得了0.7947的测试集分数, 相对于 baseline 取得了非常明显的提升。
最后,也感谢老师、助教学姐们一学期以来的辛苦工作.