ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于CNN+OpenCV的车牌识别系统设计与实现——毕设实战指南

基于CNN+OpenCV的车牌识别系统设计与实现——毕设实战指南 简介车牌识别是计算机视觉领域的经典工程场景常被选为课程设计与毕业设计题目。传统图像处理与深度学习各有局限纯OpenCV方法依赖手工特征对光照和角度敏感端到端模型则需大量标注数据和算力。工程实践往往采用混合架构——以OpenCV完成灰度化、滤波、二值化、轮廓定位与字符分割再将字符图像送入CNN分类器识别。这种设计兼顾鲁棒性与可调试性也便于在有限数据集上完成模型训练与参数调优。理解预处理管道、卷积网络结构、字符分割与推理流程能帮助快速搭建一套可运行的车牌识别系统并为后续图像识别类项目提供可复用的方法论。本文以毕设场景为切入点完整梳理了从图像输入到车牌号输出的全链路实现细节。1. 车牌识别系统CNNOpenCV这条链路为什么是毕设首选做毕设选车牌识别的人很多但真正能用起来、敢站到答辩台上讲清楚的不多。问题往往不在模型有多深而在整个链路里预处理、定位、分割、识别每一环都会出幺蛾子。这套基于CNNOpenCV的Python实现核心是把传统图像处理当作前置管道把CNN当作识别引擎训练好的模型对蓝底白字这类标准车牌效果很稳配合答辩PPT和报告文档属于那种“拿回去能改、改完能跑、跑完能答辩”的完整资源。适合正在做计算机视觉方向课设或毕设的同学也适合想快速跑通一个端到端识别流程、但不想从零开始搓轮子的从业者。2. 系统架构与模型选型为什么是OpenCV预处理CNN分类而不是端到端硬怼2.1 从传统图像处理到深度学习这套架构的分工逻辑车牌识别从技术演进上看有两条路线。一条是纯传统图像处理边缘检测、颜色分割、形态学操作全用OpenCV搞定好处是快、无需训练坏处是对光照、倾斜、遮挡极其敏感稍微换个拍摄角度识别率就崩。另一条是端到端深度学习输入图像直接进网络输出车牌号好处是鲁棒性强坏处是你得准备海量标注数据训练周期长而且毕设场景下GPU资源往往不宽裕。这套资源选了折中路线OpenCV负责定位和字符分割CNN负责分类。这个分工逻辑很实在——定位和分割是空间变换问题传统算法在规则场景下又快又准字符识别是模式分类问题CNN在这个任务上的泛化能力远超模板匹配。实践下来这种结构比纯端到端模型容易调试得多哪一步出问题可以直接在中间结果图上看到对答辩时的“你是怎么排查问题的”这类提问来说是个加分项。2.2 CNN模型结构设计卷积层、池化层和全连接层的取舍代码里的CNN模型不算深因为车牌字符类别就那么多汉字、字母、数字加起来几十类不需要ResNet那种上百层的容量。常见做法是三层卷积两层全连接import torch.nn as nn class LicensePlateCNN(nn.Module): def __init__(self, num_classes): super(LicensePlateCNN, self).__init__() self.conv_layers nn.Sequential( nn.Conv2d(1, 32, kernel_size3, padding1), # 输入单通道灰度图 nn.ReLU(), nn.MaxPool2d(2), # 28x28 - 14x14 nn.Conv2d(32, 64, kernel_size3, padding1), nn.ReLU(), nn.MaxPool2d(2), # 14x14 - 7x7 nn.Conv2d(64, 128, kernel_size3, padding1), nn.ReLU(), nn.MaxPool2d(2) # 7x7 - 3x3 ) self.fc_layers nn.Sequential( nn.Flatten(), nn.Linear(128 * 3 * 3, 256), nn.ReLU(), nn.Dropout(0.5), # 防过拟合训练时随机失活 nn.Linear(256, num_classes) ) def forward(self, x): return self.fc_layers(self.conv_layers(x))这里的参数有几个值得注意的地方。输入用单通道灰度图而不是三通道彩色图因为车牌字符识别对颜色不敏感灰度化能显著降低计算量训练速度也更快。第一层卷积输出32个特征图第二层64第三层128逐层翻倍是CNN的常规设计思路——浅层学边缘和纹理深层学字符的部件级特征。Dropout层放在全连接层中间目的是防止模型对训练集过拟合毕设数据集通常只有几百张图不加Dropout的话验证集准确率很容易在某个epoch之后反而往下掉。2.3 OpenCV预处理管道灰度化、去噪、二值化的顺序和参数预处理管道是这套系统的地基。你可能会想直接用原始RGB图喂给CNN不就行了实际上OpenCV的预处理起着两个作用一是在定位阶段提升车牌区域的对比度二是给CNN提供更干净的字符输入。顺序一般固定为灰度化 → 高斯滤波去噪 → 自适应二值化 → 形态学操作。import cv2 import numpy as np def preprocess_image(image_path): # 读入图片并缩放太大影响定位速度太小丢失细节 img cv2.imread(image_path) img cv2.resize(img, None, fx0.5, fy0.5, interpolationcv2.INTER_CUBIC) # 第一步灰度化把三通道压缩成单通道 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 第二步高斯滤波去噪核大小取5x5sigma取0由函数自动算 blurred cv2.GaussianBlur(gray, (5, 5), 0) # 第三步自适应阈值二值化blockSize取31C取5 binary cv2.adaptiveThreshold( blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 5 ) # 第四步形态学闭运算先膨胀后腐蚀连回断裂的字符笔画 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) morphed cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) return morphed高斯滤波的核大小直接影响去噪效果和边缘保留的平衡。核太大会把字符边缘也抹掉核太小等于没滤。5x5是实测下来对车载摄像头噪声比较合适的值。自适应阈值比全局阈值在光照不均场景下稳得多它按局部邻域计算阈值每个像素的阈值都不一样。blockSize参数表示邻域大小31的意思是看周围31x31像素范围C是常数项从阈值里减掉增大C会让二值化结果更暗更“挑剔”。形态学闭运算主要解决字符笔画断裂的问题——车牌字符被污渍或反光影响时膨胀能连上断点再腐蚀还原轮廓。2.4 车牌定位轮廓检测与几何筛选的实战参数车牌定位是整体准确率的最大变量。CNN再好车牌框找错了也白搭。基于OpenCV的经典做法是找轮廓、筛轮廓利用车牌的宽高比和面积占比作为硬性条件。def locate_plate(binary_img): # 提取轮廓 contours, _ cv2.findContours( binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) candidates [] for cnt in contours: x, y, w, h cv2.boundingRect(cnt) # 宽高比标准车牌约3:1放宽到2:1到4.5:1 aspect_ratio w / h if aspect_ratio 2.0 or aspect_ratio 4.5: continue # 面积占比车牌在整张图中通常占1%到10% area_ratio (w * h) / (binary_img.shape[0] * binary_img.shape[1]) if area_ratio 0.01 or area_ratio 0.10: continue candidates.append((x, y, w, h)) # 按面积排序取最大的作为车牌区域 if not candidates: return None return max(candidates, keylambda rect: rect[2] * rect[3])RETR_EXTERNAL只提取最外层轮廓这样能避开字符内部的孔洞轮廓。CHAIN_APPROX_SIMPLE压缩水平、垂直和对角方向的线段减少存储点数。宽高比是筛选条件里最硬的一条——蓝底白字车牌长宽比在3.14左右2.0到4.5的区间足够容纳拍摄角度带来的轻微透视变形。面积占比的下限设1%低于这个值的噪声区域直接丢弃上限10%避免把车头大块区域误判成车牌。3. 从图像到车牌号字符分割与CNN推理全流程复现3.1 字符分割投影法与连通域方法的取舍拿到车牌区域后下一步是把“京A12345”这样的整体图像切成单个字符。如果直接在整张车牌上跑CNN分类输出的维度会爆炸——你不知道一个车牌上有几个字符而且字符间距不固定。所以必须先把字符位置找出来。两种主流做法投影法和连通域分析法。投影法的思路是把二值图像在垂直方向上做像素统计统计值大的列就是字符区域统计值接近零的列就是字符间隙。优点是对标准字符间距的车牌效果好缺点是遇到铆钉、边框干扰时容易把噪声也当成分割边界。连通域法更好理解——把二值图中连在一起的白色像素块找出来每个块就是一个候选字符。这套资源走的是投影法加的连通域校验两者结合比单用一种稳。def segment_characters(plate_img): # 先转为灰度并二值化确保输入干净 gray cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 垂直投影统计每一列的白色像素数 col_sum np.sum(binary, axis0) # 找到字符起始和结束位置 in_char False char_regions [] start 0 for i, val in enumerate(col_sum): if val 0 and not in_char: in_char True start i elif val 0 and in_char: in_char False end i # 宽度小于5个像素的视为噪声 if end - start 5: char_regions.append((start, end)) # 按位置从左到右排序 char_regions.sort(keylambda r: r[0]) return char_regions这段代码的关键逻辑在阈值选择上。0和THRESH_OTSU连用是一种高级技巧——threshold函数会基于图像直方图自动计算最优阈值替代硬编码的固定值。不同照片的明暗条件千差万别固定阈值在暗光照片上会把字符糊成一片Otsu在大多数场景下能自动找到区分前景和背景的分界点。分割结果需要警惕两个问题汉字可能被切成两半因为“京”字的左右结构在投影上可能出现中间断开的假象数字“1”和字母“I”太窄可能被当作噪声滤掉。实践中遇到第一种情况常见做法是检查相邻字符间隔如果间隔明显小于其他字符间距就合并这两个区域遇到第二种情况把最小宽度阈值从5降到3但这样也会引入更多虚假区域需要后续用CNN的置信度来做二次过滤。3.2 CNN推理把字符图像转成模型输入张量字符区域定好后每张字符图要经过与训练时完全相同的预处理才能进模型。很多新手在这一点上翻车——训练时做了归一化推理时忘了做训练时图像尺寸是32x32推理时直接resize成任意尺寸。这些都会导致推理结果完全不可用。import torch from torchvision import transforms # 推理时的变换顺序必须与训练时一致这是原则性问题 inference_transform transforms.Compose([ transforms.ToPILImage(), transforms.Resize((32, 32)), transforms.ToTensor(), transforms.Normalize(mean[0.5], std[0.5]) ]) def predict_char(char_image, model, device): # char_image是numpy数组格式的单通道二值图 tensor inference_transform(char_image).unsqueeze(0) tensor tensor.to(device) model.eval() # 关键切换到评估模式关闭Dropout with torch.no_grad(): outputs model(tensor) probs torch.softmax(outputs, dim1) confidence, pred torch.max(probs, dim1) return pred.item(), confidence.item()model.eval()这行很多人会漏掉。PyTorch默认是训练模式Dropout层会随机丢弃神经元同一个输入每次过模型结果都不一样。推理时如果忘了切到eval模式你会在测试集上看到一种诡异的现象同一张图识别两次结果不一样。这个坑的血泪经验后面避坑章节还会再提。transform的Normalize用了mean0.5和std0.5对应把像素从[0,1]区间映射到[-1,1]区间。你可能会问为什么要做这一步最直接的解释是CNN训练时权重是在这个分布下收敛的推理时输入分布不一致权重就没法正常工作。3.3 端到端整合主流程代码与坐标可视化校验把前面几块串在一起整个系统的调用链路很清晰读图 → 预处理 → 定位 → 分割 → 逐字符识别 → 拼接结果。def recognize_license_plate(image_path, model, device): # 1. 预处理 morphed preprocess_image(image_path) # 2. 定位车牌区域 plate_rect locate_plate(morphed) if plate_rect is None: return None, None, 未检测到车牌区域 x, y, w, h plate_rect original cv2.imread(image_path) plate_img original[y:yh, x:xw] # 3. 字符分割 char_regions segment_characters(plate_img) # 4. 逐字符识别 result confidences [] for start, end in char_regions: char_img plate_img[:, start:end] pred, conf predict_char(char_img, model, device) result str(pred) confidences.append(conf) # 5. 在原始图上画矩形框并标注结果方便直观检查 cv2.rectangle(original, (x, y), (xw, yh), (0, 255, 0), 2) cv2.putText(original, result, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) return original, result, min(confidences)这里最值得说的是最后一步的可视化输出。调试车牌识别系统时如果只输出一个字符串出错了你根本不知道错在哪一步——是定位框偏了、分割切错了还是CNN分类错了。把矩形框和识别结果画回原图一眼就能定位问题环节。这是我在调试视觉系统时的习惯做法永远让中间结果可见不要只输出最终结果。4. 训练与调参让CNN在自己的数据上收敛的关键配置4.1 数据集组织与标签编码从文件夹结构到One-Hot向量CNN训练前要做的最基础的事情是数据整理。字符图片按类别放文件夹文件夹名就是类别名这是最不容易出错的方案。每个类别的图片数量要尽量均衡——如果“京”字有800张而字母“Z”只有50张模型会严重偏向样本多的类别。from torch.utils.data import Dataset from PIL import Image import os class CharDataset(Dataset): def __init__(self, root_dir, transformNone): self.classes sorted(os.listdir(root_dir)) self.class_to_idx {cls: i for i, cls in enumerate(self.classes)} self.samples [] # 遍历每个类别文件夹收集图片路径和标签 for cls in self.classes: cls_dir os.path.join(root_dir, cls) for fname in os.listdir(cls_dir): if fname.lower().endswith((.png, .jpg, .jpeg)): path os.path.join(cls_dir, fname) label self.class_to_idx[cls] self.samples.append((path, label)) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] img Image.open(path).convert(L) # 统一转灰度 if self.transform: img self.transform(img) return img, label标签编码方式这里没有直接做One-Hot而是用整数索引class_to_idx然后交给PyTorch的CrossEntropyLoss处理。CrossEntropyLoss内部会自动做one-hot形式的目标分布计算手工做One-Hot反而多此一举还容易出错。Class_to_idx用sorted排序而不是直接用os.listdir的原始顺序这是为了让类别索引在不同操作系统下保持一致——Windows和Linux的文件系统返回顺序不一样不排序的话同一个类可能在两台机器上对应不同索引训练好的模型换台机器推理就全错了。4.2 训练超参数学习率、批量大小、epoch与优化器选择训练阶段有四个参数直接决定模型能不能收敛学习率、batch size、epoch数和优化器。资源代码里的默认值一般是学习率0.001、batch size 32、epoch 30、Adam优化器。我给个实测观察你根据自己的显卡显存来调。import torch.optim as optim from torch.utils.data import DataLoader # 训练配置 num_epochs 30 batch_size 32 learning_rate 0.001 device torch.device(cuda if torch.cuda.is_available() else cpu) model LicensePlateCNN(num_classeslen(dataset.classes)).to(device) # Adam的默认参数betas(0.9, 0.999)足够大多数场景 optimizer optim.Adam(model.parameters(), lrlearning_rate) criterion nn.CrossEntropyLoss() train_loader DataLoader(dataset, batch_sizebatch_size, shuffleTrue, num_workers2) for epoch in range(num_epochs): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() # 梯度裁剪防止loss突变成NaN torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() running_loss loss.item() avg_loss running_loss / len(train_loader) print(fEpoch [{epoch1}/{num_epochs}], Loss: {avg_loss:.4f})学习率0.001是Adam优化器的经验安全值高于这个值loss容易震荡低于这个值收敛太慢。每轮训练打一次平均loss如果看到loss在某个epoch后不再下降说明模型容量到了上限要么加卷积层要么加数据量改学习率基本没用。梯度裁剪很多人不知道但对这个小模型很重要。车牌字符图片里偶尔有极端噪声样本一次异常梯度就能让权重飞到天上去loss变成NaN再也回不来。clip_grad_norm_把梯度范数截断到1.0相当于给反向传播加了安全阀后续所有epoch的loss曲线都会平稳很多。4.3 验证与混淆矩阵准确率之外还要看哪些指标训练集准确率98%不代表测试集也能到98%所以数据要划出独立的验证集常见比例是8:2或者9:1。验证集绝对不能参与训练这是机器学习的铁律。资源里的报告模板大概率会要求你贴准确率曲线但答辩时老师更可能问的问题是哪几类字符最容易混淆from sklearn.metrics import confusion_matrix import matplotlib.pyplot as plt import numpy as np def evaluate_model(model, val_loader, class_names, device): model.eval() all_preds [] all_labels [] with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) # 计算混淆矩阵行是真实标签列是预测标签 cm confusion_matrix(all_labels, all_preds) acc np.mean(np.array(all_preds) np.array(all_labels)) # 打印被错分最多的类别组合 print(fOverall Accuracy: {acc:.4f}) for i in range(len(class_names)): for j in range(len(class_names)): if i ! j and cm[i][j] 0: print(f{class_names[i]} - {class_names[j]}: {cm[i][j]}次) # 画混淆矩阵热力图答辩PPT里可以直接用 fig, ax plt.subplots(figsize(10, 8)) im ax.imshow(cm, cmapBlues) ax.set_xticks(range(len(class_names))) ax.set_yticks(range(len(class_names))) ax.set_xticklabels(class_names, rotation45) ax.set_yticklabels(class_names) for i in range(len(class_names)): for j in range(len(class_names)): ax.text(j, i, cm[i][j], hacenter, vacenter) plt.savefig(confusion_matrix.png, dpi150, bbox_inchestight)车牌字符识别里最容易混淆的几组是“0”和“O”、“1”和“I”、“8”和“B”。汉字里“京”和“津”在分辨率低的时候也容易搞混。这些对拍照片的人交警系统可能不算大问题但对你答辩来说能主动说出“模型在哪类字符上表现弱、为什么弱、怎么改进”比单报一个98%的准确率有说服力得多。4.4 训练时常见翻车记录loss不降、验证集准确率抖动loss不降的最常见原因是学习率太大或数据没归一化。肉眼检查方法把第一个batch的图片打印出来看确认像素值范围是[-1,1]还是[0,255]。如果发现是[0,255]Normalize的mean和std设置铁定出问题了。验证集准确率抖动大常见原因是batch size太小比如4或8梯度的方向噪声太大增大到32或64一般会好一些。验证集准确率比训练集低一大截这说明模型过拟合了。毕设场景下数据量通常只有几百到一两千张过拟合几乎必然发生。对策按优先级排先加Dropout比例从0.5提到0.7再做数据增强比如随机平移几个像素、轻微旋转、加椒盐噪声最后才是考虑换更小的模型结构。5. 避坑与排查车牌识别项目最常见的五个翻车点5.1 现象OpenCV装好了但import cv2报ModuleNotFoundError原因Python环境中存在多个虚拟环境或Anaconda基础环境里的OpenCV装到了site-packages但当前解释器指向的是另一个环境。很多人直接在系统Python里pip install opencv-python然后又建了conda环境跑代码两个环境的site-packages目录互不相通。解决先用which python和which pip确认当前命令指向的解释器路径再用同一个python解释器执行python -m pip install opencv-python。我自己的习惯是项目一创建就建独立虚拟环境所有依赖装在里面不让它污染系统环境。5.2 现象cv2.error: OpenCV(4.4.0)报错常见于读取视频或摄像头帧原因OpenCV在不同版本里对某些API的参数做了调整4.x版本里VideoCapture的某些属性设置和3.x不兼容。另一类常见报错是imread返回None路径里带了中文字符OpenCV的imread在Windows上对中文路径支持不好。解决路径保持纯英文图片放到images/这类简单目录下。读图后先加一行判断img cv2.imread(path) if img is None: raise ValueError(f无法读取图片: {path}请检查路径和文件名)这行代码能省掉大量排查时间至少能快速排除是路径问题还是算法问题。5.3 现象训练后loss下降正常但每个epoch跑完验证集准确率都在30%上下徘徊原因标签和类别索引没对齐。我在一个项目里遇到过这种情况——训练数据文件夹按字母排序验证集却按字典序重新排了一遍两个索引映射就错位了。更隐蔽的情况是数据增强过度随机旋转角度设成了180度字符都倒过来了。解决训练脚本里用统一的class_to_idx不要在训练和验证阶段各自生成。数据增强的旋转角度控制在±10度以内不要为追求“增强效果”把参数拉得过猛。先关掉数据增强跑一遍看baseline再逐步加回来这样能确认每一步对准确率的真实贡献。5.4 现象推理时同一张图片每次运行识别结果都不同原因模型没有调用model.eval()Dropout层在推理时仍然随机丢弃神经元。PyTorch默认模型在train模式即便你只是做前向传播Dropout也会产生随机性。解决推理前显式调用model.eval()并用with torch.no_grad():包裹前向过程。net.eval()这行代码写进predict函数里不要放在主流程里调用一次就以为万事大吉——如果代码里的某条分支在val模式下又触发了train()随机性就回来了。5.5 现象车牌定位框把车灯、反光条也框进来或者完全定位不到原因纯颜色分割在蓝色车牌上准确率还行但在黄昏、夜间或车身贴了深色膜的场景下车牌的蓝色特征会严重偏移。更常见的坑是轮廓筛选条件太窄某些角度下车牌宽高比偏离3:1面积占比也跌破1%被过滤条件直接判死。解决把定位阶段的中间结果画出来检查——找一张模板图片把findContours找到的所有候选轮廓全部画在原图上看是候选太少还是筛选条件太严格。宽度比区间放到1.5到5.0面积占比下限放到0.5%多放几个候选进去再用CNN做最终确认。车牌定位这事宁可多框不可漏框后续还可以用字符数来做二次校验——一个车牌应该切出7个或8个字符少于这个数就说明定位或分割有问题。6. 进阶用法数据增强与模型集成把准确率再往上推一截车牌识别项目做到能跑通、能答辩已经达到基本要求。但如果想让系统在更真实的场景下也站得住有两个投入产出比很高的方向数据增强和模型集成。数据增强是应对数据量不足的第一手段。车牌字符图片的增强策略跟ImageNet那套不完全一样字符识别对旋转和缩放最敏感但对颜色变化不敏感——因为字符本来就是黑白二值图。我常用的增强组合是±10度旋转、水平或垂直平移2个像素、0.9到1.1倍缩放。这些变换模拟的是拍摄角度轻微变化和车辆轻微移动的效果。要用PyTorch的transforms在训练时动态做不要提前生成一万张增强图片存硬盘——训练时实时做省存储还相当于每个epoch看到的是不同的数据模型不容易背下来训练集。另一种有效的增强是随机遮挡。车牌上常有泥点、反光斑训练时在字符图中间随机抹掉一个小方块模型被迫学会从残缺字符中提取特征。这个思路跟Cutout正则化一脉相承实测对车牌识别这类局部特征任务提升很明显。缺点是不好解释给答辩老师听如果导师比较传统建议把随机遮挡说成“模拟车牌污损场景的数据增强手段”。模型集成就更直接了——训练三个结构略有差异的CNN比如一个三层卷积、一个四层卷积、一个加宽卷积通道的变体推理时三个模型投票决定最终字符。单个模型在某个字符上可能犯错三个模型同时犯同样错误的概率要低得多。代价是推理时间变成三倍但毕设演示时一张图跑200毫秒和跑600毫秒肉眼根本分辨不出来。集成用的三个模型不要从同一个随机种子训练出来——那样它们学到的东西几乎一模一样集成没有意义。分别用42、2024、7这三个种子初始化才能保证模型之间的差异性。最后多提一句验证方法。不管你怎么折腾最终都要回到一个朴素的习惯上建一个固定的验证集里面放二十张带有不同场景特征的车牌图——白天、傍晚、倾斜角度大、带泥点、汉字笔画复杂。每次改动完代码或训练完新模型跑一遍这个验证集把准确率和错例截图存档。我从那以后每次改完预处理参数都强制走一遍这个流程哪怕只改了高斯滤波的核大小也要把二十张图全过一遍。这样调参才有方向答辩被问到“你这个参数是怎么定的”时你能拿出对比数据而不是凭感觉。希望帮到你。本文还有配套的精品资源点击获取
返回列表