2026/10/11 19:15:15

TensorFlow与PyTorch双后端OCR实战:CTPN检测+CRNN识别

TensorFlow与PyTorch双后端OCR实战:CTPN检测+CRNN识别 简介面向自然场景文字检测与中文OCR识别的毕业设计Python源码包基于TensorFlow框架同时提供Keras与PyTorch双版本实现。整体包含文本方向分类、CTPN文本区域检测、CRNN端到端不定长识别三个网络覆盖从图像输入、文字定位到序列识别的完整流程适合计算机视觉/OCR方向的学生开展实验和二次开发。压缩包共237个文件包含91个Python脚本、50个pyc编译文件、环境构建脚本、样例图片、预训练模型等整体约62.71MB目录结构清晰。已集成的VGG16方向分类模型使用8000张图像训练准确率达88.23%CTPN以CNNRNN检测文本区域CRNN则通过GRUCTC完成不定长中文识别并提供CPU/GPU一键部署脚本与PyTorch迁移版本。目前已有697人学习下载完整模型文件和使用说明一应俱全可帮助读者快速跑通demo并支撑毕业设计中的算法对比、参数调优与代码扩展。1. 从毕业设计到可复现项目一套同时给 TensorFlow 与 PyTorch 的 OCR 代码包毕业设计做自然场景文字检测和中文识别的同学十有八九会卡在同一个位置网上代码一堆但要么只有检测没有识别要么识别是英文的要么训练环境怎么都跑不起来。这套基于 tensorflow、keras、pytorch 的 Python 源码工程把自然场景文字检测和端到端中文文字识别串成了一条完整流水线检测侧用 CTPN 这类候选框方案识别侧走 CRNN CTC 路线同时给出了两种后端的实现。对做毕业设计、课程项目或者刚入 OCR 行的人来说它最大的价值不是某个算法多新而是数据准备、训练、推理、接口对接这些环节都有现成的代码可以改拿到就能跑通再替换成自己的场景数据做复现和二次开发。2. 整体结构与技术选型检测、识别两条链路怎么分工TF 和 PyTorch 怎么选2.1 为什么仓库里同时出现 TensorFlow、Keras 和 PyTorch很多初学者拿到这套代码会疑惑一个 OCR 项目为什么两套框架都上了是不是多此一举。实际上这是毕设和工业项目里很常见的设计思路——TensorFlow 2.x 自带 Keras 高层 API写训练循环、指标回调非常快适合先跑通一条 baseline而 PyTorch 的动态图特性适合调试中间层输出改网络结构不用重新建模。同一份算法逻辑各实现一遍哪个框架跑出问题就用另一个去对照查定位 bug 会快很多。从答辩角度讲能同时说清楚“同一套检测和识别流程在两种框架下的实现差异”本身就比只会调包跑的代码有说服力。比如 tf.nn 里的双向 RNN 封装和 PyTorch 的 nn.LSTM 在序列长度处理上略有不同这些细节拿出来讲就是加分项。从实用角度讲如果你的服务器只有 TF 环境可以直接用 tensorflow 后端如果你习惯 PyTorch 的断点调试那就走 pytorch 分支工作量只在于把模型定义和训练循环切换一下。2.2 整条 OCR 流水线上每个环节的输入输出自然场景 OCR 不是把一张图丢进一个模型就出文字它是一条链路。按这套代码的常规组织方式流水线分成五个环节每个环节的输入输出需要先对齐原图进来先做预处理包括保持长宽比的 resize、归一化、把最长边限制在一定像素范围内这一步解决自然场景图片尺寸差异过大的问题。预处理后进入文字检测模块检测模型输出的是文本行的候选框坐标而不是单字坐标这在 CTPN 里是一组宽度固定的小框后处理再把这些小框合并成完整的文本行。拿到文本行框后把原图中对应区域裁剪出来做透视校正或简单拉伸成统一高度这就是识别模型的输入。识别模型输出的是按时间步排列的概率分布经过 CTC 解码后变成一串字符索引最后映射到中文字表得到可读文本。整条链路的中间产物在代码包里都有对应脚本检测输出的 JSON 里包含框坐标和置信度识别的中间结果是 numpy 数组形式的序列概率这些接口搞清楚之后你替换成自己的图片或者接入视频帧都会顺手很多。2.3 目录结构与关键文件清单拿到压缩包先别急着点训练脚本先看目录结构。一套标准的毕设 OCR 代码包一般长这样ocr_project/ ├── config/ │ ├── detection_config.py # 检测模型参数anchor 比例、阈值、resize 尺寸 │ └── recognition_config.py # 识别模型参数词表路径、img_height、学习率 ├── data/ │ ├── make_vocab.py # 从标注文件生成中文字表 │ ├── synth_text.py # 合成训练图片补足真实样本不足的问题 │ └── dataset.py # 数据加载器负责读图、padding、生成 batch ├── detection/ │ ├── ctpn/ │ ├── nms.py # 非极大值抑制检测框后处理 │ └── test_detection.py # 单张图片检测 demo ├── recognition/ │ ├── crnn/ │ ├── decode.py # CTC 贪心解码 / beam search 解码 │ └── test_recognition.py # 单张文本行识别 demo ├── tools/ │ ├── train_detection.py │ ├── train_recognition.py │ ├── inference.py # 检测 识别串联脚本 │ └── evaluate.py # 计算整张图识别准确率 └── weights/ # 放预训练权重或训练产出我一般建议先从test_detection.py和test_recognition.py跑起这俩是独立的小入口能快速验证模型能不能加载、图片走不走得通。直接跑训练脚本一旦报错你会分不清是环境问题还是代码问题排查成本高很多。3. 文字检测模块实战把自然场景里的文字框找出来3.1 检测模型的核心设计anchor、BLSTM 与文本线构造代码包里的检测模型走的是类 CTPN 路线这套思路对自然场景文字很有效原因是它把“文字行”当成一个有序列特征的物体来检测而不是像通用目标检测那样框出孤立物体。核心设计有三块。第一是 anchor 机制。CTPN 的 anchor 不是正方形而是宽度固定、高度可变的竖长条常见设置为宽度 16 像素高度从 11 到 273 按 0.7 倍缩放生成一组。这样设计是因为文字行天然是“扁长”的宽度方向的跨度靠连续 anchor 拼接高度方向靠不同尺度的 anchor 覆盖。训练时正样本要求 anchor 与真实框的 IOU 大于 0.7负样本小于 0.3介于中间的忽略。第二是序列上下文建模。自然场景里的文字会有弯曲、倾斜、遮挡单看一个局部小框很难判断是不是文字所以 CTPN 在卷积特征图之后接了双向 LSTM把同一水平线上的特征连成一串序列让模型学会利用左右邻居信息。这个结构和识别侧的 RNN 不是一个概念但思想相通——文字必须是连续序列才可信。第三是文本线构造。模型预测出的是一堆小 anchor每个 anchor 带 vertical 坐标和 score后处理要把同一条文本行上的 anchor 连起来规则很简单水平距离小于阈值、垂直方向重叠度高的小框合并成一根线。nms.py里做的就是这些既有传统 NMS也有针对文本行的合并逻辑。3.2 检测模型训练参数配置训练检测模型最关键的几个参数在config/detection_config.py里常见设置为参数常见取值说明anchor 基础宽度16控制文本行切分的粒度anchor 高度缩放倍率0.7决定每档高度的间隔anchor 数量10覆盖的高度范围约 11~273正样本 IOU 阈值0.7低于这个值的 anchor 不算正样本负样本 IOU 阈值0.3高于这个值的 anchor 不算负样本resize 最长边960过大会显存不够过小小字检测不到score 阈值推理0.8低于此值的候选框丢弃NMS IOU 阈值0.5控制合并框的激进程度训练命令一般是python tools/train_detection.py \ --dataset_dir data/ICDAR2015 \ --gpu 0 \ --batch_size 8 \ --max_len 960 \ --lr 1e-4 \ --num_epochs 50这里--dataset_dir指向检测训练数据格式是图片路径加同名的 txt 标注文件每行对应一个文本行框的四个顶点坐标--max_len 960不是简单缩放到 960而是保持长宽比、最长边不超过 960这样能兼顾小字和显存占用--lr 1e-4是 CTPN 这类检测模型比较稳的起步学习率不建议一上来就 1e-3。这个脚本跑起来之后前几轮 loss 会快速下降但框的视觉效果要到第 20 轮左右才像样。如果 loss 一直不降先检查标注格式是不是四个角点或者 xywhCTPN 的 loss 里坐标回归部分对标注格式很敏感。3.3 跑通检测 demo代码包里已经帮你封装好了单图检测入口不需要先训练就能测试。假设预训练权重放在weights/detection_ctpn.h5或者weights/detection_ctpn.pth跑法这样python detection/test_detection.py \ --model_path weights/detection_ctpn.h5 \ --image_path demo/street.jpg \ --backend tensorflow \ --score_threshold 0.8 \ --nms_threshold 0.5输出会保存一张画了检测框的结果图同时在终端打印每个框的坐标和置信度。--backend这个参数就是这套代码的特色可以切换 tensorflow 和 pytorch方便你验证两边结果是否一致。如果你用的是 PyTorch 权重把 backend 改成 pytorch 就行不需要改其他代码。跑通之后你大概率会遇到两个现象一是检测框把路牌上的大字框得很好但店铺门口的小字漏了这时候把score_threshold降到 0.7漏检会缓解但误检会变多二是倾斜文本行的框不太贴边这是 CTPN 的固有短板它擅长水平文本对倾斜严重的文本行效果一般代码包的后续版本有的会加角度回归但基础版默认不带。3.4 后处理框合并、NMS 与按行排序检测模型输出的候选框直接拿来用是不行的首先要做一次按行合并接着做标准 NMS 去掉重复框最后按阅读顺序排序。nms.py里这段逻辑是通用的可以直接抄进你自己的项目import numpy as np def nms(boxes, scores, iou_threshold0.5): # boxes: [[x1, y1, x2, y2], ...] # scores: 每个框的置信度 order np.argsort(scores)[::-1] keep [] while order.size 0: i order[0] keep.append(i) xx1 np.maximum(boxes[i, 0], boxes[order[1:], 0]) yy1 np.maximum(boxes[i, 1], boxes[order[1:], 1]) xx2 np.minimum(boxes[i, 2], boxes[order[1:], 2]) yy2 np.minimum(boxes[i, 3], boxes[order[1:], 3]) w np.maximum(0.0, xx2 - xx1) h np.maximum(0.0, yy2 - yy1) inter w * h area_i (boxes[i, 2] - boxes[i, 0]) * (boxes[i, 3] - boxes[i, 1]) area_o (boxes[order[1:], 2] - boxes[order[1:], 0]) * (boxes[order[1:], 3] - boxes[order[1:], 1]) iou inter / (area_i area_o - inter) order order[1:][iou iou_threshold] return keep这段 NMS 的作用是先把置信度最高的框拿出来把和它 IOU 超过阈值的框全删掉然后在下一次迭代处理剩余框。iou_threshold的取值很关键0.5 是通用设置文本检测里调低到 0.3 会让相邻文本行更容易被拆开调高到 0.7 则可能把两行文字合成一个框。在实际调用时我一般先按 y 坐标排序再按 x 排序保证输出顺序是“从上到下、从左到右”否则识别出来的文字顺序是乱的。4. 端到端中文文字识别从词表到 CTC Loss 再到解码输出4.1 CRNN 识别链路CNN 提特征双向 LSTM 建模序列识别模块走的是 CRNN 架构这是中文 OCR 任务里最成熟、最容易复现的方案整条链路三句话能说清CNN 把图像变成特征图双向 LSTM 把特征图按时间步建模成序列CTC 把序列对齐成文字标签。具体展开输入图片先进卷积网络常见做法是取 VGG16 的前几层或者一套轻量 CNN经过四次池化后高度方向被压缩到 1 到 2 个像素宽度方向保留足够的时间步。这里有一个模型设计细节输入图片的高度固定为 32宽度不固定所以 CNN 输出的特征图宽度是可变的这样就能处理不同长度的文本行。特征图接着做维度变换把通道维和宽度维重排成“时间步 × 特征维”的格式送进双向 LSTM。双向 LSTM 在这里的任务是给每个时间步补上上下文信息。为什么要双向因为“长城”这两个字从左往右看“长”预测出“城”的概率高但有的字符歧义需要看右边才知道左边是什么双向 LSTM 把正向和反向的隐状态拼起来识别率提升非常明显。LSTM 的输出接一个全连接层维度等于字表大小加一个 blank每个时间步输出一个概率分布。CTC Loss 解决的核心问题是训练数据里只有文本行的整体标签没有每个时间步对应哪个字符的标注。CTC 允许预测序列和标签序列在长度上不对齐它把所有可能对齐方式的概率求和作为损失。这句话听起来抽象但你只需要理解这种设计让 CRNN 训练时不需要做字符级切分省了巨大的标注成本。4.2 中文字表和训练标签的准备识别模型训练前第一步是生成字表。代码包里的data/make_vocab.py做的事情是扫描所有训练标注文本把所有出现过的字符去重后写进char_list.txt每行一个字符。中文字表一般覆盖 6000 到 8000 个字符比如一级汉字加上常用标点和数字字母如果你只做身份证号识别几百个字符就够字表越小模型越容易收敛。生成字表和索引映射的脚本我一般这样写import codecs chars [] with codecs.open(data/char_list.txt, r, encodingutf-8) as f: for line in f: line line.strip() if line: chars.append(line) char2idx {blank: 0} # 0 号留给 CTC 的 blank idx2char {0: blank} for c in chars: if c not in char2idx: char2idx[c] len(char2idx) idx2char[char2idx[c]] c print(字符数含 blank:, len(char2idx)) assert len(char2idx) 10000, 字表太大检查是否混入了异常字符这里有几个约定必须保持一致第一个索引要留给 blankCTC 在解码时会把 blank 和相邻重复字符折叠掉字表一旦生成训练和推理必须用同一份char_list.txt换字表会导致索引错位识别结果全是乱码。训练数据的标签文件是纯文本每行格式“图片路径 \t 标签文本”经典做法是data/train_imgs/0001.jpg 北京市朝阳区 data/train_imgs/0002.jpg TensorFlow OCR标签文本里不要带空格因为拆分行的 tab 分隔已经确定了字段边界空格会进字表变成字符增加无意义的识别难度。4.3 训练脚本与关键参数识别模型的训练入口是tools/train_recognition.py启动命令长这样python tools/train_recognition.py \ --train_file data/train_list.txt \ --val_file data/val_list.txt \ --vocab_path data/char_list.txt \ --batch_size 64 \ --img_height 32 \ --img_width 280 \ --epochs 30 \ --lr 1e-3 \ --device cuda:0--img_height 32是 CRNN 的标准输入高度基本不用改--img_width 280是 batch 内 padding 的统一宽度真实图片宽度超过 280 会等比缩放不足 280 的右侧补白。这里容易犯的错误是把宽度统一设成固定值后整张图被拉伸变形长文本行被压扁导致识别率暴跌正确做法是高度固定、宽度按原始长宽比缩放再补白到统一宽度。--lr 1e-3配 Adam 优化器是 CRNN 训练的常见组合跑 15 轮左右验证集准确率不涨了把学习率降到 1e-4 再继续。我在训练中文识别模型时还有一个习惯每个 epoch 结束后把验证集里预测错的样本图片和预测结果打到终端光看 loss 曲线看不出模型是不是在瞎猜。4.4 推理解码与检测识别串联训练完成后的推理阶段模型输出是一个维度为“时间步数 × 字符数”的概率矩阵。解码最常用的是贪心策略每个时间步取概率最高的索引然后去重、去 blank。代码实现如下import numpy as np def greedy_decode(prob, idx2char): # prob: [seq_len, num_classes] seq_len prob.shape[0] idx_list np.argmax(prob, axis-1) prev -1 out [] for idx in idx_list: idx int(idx) if idx ! prev and idx ! 0: # 0 是 blank out.append(idx2char[idx]) prev idx return .join(out)这段代码的逻辑分三块先对每列取 argmax 得到索引序列然后做 CTC 标准的“去重复”再把 blank 替换成空最后按索引查字表拼成字符串。idx ! prev是去重复idx ! 0是去 blank顺序不能反否则会漏字符。decode.py里除了贪心解码还提供了 beam search 的实现思路beam search 会保留多个候选路径再打分比贪心准但慢适合对识别精度要求高、对延迟不敏感的离线场景。实际部署时我把解码策略做成配置项线上接口用贪心解码保证响应速度离线批量脚本用 beam search 拉精度。检测和识别串联的调用逻辑代码包里tools/inference.py做了封装核心流程是def ocr_pipeline(image_path, detector, recognizer): boxes detector.detect(image_path, score_threshold0.8) # 按从上到下、从左到右排序 boxes sorted(boxes, keylambda b: (b[0][1], b[0][0])) results [] for box in boxes: crop perspective_crop(image_path, box) prob recognizer.predict(crop) text greedy_decode(prob, idx2char) results.append({box: box, text: text}) return results这段脚本先把检测出来的框按阅读顺序排好再逐个裁剪、识别最后把位置和文本绑定在一起。注意perspective_crop对斜框要做四点透视变换如果直接把原图上最小外接矩形裁出来斜框的文本会被切掉一半识别率直接报废。5. 常见问题与避坑版本冲突、漏检、乱码和显存溢出5.1 TensorFlow 2.x 加载旧 h5 模型报错现象用tf.keras.models.load_model()加载压缩包里自带的 h5 权重报错提示找不到keras_metadata.pb或者出现Unknown op有时候报错信息很抽象直接挂在读取第一层权重上。原因这个代码包如果是在 TensorFlow 2.5.0 之前的环境里训练保存的Keras 的序列化格式和后来版本不完全兼容另外有的 h5 是用独立keras库保存的和你本地使用的tensorflow.keras不是同一个包op 注册表对不上。解决遇到这种情况别死磕 h5优先把权重导出成 SavedModel 格式再加载或者用load_model时报错的 op 名去搜对应版本。我每次拿到这类权重第一件事就是看保存它的训练脚本里 import 的是from tensorflow import keras还是import keras两种加载方式不通用。确认不了就直接用load_weights按层名加载配合model.build(input_shape)先建模型再填权重绕开序列化元数据。5.2 GPU 三件套不匹配训练时根本没用上显卡现象代码包在训练的时候终端打印Num GPUs Available: 0或者直接报CUDA_ERROR_NO_DEVICE但nvidia-smi明明能看到显卡。还有一种是显卡亮着但训练速度比 CPU 还慢明显没走 GPU。原因TensorFlow 的 CUDA 版本不是看系统装了什么就用什么它只认自己编译时对应的 CUDA 和 cuDNN 版本。常见组合是 TF 2.5.0 对应 CUDA 11.2 和 cuDNN 8.1而笔记本驱动装的是 550.144.03这个驱动版本对应的 CUDA 是 12.x两边的运行库对不上。新显卡比如 RTX 40 系在 TF 2.10 以下版本会导致 GPU 不可用这是另一个坑。解决先用nvidia-smi看驱动版本对应的 CUDA 能力再去查 TensorFlow 官方支持矩阵按矩阵装配套的 CUDA 和 cuDNN。不要动系统驱动把 CUDA 的路径配好用 conda 管理虚拟环境在环境里装正确版本的 tensorflow。排查命令我每次都跑这三行python -c import tensorflow as tf; print(tf.__version__) python -c from tensorflow.python.client import device_lib; print(device_lib.list_local_devices()) nvidia-smi看到最后一行能把 GPU 设备列出来再进训练脚本这个问题就算趟过去了。5.3 检测框碎掉或连成一片NMS 参数怎么调现象跑检测 demo 的时候原本是一行字的招牌结果检测框断成三四截或者反过来两行挨得近的文字被连成一个框识别出来一大串鬼画符。原因框碎掉通常是推理时的score_threshold设得太高笔画浅、字号小的文本候选得分低直接被过滤剩下的框连不成完整文本线框连成一片则是 NMS 的iou_threshold设得太大两个不同文本行的框重叠区域超过阈值也不被抑制。解决我一般把调试流程固定下来检测结果画出来先看两类问题再按策略调参。把score_threshold从 0.8 降到 0.7nms_threshold从 0.5 降到 0.3正常情况下碎框和粘连问题能同时缓解。anchor 的高度覆盖范围也要检查只覆盖 11 到 273 的话那些巨幅店招上的大字可能落在覆盖区间之外需要把 anchor 高度上限调大。每调一次参数就把检测框可视化结果存下来对照光看打印坐标看不出问题。5.4 中文标签乱码与字表索引错位现象训练验证的时候打印出的文本变成一堆问号或者乱码识别出来的结果像中文但完全对不上原文本还有的报UnicodeDecodeError直接中断训练。原因中文训练数据标注文件有的用 GBK 编码保存有的用 UTF-8脚本里open()默认按系统本地编码读Windows 下默认 GBKLinux 下默认 UTF-8两个环境读同一个文件结果不一样。字表从 GBK 文件读出来再按 UTF-8 存索引映射全乱识别结果自然全错。解决所有读写标注文件、字表文件的地方统一用codecs.open(path, r, encodingutf-8)或者open(path, r, encodingutf-8)显式指定编码不要依赖系统默认。字表生成之后手动检查一眼打印前 50 个字符确认没有“”以外的异常字符。写标签文件时也强制 utf-8并在脚本开头加一个断言检查char_list.txt能被完整读回且索引数不变这一招能防住 90% 的乱码问题。5.5 显存溢出小 batch 也 OOM问题不一定在 batch size现象batch_size 已经降到 8照样报ResourceExhaustedError用nvidia-smi看显存占用只有 2GB但程序说显存不够。原因TensorFlow 默认直接占用全部显存有时候是图初始化时预留的缓存太大更常见的是输入图片没限制最大边长原图 4000×3000 直接进网络特征图在 CNN 中间层的尺寸爆炸显存还没来得及回收就溢出了。解决在数据加载器里统一限制最长边不超过 960超过就等比缩放这和检测训练时的--max_len保持一致。TF 侧配置显存按需增长注意不要无脑设memory_growth后就不管它解决的是占满问题但模型本身的激活值仍然会累积。PyTorch 这边可以用torch.cuda.empty_cache()在验证阶段释放缓存训练阶段保持真实 batch 小、用梯度累积模拟大 batch。我实际跑的时候一张 960 以内的图batch_size 16 在 8GB 显存上是可以跑通的再大就要考虑混合精度了。6. 进阶合成数据、模型导出与二次开发的切入点6.1 用合成数据把中文识别率拉起来真实场景标注很贵特别是中文几千个类别的样本要覆盖全靠人工拍照标注不现实。代码包里的data/synth_text.py就是干这个的找一批字体文件把字符随机拼成词组贴到随机背景上加一点噪声、旋转、颜色扰动自动生成带标签的训练图。合成脚本的核心逻辑不复杂from PIL import Image, ImageDraw, ImageFont import random def synth_text(text, font_path, out_size(280, 32)): img Image.new(RGB, out_size, (255, 255, 255)) draw ImageDraw.Draw(img) font ImageFont.truetype(font_path, random.randint(20, 28)) draw.text((4, 2), text, fontfont, fill(0, 0, 0)) # 加轻微旋转和噪声模拟自然场景 img img.rotate(random.uniform(-2, 2), expandFalse) return img字体文件是合成数据效果的分水岭至少要准备三种风格黑体、宋体、楷体每个样本随机用其中一种。生成时按“图片文件名 标签文本”命名之后一键生成训练列表文件。合成数据训练出的模型在合成集上准确率很高但真实场景会掉点常见做法是合成数据和真实数据按 7:3 混在一起训练。我从实践里的体会是合成数据主要解决字表覆盖率真实数据负责把模型拉回现实。6.2 从 h5/pth 到部署导出 SavedModel 或 ONNX毕设项目可能只要能在 Jupyter 里跑通就行但如果你要把这套 OCR 接成接口模型导出这步省不了。TensorFlow 侧导出 SavedModel训练完调用model.save(ocr_export, save_formattf)或者tf.saved_model.save(model, ocr_export)这样的目录结构可以脱离 Python 训练环境被服务端加载。PyTorch 侧导出 ONNX核心是固定输入尺寸和动态轴。中文识别模型建议把宽度维设成 dynamic这样调用方送任意宽度的图片都行torch.onnx.export( model, dummy_input, ocr_crnn.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch, 2: height, 3: width}} )注意dynamic_axes里把宽高都标成动态否则导出的模型只认固定 280×32 的输入换张细长图直接报 shape 不匹配。导出之后用 ONNX Runtime 或者 TensorRT 推理速度一般比原框架快不少。6.3 你要改成身份证识别或车牌识别从哪里下手拿到这套代码想改成特定业务我的建议是按这个顺序动手先跑通原版 demo生成一张带检测框的结果图确认整条链路正常然后替换数据这一步最花功夫检测侧如果有斜框就补一些倾斜样本识别侧把字表缩成你需要的字符集合改完数据改配置识别模型的输出类别数必须和字表长度对齐这是最容易漏的地方最后才是动网络结构。改身份证号识别时识别字表缩小到 10 个数字加一个 X训练收敛速度会明显变快准确率比通用字表高一大截。改票据识别时检测模型保持原样识别侧把高度从 32 调到 48因为票据数字笔画细、密度高高度压缩太狠会把笔画融在一起。我头一回跑这种毕设代码包是拿到手就直接冲训练脚本结果环境搭了三天没跑起来后来换了个顺序省了很多事先看 config 里的参数再跑测试 demo确认输入输出接口最后才动训练。从那以后我每次拿到任何 OCR 相关工程包都强制自己先花半小时把框架版本、字表编码、输入输出尺寸这三个基线对齐再谈调参和改造。希望帮到你。本文还有配套的精品资源点击获取