☰
基于one-hot编码与CNN的5位数验证码识别完整实战
2026/10/1 22:45:50 网站建设 项目流程

简介:一套基于独热编码与卷积神经网络实现五位验证码识别的完整项目,包含可直接运行的Python源码、数据集与详细注释,适合计算机相关专业学生用于毕业设计、课程设计或深度学习入门实践。项目立足真实验证码图片识别场景,从数据预处理、独热标签构造到卷积神经网络模型构建与训练评估均有清晰实现,代码注释便于逐段理解,稍加改动即可扩展。包体共两千个文件,包括一千九百八十张样本图片、六个Python主程序、八个配置文件及说明文档,整体大小四十三点二五兆字节,数据与代码结构规整,便于本地复现。目前已有一百八十四人学习下载。该资源对想快速上手图像识别任务的初学者尤其友好,既可作为入门进阶的练习素材,也能作为毕业设计初期的项目雏形;对有一定基础的学习者,还可基于现有模型调整网络结构或增加数据增强,进一步探索验证码识别与光学字符识别相关技术。

1. 5位数验证码识别,one-hot编码和CNN是怎么搭伙干活的

做验证码识别的同学应该都有过这种体验:网上找了一堆所谓“源码包”,下载下来要么缺数据集,要么跑起来报错,最后还得自己从零开始写。这个项目标题给了一个比较完整的路径:用one-hot编码给5位数验证码做标签,配合CNN卷积神经网络做识别,附带python源码、数据集和详细注释,定位是能直接拿来当毕设。整套方案的思路很直接——不搞CTC、不搞循环神经网络,就是老老实实把“识别5个字符”拆成“分类5次”,每个字符用one-hot编码表示成10维向量,CNN负责把图像特征提出来,最后接全连接层输出5组概率分布。这个方案适合两类人:一类是正在做图像识别方向毕业设计、需要稳定跑通一个完整项目的同学,另一类是刚入门深度学习、想搞清楚CNN和one-hot编码实际怎么用在真实任务上的开发者。它解决的痛点很明确:验证码识别不是只有一个模型结构问题,还包括数据怎么来、标签怎么编、训练怎么收敛、预测怎么后处理。

2. 先懂原理再动手:one-hot标签在CNN里到底改了什么

2.1 把“识别5个字符”拆成“分类5次”:one-hot编码的维度设计

验证码图里的内容是5个数字,每个数字是0到9。这个项目把识别任务定义为:输入一张图片,输出5个数字。从深度学习角度讲,这不是一个端到端的序列识别问题,而是5个独立的分类问题。因为5个字符有固定位置(虽然可能有轻微偏移),CNN只需要学会“每个位置上是哪个数字”。

one-hot编码在这里的作用,是把“数字3”这种类别标签变成向量[0,0,0,1,0,0,0,0,0,0]。每个字符对应一个10维向量,5个字符就拼接成50维的标签向量。训练时,CNN最后一层输出也是50维——前10维是第1个字符的概率分布,第11到20维是第2个字符,依此类推。

这种标签设计和直接输出一个5位整数完全不同。如果标签是“12345”这种整体数字,模型要学习的映射空间是10万类,数据量很难撑起来。拆成5个10分类之后,每个位置只需要区分10个数字,训练难度指数下降。这就是one-hot编码在这个项目里的核心价值:把复杂的多标签任务拆解成简单分类任务的组合。

标签维度设计的细节:

# 假设一个5位验证码的文本标签是 "12345" # 每个字符转成one-hot向量,再拼接成50维标签 import numpy as np num_classes = 10 # 0-9共10类 label_str = "12345" def text_to_onehot(text, num_classes=10): onehot = [] for ch in text: vec = np.zeros(num_classes, dtype=np.float32) vec[int(ch)] = 1.0 onehot.extend(vec) # 拼接成50维 return np.array(onehot, dtype=np.float32) label_vector = text_to_onehot(label_str) print(label_vector.shape) # 输出 (50,)

这里每个字符独立做one-hot,再按顺序拼接。训练时的损失函数用二元交叉熵或者分类交叉熵都可以,但要注意标签的维度必须和模型最后一层输出维度严格一致。很多新手在这步翻车,后面第5章会详细讲这个坑。

2.2 CNN不是黑匣子:验证码图像从输入到输出的张量流向

CNN在这个项目里干的活,是把一张验证码图片(比如60x160像素)变成5组数字概率。整个过程可以拆成四个阶段:图像输入、卷积特征提取、全局特征整合、分类输出。

图像输入阶段,图片会被缩放到固定尺寸,转成张量 shape 为(height, width, channels)。彩色图是3通道,灰度图是1通道。这个项目最常见做法是转灰度,因为验证码的背景噪点往往是彩色干扰,灰度化之后反而突出字符轮廓。

卷积特征提取阶段,多个卷积层和池化层交替堆叠。卷积层用滑动窗口提取局部特征——竖线、横线、曲线、断点、交叉点,这些底层特征逐层组合成更抽象的字符结构。池化层的作用是降维,把图像尺寸逐步减半,同时保留主要特征。这个阶段输出的特征图,已经不再是原始像素,而是“这张图里有哪些对分类有用的模式”。

分类输出阶段,特征图会被展平(flatten),送入全连接层,最后输出50维向量。50维向量里每10维经过softmax变成概率分布,取最大值对应的下标就是预测数字。

值得注意的一点是:CNN不是天生就知道“验证码里第2个字符是什么”。它学到的是——经过卷积池化后,特征图里某个空间区域对应第2个字符的位置。所以网络结构里的全连接层需要足够宽,才能把位置信息和字符信息关联起来。

2.3 为什么不用LSTM和CTC:单字符切割稳定的场景,CNN是最省事的选择

验证码识别还有一条常见技术路线——CRNN + CTC Loss,也就是用CNN提特征、LSTM建模序列、CTC做对齐。这条路在长度不固定、字符粘连严重的验证码上效果更好,但代价是训练复杂度高、调参难度大,模型收敛也慢。

这个标题里的项目用的是纯CNN,说明它的应用场景经过了一次重要假设:验证码字符之间没有粘连、位置相对固定。这种假设在实际工程里是站得住的——很多网站的验证码虽然加了噪点和干扰线,但字符间距是均匀的,CNN不需要自己学会“切分字符”这个动作,只需要学会“每个固定区域是什么数字”。

从毕设角度讲,纯CNN方案的价值在于:每个环节都能在论文里讲清楚,数据预处理、网络结构设计、损失函数选择、训练调参,每一步都有明确的理论依据。CRNN方案虽然显得更“高级”,但训练不稳定、调参周期长,对本科毕设来说风险偏高。

另外,纯CNN方案在推理速度上有优势。没有循环结构,整个模型可以一次性前向传播得到结果,对于“输入图片、输出5个数字”这种定长识别任务,计算效率比序列模型高一个量级。如果你要做一个验证码识别的演示系统,每秒处理几十张图是小意思。

2.4 训练集怎么准备:仿真生成在当前场景比爬真实样本更靠谱

验证码识别的训练数据有两个来源:真实抓取和仿真生成。真实抓取的数据更贴近线上环境,但存在三个问题:标签获取难(你得自己识别或对接接口)、样本分布不可控、可能涉及合规风险。所以这个项目选择的是仿真生成——自己写代码生成验证码图片,字符、字体、颜色、噪点、扭曲全部可控,标签是生成时就确定的,天然准确。

仿真生成的好处不只是方便。对CNN训练来说,数据多样性直接决定模型的泛化能力。你可以控制干扰线密度、噪点强度、字符旋转角度,把训练集做得比真实场景更难,这样模型在真实场景上反而更鲁棒。这也是我在做类似项目时一直推荐的做法:先用仿真数据把模型打通,再考虑要不要加入真实样本微调。

数据生成代码的关键结构:

from PIL import Image, ImageDraw, ImageFont, ImageFilter import random import numpy as np def generate_captcha(text, width=160, height=60): """生成单张验证码图,返回图片数组和标签文本""" image = Image.new('RGB', (width, height), (random.randint(200, 255),) * 3) draw = ImageDraw.Draw(image) # 绘制干扰线 for _ in range(random.randint(3, 6)): x1, y1 = random.randint(0, width), random.randint(0, height) x2, y2 = random.randint(0, width), random.randint(0, height) draw.line((x1, y1, x2, y2), fill=(random.randint(0, 150),) * 3, width=2) # 逐个绘制字符,模拟轻微偏移 font = ImageFont.truetype("arial.ttf", 36) for i, ch in enumerate(text): x = 10 + i * 30 + random.randint(-3, 3) y = random.randint(5, 15) draw.text((x, y), ch, font=font, fill=(random.randint(0, 150),) * 3) # 添加高斯模糊和噪点 image = image.filter(ImageFilter.GaussianBlur(0.8)) img_array = np.array(image.convert('L'), dtype=np.float32) / 255.0 return img_array, text

这里有几个参数决定了训练集的难度:字符间距(30像素)、偏移范围(±3像素)、模糊半径(0.8)、干扰线数量(3到6条)。这些参数可以在后续训练时动态调整,我一般建议先用低难度参数训练出一个baseline,再逐步加大难度做数据增强,而不是一上来就生成特别扭曲的图。

3. 在本地跑通这个毕设源码包:目录、依赖与最小命令

3.1 拿到压缩包之后,先把项目结构理清楚

源码包解压之后,第一件事不是看代码,而是先看清文件结构。常见的结构是这样的:数据集文件夹(存放生成好的训练图片或生成脚本)、模型定义文件(CNN网络结构)、训练脚本、预测脚本、以及一个requirements.txt或README说明文档。

拿到项目先做三件事。第一,看数据文件夹里有没有现成图片,还是需要通过脚本生成;第二,看模型定义文件和训练脚本是不是分离的;第三,看训练好的权重文件是否存在。这三件事决定了你是“直接能跑预测”还是“需要先训练再预测”。

如果数据集文件夹里是空的,也没有生成脚本,那这个项目就得先自行造数据,工作量会多不少。如果是带生成脚本的,先在本地跑一遍生成脚本,确认图片能正常产出、格式是对的,再接训练。这个顺序能帮你把“数据问题”和“模型问题”分开排查。

3.2 环境依赖与安装:Python版本和Keras/TensorFlow这套常见的组合

这类项目最常见的依赖组合是Python 3.8到3.10 + TensorFlow 2.x + Keras + NumPy + Pillow + OpenCV。TensorFlow 2.x集成了Keras,直接顺序安装。

依赖安装:

# 建议用虚拟环境,Python 3.9 兼容性最好 python -m venv captcha_env source captcha_env/bin/activate # Windows下是 captcha_env\Scripts\activate pip install tensorflow==2.13.0 numpy pillow opencv-python

这里TensorFlow 2.13是兼容性比较稳的版本。装完验证一下:

python -c "import tensorflow as tf; print(tf.__version__); print(tf.config.list_physical_devices('GPU'))"

输出里能看到GPU信息,说明训练环境就绪。如果没有GPU,TensorFlow会自动走CPU,5位数验证码这种小数据集CPU也能接受。我见过不少人在环境上卡很久,大部分是Python 3.11以上版本和旧版TensorFlow不兼容,建议直接从Python 3.9 + TensorFlow 2.13起步。

3.3 从一行命令跑通到保存模型:生成数据、训练、预测的最小流程

这个项目的最少必要操作是三步。第一步,生成或加载数据集;第二步,训练模型并保存权重;第三步,加载权重预测新图。把一个最小流程拆开,代码逻辑如下。

最小训练流程:

import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout from tensorflow.keras.utils import to_categorical # 假设 generate_captcha 已经准备好 # 生成训练集 X_train, y_train = [], [] for _ in range(5000): img, text = generate_captcha("12345") # 实际使用时随机生成5位数字 X_train.append(img) y_train.append(text_to_onehot(text)) X_train = np.array(X_train).reshape(-1, 60, 160, 1) y_train = np.array(y_train) # CNN模型:3层卷积 + 3层池化 + 全连接输出50维 model = Sequential([ Conv2D(32, (3, 3), activation='relu', input_shape=(60, 160, 1)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Conv2D(128, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Flatten(), Dense(512, activation='relu'), Dropout(0.5), # 防止过拟合 Dense(50, activation='sigmoid') # 50维输出,每10维对应一个字符 ]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) model.fit(X_train, y_train, batch_size=64, epochs=20, validation_split=0.1) model.save('captcha_model.h5')

这段代码设计的核心是最后的输出层:50维,用sigmoid激活。前面的卷积层参数——32、64、128是卷积核数量,逐步翻倍是常见做法,因为越靠后的层越需要提取高层语义特征。Dropout 0.5写在全连接层之后,目的是让模型不依赖某几个神经元的组合,提升泛化能力。

预测新图的流程:

from tensorflow.keras.models import load_model model = load_model('captcha_model.h5') def predict_captcha(img_array): """输入60x160的灰度图,输出5位数字字符串""" x = img_array.reshape(1, 60, 160, 1).astype(np.float32) pred = model.predict(x)[0] # shape (50,) result = "" for i in range(5): # 每10维取最大概率下标,就是该位置的数字 digit = np.argmax(pred[i*10:(i+1)*10]) result += str(digit) return result

预测逻辑里dim取10是写死的,对应0到9共10个类别。如果改成字母验证码(比如数字+小写字母),这个10要改成36,同时标签编码也要跟着改。预测时有个小细节:因为用了sigmoid而不是softmax,理论上每个位置的10维概率和不为1,但这不影响argmax取最大值的逻辑,实际使用完全没问题。

3.4 刚开始训练看到的输出:loss和accuracy具体怎么读

训练启动后,终端会每个epoch打印一行指标,通常是loss、accuracy、val_loss、val_accuracy。很多第一次跑这个项目的人看到loss在0.1左右、accuracy在99%以上,就觉得模型已经练好了,这其实是个误区。

训练集准确率高不代表预测效果好。当训练集里有大量相似图片时,模型很可能是在“背答案”。判断训练是否正常,重点看val_loss和val_accuracy——这是模型在没见过的数据上的表现。如果val_accuracy在97%以上,说明模型的泛化能力基本可用。如果val_loss长时间不降甚至回升,而训练集loss还在降,那就是过拟合了。

另外要注意:这类验证码项目的准确率指标是全图级别的。只要5个字符里有一个猜错,整张图就算识别失败。所以即便字符级准确率是99%,全图级准确率可能是95%(0.99的5次方)。写论文或做演示时,一定要说清楚这个准确率是字符级还是图片级,我见过好几个人在这个问题上被答辩老师问住。

4. 提高识别率的必调参数:图像尺寸、学习率、batch和one-hot标签形态

4.1 图像尺寸:字符越大背景越少,CNN学得越容易

验证码图片的尺寸直接决定了CNN能“看到”多少细节。原图如果只有40x120,字符区域可能只有20像素高,卷积核扫描时很难捕捉到笔画特征。最常见的做法是统一缩放到60x160,这样字符占的面积比例更大,背景噪点占比变小,模型学起来更容易。

缩放操作在数据生成或预处理阶段完成:

from PIL import Image import numpy as np def preprocess_image(img_path, target_size=(60, 160)): """读取图片,缩放并转灰度,返回(60, 160, 1)的float32数组""" img = Image.open(img_path).convert('L') # 灰度化 img = img.resize(target_size) # 统一尺寸 arr = np.array(img, dtype=np.float32) / 255.0 return arr.reshape(target_size[0], target_size[1], 1)

这里把像素值除以255是标准操作。如果不归一化,像素值在0到255之间,CNN的梯度计算容易不稳定,训练前期loss下降会很慢。图像尺寸不是越大越好——尺寸翻倍意味着计算量增加4倍,但对验证码识别精度的提升非常有限,60x160对5位数验证码已经够了。

4.2 学习率和batch size:验证码这类小数据集最容易过拟合的地方

学习率是最影响训练稳定性的超参数。在TensorFlow的adam优化器里,默认学习率是0.001,对验证码识别这种小数据集来说,这个值基本能工作。但如果你发现loss震荡剧烈,或者训练集准确率很高而验证集很差,就要往下调。

batch size的影响容易被忽略。batch太小(比如8),每个batch的数据分布波动大,loss曲线会上下跳动;batch太大(比如512),模型收敛速度变慢,而且在小数据集上更容易陷入局部最优。对5000张训练图这个规模,64到128是比较稳妥的区间。

训练时的回调函数建议加两个:学习率衰减和早停。

from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping callbacks = [ ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-6), EarlyStopping(monitor='val_loss', patience=8, restore_best_weights=True) ] model.fit(X_train, y_train, batch_size=64, epochs=50, validation_split=0.1, callbacks=callbacks)

ReduceLROnPlateau的作用是:当验证集loss连续3轮不下降,就把学习率乘以0.5。EarlyStopping的作用是:验证集loss连续8轮不下降,就停止训练并恢复到效果最好的那轮权重。这两个回调能帮你在训练时少做很多人工干预,也是毕设论文里可以写进去的工程细节。

4.3 五个分类器如何共用一个特征提取器:全连接层的拆分逻辑

网络结构里有一个容易忽略的设计点:模型最后虽然输出了50维,但结构上只有一个全连接层,不是5个并列的全连接层。这其实是刻意的选择——5个字符共享同一个CNN特征提取器,而不是各自训练一套特征。

这样做的理由是:验证码里5个字符的字体、颜色、干扰线都是同一套生成逻辑,字符之间的底层特征是共享的。比如“横线”“竖线”“圆弧”这些基本笔画,不管出现在第1位还是第4位,它们在卷积层里的特征表示应该是一致的。共享特征提取器意味着用5倍的数据量来训练同一套特征参数,训练效率更高,泛化效果也更好。

如果你想让模型更精细,可以把最后一层改成5个独立的Dense(10)分支,每个分支接softmax。这种多输出结构在Keras里也能实现,但要改损失函数和训练逻辑,复杂度高不少。在验证码字符不粘连的前提下,单个Dense(50)的共享结构完全够用,也更稳。

4.4 用训练曲线判断该停还是该接着跑

训练过程不要只看最后一个epoch的数值,要把loss和accuracy的走势结合起来看。这里有三条经验法则。

第一,训练集loss和验证集loss一起降,说明模型还在学,继续训练。第二,训练集loss继续降但验证集loss开始回升,说明过拟合开始,早停回调会自动处理。第三,两个loss都降不动了,且验证集准确率在97%以上,说明模型能力已到瓶颈,这时候单纯加大训练轮数没有意义,要考虑增加数据量或让数据更复杂。

一个训练中的典型输出对比:

Epoch 12/30 loss: 0.0234 - accuracy: 0.9941 - val_loss: 0.0876 - val_accuracy: 0.9812 Epoch 18/30 loss: 0.0102 - accuracy: 0.9973 - val_loss: 0.1123 - val_accuracy: 0.9765

看到这种“训练集在涨、验证集在跌”的模式,说明模型在过拟合。即使val_accuracy还有97%,继续训练只会让模型更偏激。正确的做法是停止训练,或者提高Dropout比例、加大数据增强强度。

5. 避坑与排查:5位数验证码识别最常见的5个问题

这一章我把实际跑项目时最容易遇到的5个坑记录下来,全部按“现象、原因、解决”的结构写。

5.1 训练loss正常下降,预测却全错

现象:训练过程中loss降到0.1以下,训练集准确率接近100%,但拿一张全新的验证码图去预测,5个字符全错或错一大半。

原因:训练集和预测时的图像预处理不一致。最常见的有三种——训练时用的灰度图,预测时忘了转灰度,直接把彩色图喂进去;训练时做了归一化到0到1,预测时没做;训练时图片尺寸是60x160,预测时是原始尺寸。模型学到的所有特征都建立在固定输入分布上,输入分布一变,输出全乱。

解决:把预处理逻辑统一封装成一个函数,训练和预测都走同一个入口。上面写的preprocess_image函数就是干这个的。调一次预测函数,打印中间数组的shape和数值范围,核实最小值是0、最大值是1。

5.2 标签维度对不上:训练时报错或loss不降

现象:model.fit直接报维度错误,或者loss卡在某个值附近完全不动。

原因:最后一层Dense(50)的50和标签向量的维度不一致。比如你只对单个字符做了one-hot(10维),或者忘了拼接5个字符的标签(实际只有10维),或者用to_categorical把整体label转成了10类。

解决:在训练脚本里加两行检查代码:

assert y_train.shape[1] == 50, f"标签维度应为50,实际为{y_train.shape[1]}" assert model.output_shape[-1] == 50, f"输出维度应为50,实际为{model.output_shape[-1]}"

如果标签维度不是50,回到text_to_onehot函数,确认循环体内的onehot.extend(vec)确实执行了5次。常见的低级错误是return写在了循环体里,导致只拼接了一个字符。

5.3 数据增强把字符切坏了

现象:加入旋转、缩放、平移等数据增强后,验证集准确率反而大幅下降。

原因:数据增强的强度设置太激进。比如旋转角度设成30度,字符旋转后超出了原本的“格子”,看起来像两个字符叠在一起;或者平移幅度过大,把边缘字符推出了图像边界。模型学到的特征被这些坏样本污染了。

解决:在数据增强的调试阶段,先把增强后的图片可视化,确认增强后的图片“人眼还能认出来”再进训练集。

import matplotlib.pyplot as plt # 增强后立即可视化,而不是直接喂给模型 plt.imshow(augmented_img, cmap='gray') plt.title("check if human can read") plt.show()

对于验证码场景,旋转范围控制在±8度以内,平移控制在±3像素。增强的目的不是制造人类都认不出的图片,而是让模型见到的字符笔画有轻微变化。

5.4 验证集是自己人:数据泄露导致指标虚高

现象:验证集准确率98%,但换一批真实场景图片来测,准确率跌到70%。

原因:训练集和验证集是从同一个生成器里出来的,甚至在生成时用了同一套随机种子。模型等于“见过”验证集的形态风格,指标自然虚高。这在毕设评审时很容易被追问。

解决:在生成训练集和验证集时,确保两批数据完全独立生成,并且用不同字体、不同干扰线密度。更严格的做法是——训练集用一套生成参数,验证集用另一套更强的干扰参数,这样指标才真实反映模型的泛化能力。正好在项目报告里标注“独立测试集”也是一个加分点。

5.5 模型文件太大或推理太慢,演示环节掉链子

现象:训练好的模型文件几百MB,树莓派或普通笔记本上跑一次预测要100毫秒以上,现场演示流畅度很差。

原因:卷积核数量过多、全连接层过大。模型结构里的Dense(512)参数量很大,是模型体积的主要来源。

解决:在演示版本里,把Dense(512)改成Dense(128),训练20轮,准确率基本不降,但模型体积能减小到原来的四分之一。或者用TensorFlow Lite做量化,把浮点模型转成int8模型,体积能压到20MB以内,推理速度还能提升。

6. 再往前走一步:把单模型准确率从95%推到99%的验证方法

模型训练完、准确率到95%左右,毕设看起来已经完整了。如果你想在这个基础上再提升,并且论文里能多写几页有价值的实验,可以尝试一个低成本高回报的方向:在固定模型结构不变的前提下,用多模型融合或者自洽性校验。

先说自洽性校验。这个方法完全不改变模型,只是改变预测逻辑:对同一张图做多组轻微变换,比如平移1到2个像素、轻微旋转、加少量噪声,然后分别预测。如果5次预测结果一致,说明模型很确定;如果不一致,就取出现次数最多的结果。符号级准确率从95%推到97%以上,靠这个方法就够了。它的代价是推理时间变成原来的几倍,但对演示系统来说完全可接受。

再说多模型融合。用同一套数据集,但修改随机种子,训练三个结构相同但初始化不同的模型。预测时三个模型各自输出50维向量,加和后再取argmax。这个做法的原理是:不同初始化会让模型学到的特征略有差异,三个模型的“共识”会比单个模型更可靠。

# 多模型融合预测 models = [load_model(f'captcha_model_{i}.h5') for i in range(3)] def ensemble_predict(img_array): x = img_array.reshape(1, 60, 160, 1) pred_sum = np.zeros(50) for model in models: pred_sum += model.predict(x)[0] # 累加概率 result = "" for i in range(5): digit = np.argmax(pred_sum[i*10:(i+1)*10]) result += str(digit) return result

这三个模型可以用同样的训练集,也可以把训练集按不同随机种子的方式重新打乱。实测下来,融合模型在验证集上的表现比单品模型提升1到3个百分点,而且实现代价非常低——就是把训练脚本跑三遍,保存三个权重文件。

最后提醒一点:做实验记录时,同一张测试图片上的失败案例,建议单独建一个文件夹存下来。训练时最难处理的往往是特定字体和特定干扰线组合的样本,把失败样本可视化之后,你能总结出模型的“盲区”,写到论文里就是很有说服力的分析章节。我自己做这类项目时的习惯是——模型在训练集上表现优异时,会先去找一些跟训练集风格差异大的图片试试,实测效果远远好于盯着验证集准确率看很久。上面这些思路希望能帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询