从零搭一个能用的智慧识花系统:设计思路、训练细节和那些坑
我今年花了不少时间做了一个基于深度学习的智慧识花系统,从数据集准备、模型训练、后端接口到前端展示全流程走了一遍。这篇东西不是来秀架构图的,而是把我踩过的坑、想明白的道理、以及最终能跑通的方案一步步写出来。不管你是拿它做毕业设计、课设,还是纯粹想做一个能识别花的小工具,都可以参考这套思路。
先把这个系统是什么说清楚:你拍一张花的照片传上去,系统告诉你这是什么花、大概置信度多少、还能展示一些基本信息。后端用卷积神经网络做图像分类,前端支持单张图片上传和实时预览,整套代码不是玩具,是能够容纳几百个类别、具备工程化改造空间的项目。
适合谁来读?如果你刚入门深度学习,想找一个图像分类方向练手,这篇文章能帮你建立完整的项目视角;如果你是正在做毕业设计,需要写系统设计和万字论文,后面关于报告结构的章节可以直接套用;哪怕你只是好奇"识花是怎么实现的",看完也能明白核心原理,不会被花里胡哨的词汇绕晕。
1. 整体设计思路拆解
1.1 核心需求解析:识花系统到底要解决什么问题
做任何一个项目,第一步不是选技术,而是把需求拆干净。智慧识花系统表面上就一个动作——"拍照,告诉我是啥花",但拆开来看有几个隐性的要求。
第一是识别的准确性。花和花之间差别有时候特别小,比如两三种菊花放在一起,颜色、花瓣形态都很接近,人眼都要凑近看,模型要是分错,用户立刻觉得这系统不行。第二是响应速度。你不可能让用户对着一张照片等十秒钟才出结果,识别接口需要控制在几百毫秒到一两秒的量级。第三是易用性。上传、识别、展示结果,三步以内要完成,前端不能堆太多操作逻辑。第四是扩展性。今天能识别50种花,明天可能要加到200种、500种,系统的结构不能一加数据就推倒重来。
这些需求推导到技术选型上就非常清晰:图像分类任务必然用深度卷积神经网络;要在速度和精度之间平衡,不能一味追求最大的模型;类别的添加依赖于数据集的扩充,所以数据管线必须规范;接口设计要尽量解耦,前后端分离,模型只是其中一个可替换的模块。
我当时给自己定的目标是:完成一个不低于80类常见花卉的识别系统,测试集准确率90%以上,单次推理时间在500毫秒左右。这个目标放在今天的技术条件下并不夸张,迁移学习模型很容易达到,但它能给你一条非常明确的主线,所有后续工作都围绕它展开。
1.2 技术选型考量:为什么是深度学习,而不是传统图像识别
这里多说几句。遇到识花需求,第一反应是先用颜色直方图、花瓣轮廓、纹理特征这些传统方法。我一开始也试着用OpenCV提取HSV颜色特征加SVM分类器做过一个原型,结果很有意思:纯色背景的玫瑰花照片识别效果还行,一换到真实场景——背景有草有树、光线忽明忽暗、花只占画面三分之一——效果就崩了。
原因很简单,花卉识别的难点本来就不在"花长得不一样",而在"背景和环境的干扰太强"。传统方法靠人工设计特征,本质上是人先总结规律,再让程序去匹配,你能想到的规则有限,照片里的变化是无限的。深度学习把这个问题变成了"自动找特征",卷积神经网络从大量数据中自己学会哪些模式是有区分度的。
举一个生活中容易理解的例子。你让一个小孩子认识苹果,不是说"红色圆形就是苹果",而是带TA见过各种苹果:红富士、青苹果、磕碰过的、带叶子的、切开半个的。见多了之后,TA心里自然形成了一个"苹果的感觉",以后哪怕在昏暗灯光下看到一个不太红的苹果也能认出来。卷积神经网络做的事情就是把这个"见过很多次"的过程变成可计算的数学优化。
当然不是说传统方法一无是处,数据量很小、场景高度可控的时候,它反而更轻快。但识花系统的场景天然是开放和复杂的,所以核心识别模块选定为深度学习图像分类,这是整个项目最关键的一个决策。
1.3 系统架构分层:从数据集到用户界面的完整链路
动手写代码之前,我先把整个系统分成四个层次,这个结构也推荐给所有做类似项目的人。
数据层负责图像数据的采集、清洗、增强和划分。这一层决定模型的天花板,后面所有环节做的再好,数据垃圾,结果就是垃圾。
模型层负责加载预训练权重、训练、微调、验证、导出模型文件。这一层的核心是迁移学习,不是从头训练一个网络。
服务层负责把训练好的模型包装成可用的接口。具体来说用推理脚本加载模型权重,对上传的图片做预处理,然后返回类别和置信度。这个层可以做成Flask接口,也可以直接做成命令行工具,取决于项目形态。
应用层负责用户交互,包括前端网页上传图片、展示识别结果、花名、置信度、百科信息等等。我做的是Web页面,上传框加结果展示区,逻辑非常简单,但对完整性和演示效果来说很重要。
这样分层以后,每一层的职责单一,出了任何问题都能快速定位——准确率不高去看数据层和模型层,接口报错去看服务层,页面不显示结果去看应用层。很多初学者喜欢把所有代码揉在一个文件里,改一个地方牵着全身动,项目规模小还能忍,一旦数据量上来、训练次数多起来,一定是灾难。
2. 核心细节解析与实操要点
2.1 数据集构建:一张图都不能马虎
数据集是整个项目的命脉。我在这上面花的时间占到了总工期的百分之四十以上,训练本身反而快得多。很多人失败的原因不是模型不够好,而是数据集太随意。
第一步是确定类别和数量。我从中国常见的观赏花卉和园林花卉里选了120类,每类初始收集200到300张图片。这个规模不是拍脑袋定的:类别太少撑不起"系统感",类别太多数据收集成本剧增,而且会引入大量相似属的混淆问题。这个量级对一次认真的项目来说正好。
第二步是数据来源。公开的花卉数据集(比如Oxford 102 Flowers)质量高,但只有102类且以欧美常见花卉为主,不够用。我的做法是组合来源:公开数据集的子集做底子,再补充网络图片和实拍图片。补充图片用脚本批量下载,然后花大量时间人工筛掉模糊、带水印、主体太小、明显标注错误的图。这个筛选过程极度枯燥,我通常会写一个简单的预处理工具,先剔除尺寸过小、清晰度评分过低的图片,再人工二次过目。
第三步是统一格式。所有图像统一为RGB三通道,尺寸在进入网络前统一缩放到224x224。图片不要求本体是方的,缩放策略是等比缩放后居中裁剪,避免直接拉伸导致花朵变形。举个例子,一张1200x800的横版照片,先缩放成边长约256左右的图,再在中心取224x224的方形区域。这样做的好处是保留花瓣的尽可能多信息,而不是把整张图压成容易变形的比例。
数据增强是另一个必须做的环节。原始样本没有那么多,而且网络对翻转、微小的色彩变化天然应该具备鲁棒性,所以训练时动态做增强:随机水平翻转、随机旋转正负15度、随机亮度对比度扰动、随机裁剪一小块再缩放回去。增强不是把图片变花哨,而是让模型见过更多"同类但不一样"的照片,从而学到本质特征而不是死记硬背。
我自己做的一个小工具函数,用来统计每个类别的图片数量。整个数据集不是一次到位的,先训练一轮,发现哪些类别准确率低,再去针对性补数据,这个迭代思路在后面排查环节还会展开讲。
2.2 模型选择:ResNet、MobileNet还是EfficientNet
现在做图像分类,基本没人从零搭一个VGG或者ResNet从头训,都是加载预训练权重做迁移学习。但预训练模型选哪个,要结合自己的硬件条件和场景来定,我把自己实测对比过的三个候选列出来。
ResNet系列是经典选择。ResNet50是很多教学项目的主角,结构稳定、社区资料多、任何奇怪的报错都能搜到答案,对初学者极度友好。但它在CPU上推理偏慢,GPU上训练优势明显,模型文件大概一百多兆,部署到服务器没问题,打包成小程序或App就偏大了。
MobileNet系列主打轻量。MobileNetV3或者MobileNetV2做成分类器,模型文件只有十几兆到几十兆,CPU推理速度很快,适合最终要部署到移动端或者低配服务器的场景。它的准确率比ResNet略低,但在迁移学习的加持下差距不大,我的实测在120类花卉上大概差一到两个百分点。
EfficientNet系列是效率和精度的平衡点。EfficientNetB0的参数量比ResNet50小,准确率却往往更高,是性能和资源兼顾的选择。缺点是某些版本的训练细节更敏感,比如输入分辨率和正则化策略需要微调,对新手不够友好。
我做项目时选的是EfficientNetB0做主力实验,同时用MobileNetV3做对比,最后根据部署目标决定用哪一个。很多同学喜欢在模型选择上花大量时间反复对比,我的建议是不要陷入选择困难,先选一个ResNet50从数据前处理到训练代码全部跑通,有了完整pipeline之后,换模型只是改不到十行代码的事情。
两个实测数据供参考:在同样的120类、每类250张图片、20个epoch训练条件下,ResNet50在验证集上准确率大约91.2%,EfficientNetB0大约92.8%,MobileNetV3大约90.4%。推理时间在普通GPU上分别是约380ms、约240ms、约120ms(含预处理)。数字因平台而异,但相对关系是稳定的。
2.3 训练策略:迁移学习的正确打开方式
迁移学习的核心思路不是"从零学",而是"站在巨人肩膀上"。预训练模型已经在ImageNet这种千万级图片上学过通用的视觉特征——边缘、纹理、形状、物体部件——我们要做的是让它适应花卉这个小领域。
关键操作是分层训练。预训练模型一般分成特征提取部分(前面的卷积层)和分类头(最后的全连接层)。标准做法是先把分类头替换成自己定制的新结构——因为ImageNet是1000类,我们只有120类——然后冻结所有特征提取层的权重,只训练新的分类头。这个阶段的训练很快,几个epoch就能达到一个不算差的起点。之后再视情况解冻部分高层卷积层,用更小的学习率微调。这样安排的原因是:底层特征非常通用,不需要大改;高层特征更接近原任务的语义层面,需要更多适应。
具体参数我采用的是:优化器Adam,初始学习率冻结阶段设置3e-4,微调阶段降到1e-4或者5e-5;batch size选32(显存不够就16,效果差距不大);训练总轮数20到30个epoch;学习率用余弦退火或分段下降都可以。早停法一定要开,监控验证集损失,连续若干个epoch不降就停止,避免过拟合。这个方法帮我省了大量无意义的等待时间。
提示:训练过程中不要只看训练集准确率。训练集准确率接近100%而验证集只有85%,这就是典型的过拟合信号。真正的监控指标是验证集loss和精度。
损失函数就是普通的交叉熵,类别多、样本还算均衡的情况下没必要上focal loss之类的高级货。但如果你的类别样本量差异很大,比如某类有500张、某类只有50张,可以考虑在交叉熵里加类别权重,把样本少的类别权重调高一点,让模型更重视它们。这个"重视"不是玄学,是数学上通过调节梯度贡献实现的。
3. 实操过程与核心环节实现
3.1 环境准备清单与工具链选择
我用的环境是Ubuntu 22.04,Python 3.10,PyTorch 2.x,GPU是一张中端显卡,显存12G。如果你的电脑没有NVIDIA显卡,纯CPU也能完成训练,只是时间会多几倍,建议把图片尺寸调小、模型选MobileNet、epoch减少,先跑通流程再考虑完整训练。
依赖库非常标准:
pip install torch torchvision opencv-python pillow numpy flask如果训练需要自动监控和日志记录,再安装tensorboard或者wandb,但最基础的训练代码用print和csv日志就足够了。不要一上来就堆一堆工具,工程里最朴素的方案往往最稳定。
3.2 数据处理流程可以这样写
核心是把图片从文件夹读取、做增强、变成张量这三个动作封装好。文件夹结构是datasets/train/类名/图片.jpg,验证集类似,这个规范让增加新类别变得很简单——新建文件夹放图片就行。
用PyTorch自带的ImageFolder读取数据,搭配torchvision的transforms做预处理和增强,代码大概长这样:
from torchvision import transforms from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder train_transforms = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.2, contrast=0.2), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) train_dataset = ImageFolder('datasets/train', transform=train_transforms) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4)Normalize那一行的mean和std是ImageNet统计出来的固定值,不要自己算。这个标准化操作和预训练权重是对应的,改掉会导致模型效果明显变差。
3.3 模型训练关键代码与参数调优日志
训练主循环其实很朴素,但有一个细节值得单独说明:加载预训练权重的正确姿势。以EfficientNetB0为例,不要手动下载权重文件换成自己的随机初始化,直接加载官方预训练权重到特征层,然后覆盖掉分类头。
import torch import torch.nn as nn import torch.optim as optim from torchvision import models model = models.efficientnet_b0(weights=models.EfficientNetB0_Weights.IMAGENET1K_V1) num_features = model.classifier[1].in_features model.classifier = nn.Sequential( nn.Dropout(p=0.2), nn.Linear(num_features, num_classes) ) # 冻结特征层 for param in model.features.parameters(): param.requires_grad = False optimizer = optim.Adam(model.classifier.parameters(), lr=3e-4) criterion = nn.CrossEntropyLoss()训练脚本我习惯写成函数式结构:train_one_epoch(epoch)、validate(epoch)、save_checkpoint。每个epoch结束都算一下验证集准确率,记录到一个csv里,方便后续画曲线和分析。下面是我某一个关键版本实验的日志节选,可以直接感受训练过程的变化:
Epoch TrainLoss TrainAcc ValLoss ValAcc 1 1.8321 56.34% 1.4210 72.18% 5 0.7214 78.92% 0.5823 83.45% 10 0.3822 89.07% 0.4021 89.12% 15 0.2135 94.83% 0.3038 91.07% 20 0.1420 97.21% 0.2786 91.86% 25 0.0983 98.54% 0.2654 92.24%观察这个日志你会发现一个非常典型的现象:训练集准确率一路上涨,但验证集准确率在某个epoch之后几乎持平甚至下降。这就是模型开始过拟合的信号。我一般在第15到20个epoch之间保存效果最好的那一版,而不是最后一次迭代的权重。
解冻微调的代码也顺便给出:
for param in model.features.parameters(): param.requires_grad = True optimizer = optim.Adam(model.parameters(), lr=1e-5)这个阶段只跑三四个epoch,学习率要小,防止把预训练学到的好的底层特征破坏掉。
3.4 服务端部署与接口实现
模型训练完之后,部署环节需要做一个取舍:不可能在服务端加载整个训练框架跑推理,所以要把模型导出成轻量的推理格式或直接在Flask里加载权重文件,但确保只在启动时加载一次,不要每次请求都重新导入模型。
我实现的是FastAPI接口,逻辑是接收上传的图片文件,做和训练时一致的预处理,然后用模型推理得到类别索引和置信度。
from fastapi import FastAPI, UploadFile from PIL import Image import torchvision.transforms as transforms app = FastAPI() model = load_model("best_model.pth") class_names = load_class_names("class_names.txt") @app.post("/predict") async def predict(file: UploadFile): image = Image.open(file.file).convert("RGB") tensor = val_transforms(image).unsqueeze(0) with torch.no_grad(): outputs = model(tensor) probs = torch.softmax(outputs, dim=1) conf, idx = torch.max(probs, dim=1) result = { "flower": class_names[idx.item()], "confidence": round(conf.item(), 4) } return result注意convert("RGB")这一步。很多用户上传的手机照片是RGBA或者带EXIF方向信息的,不转RGB会导致通道数对不上,不做方向校正会导致图片旋转。这些细节在实际测试中一定会遇到,提前处理比等报错再解决舒服得多。
前端页面我用了最简单的HTML加原生JavaScript,一个文件上传框、一个预览区、一个结果展示卡。这个形态适合课设演示,也适合快速验证系统效果。不一定要用Vue、React之类的框架,功能简单时越少依赖越稳定。
3.5 万字设计报告怎么写才有干货
如果你做这个项目是为了毕业设计,那报告结构和写代码同样重要。我见过太多人代码写得很溜,但报告全是复制粘贴的废话,最后答辩被问住。
合理的报告结构大致是:绪论(背景与意义、国内外研究现状、主要工作)、相关技术介绍(深度学习基础、卷积神经网络结构、迁移学习原理、常用模型对比)、系统需求分析(功能需求、性能需求、可行性分析)、系统设计(总体架构、数据集构建方案、模型设计、功能模块设计)、系统实现(环境搭建、数据预处理实现、模型训练实现、接口与界面实现)、系统测试(测试环境、功能测试用例、性能测试结果、准确率对比分析)、总结与展望。
这里最容易被老师挑毛病的是"国内外研究现状"和"测试分析"。研究现状别只写"深度学习发展很快"这种话,要把具体方法分类列出来:基于传统特征的方法、基于通用CNN的方法、基于注意力机制的方法等等。测试分析必须有数据支撑,比如不同模型的准确率对比表、不同训练策略的对比实验、不同光照条件下的识别效果分析。这些都是你真实做过的东西,写出来的自然有说服力。
我的做法是在项目开发过程中随手记录实验表格和结果截图,最后写报告时把这些素材组装起来,比自己凭空编造效率高得多,内容也更扎实。
4. 常见问题与排查技巧实录
4.1 训练准确率上不去的定位思路
遇到准确率低,先不要怀疑模型结构,绝大多数问题出在数据处理和训练配置上。我整理了一个定位顺序:先看训练集准确率,再对比验证集准确率。
如果两者都低,说明模型容量不够或者学习率有问题。试试更大的模型、更长时间训练、更合适的学习率。如果训练集高、验证集低,说明过拟合或者数据分布有问题。优先检查数据集是否有类别重复、是否有的类别图片和别的类别高度相似、是否有大量非花卉图片混在里面。这几个问题我实际都踩过:某次训练验证集卡在85%上不去,排查后发现两个类别的训练图片里混了不少标注错误的图,一张张筛选掉之后准确率直接到了90%以上。
还有一种隐蔽的情况是类别不平衡。如果某个类别只有30张图片而其他类别有300张,模型很容易对前者欠拟合。解决办法不只是加数据,还可以在采样时对样本少的类别做过采样,或者在损失函数里加类别权重。
4.2 过拟合的判断标准与处理路径
过拟合最直观的表现就是训练集准确率和验证集准确率之间的gap越来越大。处理办法按优先级排列:
增加数据增强的强度是最温和的手段。把随机旋转从15度扩大到30度,增加随机擦除,人为制造更多变化。其次是加正则化。Dropout的默认值一般是0.2,可以调到0.3到0.5,或者加权重衰减。迁移学习场景下还有一个非常有效的做法:少解冻一些层,或者降低微调阶段的学习率,防止模型在有限数据上剧烈变化。
我在实际项目中遇到过这样一个情况:微调阶段解冻了模型全部层,学习率用了2e-4,结果验证集准确率出现明显震荡,某个epoch从91%掉到87%。换成只解冻最后两个stage、学习率降到5e-5之后,震荡消失,最终稳定在92%左右。这说明在数据量不够大的时候,解冻层数不是越多越好。
4.3 推理阶段预测速度慢的常见原因
如果部署之后发现识别要两三秒才能出结果,先别急着换轻量模型。按顺序排查:第一,模型是否每次请求都重新加载。很多人会把模型加载代码写在接口函数内部,导致每识别一张图都重新读一遍权重文件,这是最典型的低级错误。应该把模型加载放到服务启动阶段,全局只加载一次。第二,是否用了GPU但推理时没有切到cuda。第三,输入图片是否被resize得过大。有些代码写的是resize到800x800而不是224x224,计算量成倍上升。第四,批量推理不等同于单张推理,部署时核对一下代码路径有没有走batch维度。
我的实际经验是,把模型放对设备、输入尺寸固定为224后,EfficientNetB0在普通CPU上推理也能做到500毫秒左右,这个速度已经满足一般交互需求了。
4.4 项目中容易踩的技术坑速查表
做一个表格把典型问题列出来,方便你对照排查:
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 训练loss不下降 | 学习率过大或过小、数据格式错误 | 调学习率,检查标签和图片是否对应 |
| 验证准确率忽高忽低 | 学习率过大、batch size过小 | 降低学习率、适当增加batch |
| 某几个类别永远分不清 | 数据标注混乱、类别视觉上过于接近 | 清洗数据、查找相似类的典型混淆样本、补数据 |
| 推理结果和训练效果差距大 | 预处理不一致 | 确保推理时的resize、normalize和训练完全一致 |
| 内存炸了 | 图片一次性全读入内存 | 用DataLoader流式读取,调小batch |
| 上传的图片识别报通道错误 | 图片是RGBA格式 | 统一convert("RGB") |
| 保存模型后发现类名对不上 | 类别排序错乱 | 训练时保存一份class_to_idx的映射文件 |
这些坑都很低级,但每一个都真实发生在很多项目里。我自己最快的一次排查只花了五分钟就发现问题,原因是推理时忘记做标准化,像素范围还停留在0到255,和训练时的0到1输入完全不在一个量级。这类问题在别人眼里就是"模型不准",实际就是一行代码的事。
5. 项目成果复盘与经验扩展
5.1 从单一识别到完整系统的价值闭环
很多做课程设计或毕业设计的同学,做完训练模型就以为项目结束了,这是非常可惜的。反过来想,一个只有训练脚本和几个准确率数字的项目,别人怎么看都觉得"这就是交作业";一旦你把识别接口、前后端页面、演示视频串起来,整个项目就活了。用户能上手用、能上传照片、能看到结果,这个系统的价值立即展现出来了。
以我这个项目为例,最终交付的不只是模型权重文件,还包括:完整的训练代码、推理服务、前端页面、数据集说明文档、实验记录表、项目报告和答辩PPT。这些材料合在一起就构成了一个"产品"。你去面试或者在答辩现场把自己的系统从头到尾操作一遍,比在简历上写一百个技术名词有用得多。
5.2 后续扩展:从静态识别到更大的产品想象空间
系统在基础功能跑通之后,扩展方向非常多。最直接的是把识别从单花拓展到多花检测——一张照片里有多种花,先用目标检测模型框出来,再逐一分类。技术上可以用Faster R-CNN或者YOLO系列,在原有数据集基础上增加标注框。这个扩展让系统从"单主体识别"升级到"场景理解",适用面一下大了很多。
另一个有价值的扩展方向是增加交互反馈机制。用户可以标注"识别结果正确"或"不正确",正确结果可以进入数据池增强模型,不正确的结果可以触发人工复核。这就是一个非常轻量的人机协同闭环,会让模型的实用价值随时间增长。不要小看这个想法,很多商业识花产品就是这样积累数据、提升准确率的。
5.3 经验沉淀:我给后来者的三条操作性建议
第一条建议是"先跑通极其小的版本"。不要一上来就收集120类图片、训练20个epoch。先拿10类、每类50张图、5个epoch把全流程跑通。这个"最小可用闭环"能让你快速暴露代码和工具链里的问题,等流程顺畅了再扩数据和加算力。我见过太多人连环境都没装好就直接冲大项目,最后卡在各种莫名其妙的报错上,心态直接崩掉。
第二条建议是"从第一个epoch开始就写实验日志"。哪怕只是简单记录每个epoch的训练loss、验证loss、准确率,它对后续改进的价值也是巨大的。没有日志,你改了一个参数都不知道效果是变好还是变坏,整个调优过程就成了盲人摸象。
第三条建议是"每条代码路径都保留可复现性"。数据集划分、预处理参数、模型结构、训练超参,全部记录在配置文件和文档里。这个习惯在写报告时帮你节省大量时间,也会让项目在未来被重新打开时仍然可以一键复现。很多二手代码打开就报错,就是因为环境依赖和参数设置完全没有记录。
6. 实战复盘:那些文档里不会写的东西
文章写到这里,主体内容基本完整了。最后聊一点个人体会。做这个智慧识花系统,技术上最难的从来不是调用模型、写接口,而是你有没有耐心把数据集一点点洗干净、有没有习惯每跑一个实验都记录数据、有没有意识在训练代码之外补全工程化模块。我最后用的模型其实非常简单,就是EfficientNetB0加迁移学习,但在120类、72000张图片这个体量上,它达成了92%以上的验证准确率,上线之后收到的反馈也不错。
如果你也想做一个同类项目,或者你正在写相关报告,我的建议是别把精力浪费在"选最好的模型"上,而是死磕两件事:干净的数据集、规范的工程流程。这两件事做扎实,结果一定不会差。
最后再分享一个我一直在用的小技巧:每次训练结束后,把训练日志、模型评估、一张结果混淆矩阵保存成一个以日期命名的快照目录。时间久了你会发现,这种简单的存档习惯能让你无比清晰地看到整个项目是怎样一步步变好的,做报告、复盘、定位问题都极其顺手。