多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

PyTorch实战:文字点选验证码检测与识别全流程解析

PyTorch实战:文字点选验证码检测与识别全流程解析 简介这份资源面向深度学习与计算机视觉方向的开发者、安全验证研究人员及中文OCR初学者聚焦用PyTorch实现文字点选、选字与选择文字类验证码的检测识别。包内共33个文件以20个Python脚本为核心覆盖模型定义、训练与推理流程另含3个ONNX模型文件、若干png/jpg/gif示例图、txt字符集与说明文档压缩包约146.78MB目录按service、model、src、utils等模块划分结构清晰便于按功能查阅。内容涉及PyTorch动态计算图、文字区域检测、CTC或Attention序列识别、中文字符集处理、数据预处理与模型评估部署等环节可帮助读者理解从数据标注到服务封装的完整链路。目前已有2492人学习下载适合希望系统掌握中文验证码识别方案、对照代码复现实验并积累工程排错思路的读者参考。1. 文字点选验证码识别从 PyTorch 检测到识别的完整落地路径做爬虫和自动化的同行大概率都遇到过这种场景目标站点把验证码从四位数字换成了「请依次点击 春 风 得 意」这类文字点选传统 OCR 直接整图识别根本拿不到坐标用打码平台成本又压不下来。这个资源包解决的正是这个问题——它用 PyTorch 把「中文字检测 文字识别 点选坐标回归」串成一条完整链路输入一张带提示语的验证码图输出每个目标字在图中应该点击的位置。适合有 Python 基础、想自己搭一套可控识别流程的从业者也适合正在做深度学习课程设计、需要一份能跑通的中文检测识别实战代码的人。包里是源码工程不是调好的模型权重所以你得自己准备数据、自己训练但好处是整条链路透明每个环节都能改。2. 拆开这个包目录结构、依赖与数据流走向2.1 工程目录与模块职责拿到压缩包解压后常见做法是先别急着跑花五分钟把目录扫一遍。这类点选识别工程一般会拆成几个职责清晰的目录datasets/放数据加载和标注解析models/放检测和识别的网络定义utils/放图像预处理、坐标变换、可视化这些工具函数configs/或根目录下的config.py放超参train.py和predict.py分别是训练和推理入口。检测部分通常基于轻量骨干网络加检测头识别部分要么是 CRNN 加 CTC要么是带注意力解码的序列模型点选坐标则通过检测框中心或关键点回归得到。先确认模块边界是因为后面调参和排错时你得知道改哪个文件。比如识别准确率上不去问题可能在datasets的字符集映射也可能在models的解码逻辑目录不清就只能瞎试。2.2 环境依赖与版本对应PyTorch 生态最劝退新人的就是版本对应。这个工程依赖 PyTorch、torchvision、OpenCV、NumPy、Pillow可能还有pyyaml和tqdm。装之前先确认显卡驱动和 CUDA 版本再选对应的 PyTorch 轮子。下面这套流程是我一般会走的顺序# 1. 建独立环境别污染系统 Python conda create -n captcha_ocr python3.9 -y conda activate captcha_ocr # 2. 先看显卡和驱动支持的 CUDA 上限 nvidia-smi # 3. 按官网对应关系装 PyTorch示例为 CUDA 11.8 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 4. 装其余依赖 pip install opencv-python numpy pillow pyyaml tqdm逻辑说明第一步用 conda 隔离环境避免和系统里已有的 PyTorch 打架第二步nvidia-smi右上角的 CUDA Version 是驱动支持的上限不是你必须装的版本装低于它的都兼容第三步一定要走官方 index别用某些镜像源里版本错乱的包否则会出现torch.cuda.is_available()返回 False 这种玄学问题。参数上Python 选 3.8 到 3.10 最稳3.11 以上部分老工程会有兼容坑。装完跑一句验证import torch print(torch.__version__) print(torch.cuda.is_available()) # 期望 True print(torch.cuda.get_device_name(0))如果第二行是 False先别怀疑代码九成是 PyTorch 装成了 CPU 版或者 CUDA 版本和驱动不匹配。这时候回退去官网用它的选择器重新生成安装命令比在网上乱搜「pytorch 安装教程超详细」靠谱得多。2.3 数据从哪来、标注长什么样点选验证码的数据没法直接下载现成的通常得自己采集。流程是用脚本批量请求验证码接口把图片和对应的提示语一起存下来提示语里的字就是待点击目标。标注格式常见两种一种是检测用的四点框或矩形框坐标一种是识别用的字符序列。工程里一般会约定一个label.txt每行形如图片名\t提示语\tx1,y1,x2,y2,...。这里有个容易被忽略的点提示语里的字和图中实际渲染的字必须能对上而且顺序要一致。如果站点会随机旋转、扭曲、加干扰线标注时框要贴紧字形别把干扰线框进去否则检测头学到的就是噪声。数据量上检测阶段每个字符类别至少几百个样本起步识别阶段字符集如果覆盖常用汉字建议单字样本不低于 200不然模型只会认高频字。3. 检测与识别怎么串网络结构、损失与训练循环3.1 检测头与识别头的分工这条链路的核心思路是「先定位再识别」。检测网络负责在整图里找出所有文字区域输出一批候选框识别网络对每个候选框做序列识别得到框内是什么字最后把识别结果和提示语里的目标字做匹配匹配上的框中心坐标就是点击点。检测常用的是类似 DBNet 或 EAST 的可微分二值化思路识别常用 CRNN。选 CRNN 而不是纯 CNN 分类是因为验证码里的字可能长短不一、还可能带旋转序列模型对不定长更友好。为什么不用「整图直接回归坐标」这种端到端方案因为点选验证码的目标字数量不固定提示语可能是两个字也可能是五个字端到端回归的输出维度不好定泛化差。拆成检测加识别每个模块职责单一出问题也好定位。3.2 训练循环与关键参数训练脚本的主循环结构大同小异关键是损失函数和优化器配置。检测部分常用 Dice Loss 加二值交叉熵识别部分用 CTC Loss。下面是一个简化后的训练骨架import torch from torch.utils.data import DataLoader from datasets.captcha_dataset import CaptchaDataset from models.detector import Detector from models.recognizer import CRNN device torch.device(cuda if torch.cuda.is_available() else cpu) # 数据集与加载器 train_set CaptchaDataset(rootdata/train, label_filedata/train/label.txt) train_loader DataLoader(train_set, batch_size16, shuffleTrue, num_workers4) # 两个网络分开训练先训检测再训识别 detector Detector().to(device) recognizer CRNN(num_classeslen(train_set.charset)).to(device) opt_det torch.optim.Adam(detector.parameters(), lr1e-3) opt_rec torch.optim.Adam(recognizer.parameters(), lr1e-3) for epoch in range(100): detector.train() recognizer.train() for imgs, boxes, texts in train_loader: imgs imgs.to(device) # ---- 检测分支 ---- pred_maps detector(imgs) loss_det detector.criterion(pred_maps, boxes.to(device)) opt_det.zero_grad() loss_det.backward() opt_det.step() # ---- 识别分支 ---- crops detector.crop_by_boxes(imgs, boxes) # 按框裁剪 logits recognizer(crops) loss_rec recognizer.ctc_loss(logits, texts) opt_rec.zero_grad() loss_rec.backward() opt_rec.step() print(fepoch {epoch} det_loss{loss_det.item():.4f} rec_loss{loss_rec.item():.4f})逻辑说明检测和识别用两个独立优化器是因为它们的收敛速度差异大混在一起容易一方被另一方带偏。batch_size16是显存和稳定性的折中显存够可以往上加但学习率要同步调。num_workers4在 Windows 上有时会报多进程错误遇到就改成 0。识别分支里crop_by_boxes是关键它把检测框从原图裁出来再送识别训练时用的是真值框推理时用的是检测预测框这个 train/infer 不一致是后面掉点的常见来源。参数上学习率 1e-3 是 Adam 的常规起点如果 loss 震荡就降到 5e-4CTC 训练前期 loss 下降慢是正常的别急着换模型。字符集charset一定要在训练前固定并保存推理时必须用同一个映射否则识别出来的字全是乱的。3.3 推理与坐标还原推理阶段要把检测框映射回原图坐标再和识别结果拼起来。常见做法是detector.eval() recognizer.eval() with torch.no_grad(): prob_map detector(img_tensor) boxes detector.postprocess(prob_map, threshold0.3) # 二值化连通域 results [] for box in boxes: crop crop_by_box(img_tensor, box) text recognizer.decode(crop) # CTC 贪心解码 results.append((text, box)) # 与提示语匹配输出点击坐标 targets list(prompt) points [] for t in targets: for text, box in results: if t text: points.append(box.center) breakthreshold0.3是检测二值化阈值调高会漏检、调低会多检得在验证集上试。坐标还原时注意图像预处理有没有做 resize 和 padding如果有要把框坐标按比例映射回去这一步漏了就会出现「识别对了但点偏了」的血泪经验。4. 避坑与排查训练不收敛、点偏、显存炸的常见原因4.1 现象loss 一直不降或剧烈震荡原因通常有三个学习率过大、标注框和图像没对齐、字符集映射错乱。先查标注把几张图和框可视化出来看框是不是贴住了字。再查字符集确认label.txt里的字都在charset里没有的会被静默丢弃导致标签和输出对不上。解决方式是先把学习率降到 1e-4 跑几十步看 loss 是否稳定下降同时加一个可视化脚本定期 dump 预测框。4.2 现象识别结果对但点击位置偏移这是最典型的坑。原因多半是预处理做了 resize 或 padding但坐标还原时没做逆变换。解决是在数据集里记录每张图的缩放比例和 padding 偏移推理时按同样参数反算。另一个可能是检测框中心取的是框的几何中心而验证码要求点字形中心遇到长条形字会偏常见做法是用框内像素重心代替几何中心。4.3 现象显存不足CUDA out of memory原因可能是 batch_size 太大、图像分辨率太高或者训练循环里忘了torch.no_grad()导致计算图累积。解决顺序先把 batch_size 减半再把输入尺寸降到 320 以内检查验证和推理代码有没有包在no_grad里。如果还炸用torch.cuda.empty_cache()在 epoch 之间清一次但这是缓解不是根治根治还是降规模。4.4 现象CPU 能跑 GPU 报错常见于 PyTorch 版本和 CUDA 不匹配或者张量有的在 CPU 有的在 GPU。解决是统一在模型和数据加载时.to(device)并确认torch.cuda.is_available()为 True。如果装的是 CPU 版 PyTorch重装 GPU 版即可别在代码里硬改。4.5 现象验证集准确率远低于训练集过拟合的典型表现。原因可能是数据量太小、模型太大、没有做数据增强。解决是加随机旋转、透视变换、颜色抖动这类增强同时加 dropout 和权重衰减。点选验证码的干扰线和背景噪声本身就是天然增强别把图预处理得太干净否则模型学不到鲁棒特征。5. 进阶技巧把识别准确率再往上推一档训练能跑通只是及格线真正上线还得抠准确率。我一般会从三个方向下手。第一是检测框的精细度用 DBNet 的可微分二值化代替固定阈值让阈值随图像自适应能明显减少漏检和粘连。第二是识别端的解码策略CTC 贪心解码快但容易错换成 beam search 加语言模型约束对形近字比如「未」和「末」的区分会好很多。第三是后处理匹配逻辑提示语里的字如果在识别结果里出现多次别取第一个取置信度最高的那个框。验证方法上别只看整体准确率要拆成「检测召回率」「识别准确率」「点选命中率」三个指标分别看。检测召回低就调阈值和增强识别准确低就查字符集和解码点选命中低就查坐标还原。下面这张表是我常用的排查对照指标偏低时的首要排查点常用调整检测召回率二值化阈值、标注框质量降阈值、重标框识别准确率字符集映射、解码方式统一 charset、换 beam search点选命中率坐标逆变换、中心点定义补逆变换、改像素重心还有个容易被忽视的技巧把检测和识别做成两阶段后可以拿识别置信度反过来过滤检测框低置信度的框直接丢掉能压掉一批误检。这个思路在工程里很实用代价是多一次前向但准确率提升值这个开销。从那以后我每次接这类点选识别项目都强制先把「预处理参数 → 坐标还原」这条链路单独写个测试脚本跑通再动模型。因为模型再准坐标映射错一步前面全白干。希望帮到你。本文还有配套的精品资源点击获取
返回列表