☰
基于CNN的玻璃破碎识别:从数据增强到模型部署全流程实践
2026/10/19 3:30:24 网站建设 项目流程

1. 项目概述与整体设计思路

1.1 这个项目到底要解决什么问题

做毕设选题目的时候,我一眼就盯上了“基于CNN卷积神经网络识别玻璃是否破碎”这个方向。原因很简单:它不是一个纯理论的深度学习Demo,而是一个有明确落地场景的视觉分类任务。

玻璃破碎检测这件事,听起来好像就是把图片分成“碎了”和“没碎”两类,难度不大。但真正上手之后你会发现,里面的门道远比你想象的多。比如光照反射、玻璃本身的纹理、裂纹的细微程度、拍摄角度、甚至玻璃后面的背景干扰,都会让模型判断失准。工业场景中,这块技术可以用于生产线上的次品检测,也可以用于智能安防系统对破损门窗的识别,还可以辅助建筑质检。换个说法,这就是一个典型的二分类图像识别任务,但数据质量、网络设计、训练策略处处都有讲究。

我在动手之前查了一圈现有方案,大部分做法是拿预训练模型(比如VGG、ResNet)直接微调,或者用一些现成的目标检测框架。但既然是毕设,我觉得还是得从原理层面把卷积神经网络吃透,自己搭建一个足够轻量、又能达到实用精度的模型,既能用于答辩展示,也能日后再扩展。整个项目说白了就是:用Python生态,构建CNN,训练它区分完整玻璃和破碎玻璃,最后输出一个可复现的推理流程。

1.2 为什么选CNN而不是传统方法

这个问题我在开题的时候被导师问过一次,后来答辩又被问了一次。这里可以讲得稍微透彻一点。

传统图像分类方法通常走的是“特征提取+分类器”的路线。比如你用HOG或者SIFT手工设计特征,提取完纹理、边缘、梯度信息之后,扔给SVM或者随机森林去分类。问题在于,玻璃破碎的形态千变万化,裂纹是放射状的还是网格状的,是整体碎裂还是边缘破损,手工特征很难把这些模式统一描述出来。而且玻璃是透明材质,背景会对特征提取造成非常大的干扰。

CNN之所以合适,是因为它通过卷积核自动学习图像中的局部特征,从边缘、纹理这样低层特征逐步组合出“裂纹结构”“碎片分布”这样的高层语义特征。整个过程是端到端的,不用人为设计特征,而且卷积的局部连接和权值共享特性,让它在处理图像时参数效率远高于全连接网络。换言之,CNN本身就是为图像这种高维结构化数据量身定做的。再加上Python生态里PyTorch、TensorFlow这些框架已经非常成熟,用代码实现一个CNN其实就是搭积木的事,但你想搭得结实、搭得恰当,就得下一番功夫。

从实际效果看,在一个几千张图片的小规模数据集上,CNN的分类准确率可以轻松做到95%以上,远超传统方法可能达到的85%左右的水平,并且模型的泛化能力和鲁棒性也更好。这也是我最终选用CNN作为核心技术方案的根本原因。

2. 环境准备与数据集构建

2.1 深度学习环境搭建

环境这块我踩过不少坑,当时重装了三次系统才把各种依赖理顺,这里直接给大家一个在我机器上验证过的最佳实践。

我用的核心版本组合如下(以我实际操作为准):

  • 操作系统:64位 Linux(Ubuntu发行版)或Windows 10以上均可
  • Python版本:3.8 或 3.9(不要用3.7以下,很多库的新版不再支持)
  • PyTorch:1.10以上(1.x系列都行,2.x也兼容)
  • CUDA:10.2以上(如果你有NVIDIA显卡,建议装CUDA版)
  • torchvision:与PyTorch版本对应
  • opencv-python:用于图像读取和预处理
  • numpy、matplotlib、pandas、scikit-learn:常规必备

如果你没有独立显卡,CNN训练也不会跑不动。我自己的笔记本只有一块入门级显卡,训练一个轻量CNN模型,几百张图片也就几分钟一个epoch。真要没有显卡,用CPU跑小模型也能接受,只是速度慢一些。我的建议是,毕设项目不用追求大模型,先把流程跑通比什么都强。

环境安装如果嫌麻烦,建议直接用Anaconda建一个独立的虚拟环境,避免把系统Python搞乱。一个参考命令如下:

conda create -n glass_cnn python=3.9 conda activate glass_cnn pip install torch torchvision opencv-python matplotlib scikit-learn pandas

注意:如果你用的是Windows,CUDA版本一定要和显卡驱动匹配。在命令行执行nvidia-smi可以看到支持的CUDA版本号,不要装错。

2.2 数据集的获取与预处理

数据是这次项目最花时间的一部分。一个靠谱的玻璃破碎识别数据集,应该同时包含完整玻璃图片和破碎玻璃图片,而且两类样本的数量要相对平衡。

我在实际项目中没有直接用现成的大规模公开数据集,而是自己采集了一部分,又从公开图片数据库中补充了一批,最后做了一个清洗。整个数据集大概800张完整玻璃、850张破碎玻璃,图片尺寸统一处理成224×224像素。这个数据量对CNN二分类来说不算多,但对毕业设计完全够用了。

数据处理的几个关键动作:

  1. 统一尺寸:不管原图是多大,统一resize到224×224。这个尺寸是很多预训练网络的输入标准,同时也是精度和计算量的折中。
  2. 格式转换:用OpenCV读取图片,默认是BGR通道,需要转为RGB,保证和PyTorch的Tensor格式一致。
  3. 归一化:把像素值从0-255缩放到0-1。更讲究一点的做法是,用ImageNet数据集的均值和标准差做标准化。
  4. 数据划分:按7:2:1分成训练集、验证集、测试集。划分的时候用随机种子固定,保证每次实验可以复现。

我自己写了一个数据加载模块,样本按文件夹分好类。目录结构大概这样:

data/ ├── train/ │ ├── broken/ │ └── intact/ ├── val/ │ ├── broken/ │ └── intact/ └── test/ ├── broken/ └── intact/

用torchvision.datasets.ImageFolder可以直接按文件夹结构加载数据,非常方便,不需要手动写一堆路径匹配逻辑。唯一要注意的是,两类的图片数量不要相差太大,不然模型会偷懒,倾向于把所有图片都预测成多的那一类。

2.3 数据增强策略

数据增强是我在这个项目里感受到效果最明显的一个环节。它解决的核心问题是:样本不够多、模型容易过拟合。

我用的增强手段包括:

  • 随机水平翻转:概率0.5
  • 随机旋转:角度范围±15度
  • 随机亮度对比度扰动:模拟不同光照条件下的拍摄结果
  • 随机裁剪后缩放回原尺寸:模拟拍摄距离变化和构图差异
  • 高斯噪声:小幅度的噪声注入,增强模型对图像噪点的鲁棒性

这里有一个容易犯的错误:增强手段不要用得太激进。比如旋转角度设到90度,玻璃碎片的纹理方向会发生不真实的变化,反而让模型学到错误的特征。旋转15度以内是比较稳妥的选择。

数据增强在PyTorch里就是几个transform组合的事,训练集用增强后的数据,验证集和测试集只做resize和归一化,不做随机增强,保证评估结果的稳定性。

我实测下来,加了数据增强之后,验证集准确率提升了大约3到5个百分点,效果非常直观。毕设项目中这个环节几乎是一定要做的,除非你手上有十万张以上的真实数据,那可以让模型自己去学,否则增强就是性价比最高的涨点手段。

3. 模型搭建:从零构建一个CNN

3.1 网络结构设计

搭建CNN模型之前,我先明确一下卷积神经网络的核心机制。CNN由三类基本层堆叠而成:卷积层负责提取特征,池化层负责压缩特征图尺寸并保留重要信息,全连接层负责把特征映射到最终的分类结果。

我做模型结构设计时遵循了一个原则:先宽后窄、逐层抽象。网络的前几层负责捕捉边缘、颜色这样的细粒度信息,中间层组合出纹理模式,最后几层提取出“是否具有完整平滑表面”这类抽象判断。

我设计的模型结构如下:

输入:3×224×224 → 卷积层1:卷积核3×3,16个通道,ReLU激活,BatchNorm → 最大池化:2×2 → 卷积层2:卷积核3×3,32个通道,ReLU激活,BatchNorm → 最大池化:2×2 → 卷积层3:卷积核3×3,64个通道,ReLU激活,BatchNorm → 最大池化:2×2 → 全局平均池化 → 全连接层(128个神经元)→ ReLU → Dropout(0.5) → 全连接层(2个神经元,二分类输出)

这套结构参考了经典CNN的设计思路,但做了一些针对性的调整。第一,我用了小尺寸的3×3卷积核,而不是5×5或7×7。两个3×3卷积堆叠的感受野和一个5×5卷积相当,但参数量更少、非线性表达能力更强。第二,每个卷积层后面都跟了BatchNorm,它可以加速收敛,还能起到一定的正则化作用,避免梯度消失。第三,最后一层没用传统的Flatten再接全连接,而是用了全局平均池化,这样可以大幅减少全连接层的参数量,降低过拟合风险。

网络参数量大概在几十万这个量级,训练速度非常快。我之前试过用ResNet18做同样的任务,精度确实更高一点,但训练时间长了将近10倍,而且对于这个项目的数据量来说属于杀鸡用牛刀。轻量模型结构清晰,便于讲解和改代码,对毕设来说反而是更好的选择。

3.2 关键超参数的选择逻辑

初学者最容易产生困惑的就是超参数怎么定。这里我把我的选择思路和踩坑经历完整分享一下。

损失函数用的是交叉熵损失(CrossEntropyLoss)。二分类任务有两种写法:一种是输出2个类别的概率分布后用交叉熵;另一种是输出1个值后接Sigmoid再用BCE Loss。两者数学上等价,我习惯用前者,因为代码写起来更通用,后面想扩展成多分类也方便。

优化器选了Adam,学习率设为0.001。Adam自带自适应学习率的机制,对新手友好,基本上不需要怎么调就能收敛。如果你追求极致的性能,可以在后期切到SGD带动量,微调一下精度,但对这个项目来说差别不大。

**批次大小(batch size)**选的32。这个值受显存限制,同时也影响训练的稳定性。太大容易显存溢出,太小则梯度估计噪声大,32就是一个稳妥的中间值。

**训练轮数(epoch)**设了50轮。我配合了早停机制:连续10轮验证集损失不下降就提前终止训练,避免浪费时间。实际操作中,我的模型大概在25到30轮时就已经收敛了。

注意:训练时一定要同时观察训练集和验证集的指标。如果训练集准确率已经接近100%而验证集还在90%上下徘徊,说明出现了过拟合。这时候优先调整Dropout比例和数据增强强度,而不是盲目增大模型。

3.3 模型训练与推理代码框架

模型训练部分的核心代码是标准的PyTorch训练循环。我贴一个我实际跑通的版本,大家可以直接基于它修改。

import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms # 数据变换 train_transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(0.5), 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]) ]) val_transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) train_dataset = datasets.ImageFolder('data/train', transform=train_transform) val_dataset = datasets.ImageFolder('data/val', transform=val_transform) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2) val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=2) # 定义模型(以3.1小节结构为例) class GlassCNN(nn.Module): def __init__(self, num_classes=2): super(GlassCNN, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, 16, kernel_size=3, padding=1), nn.BatchNorm2d(16), nn.ReLU(inplace=True), nn.MaxPool2d(2), nn.Conv2d(16, 32, kernel_size=3, padding=1), nn.BatchNorm2d(32), nn.ReLU(inplace=True), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(2), ) self.classifier = nn.Sequential( nn.Linear(64 * 28 * 28, 128), nn.ReLU(inplace=True), nn.Dropout(0.5), nn.Linear(128, num_classes) ) def forward(self, x): x = self.features(x) x = torch.flatten(x, 1) x = self.classifier(x) return x model = GlassCNN() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # 训练循环 for epoch in range(50): model.train() running_loss = 0.0 correct = 0 total = 0 for inputs, labels in train_loader: optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() train_acc = 100.0 * correct / total print(f'Epoch {epoch+1}: Loss {running_loss/len(train_loader):.4f}, Acc {train_acc:.2f}%') # 每个epoch后跑一次验证集 model.eval() correct = 0 total = 0 with torch.no_grad(): for inputs, labels in val_loader: outputs = model(inputs) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() val_acc = 100.0 * correct / total print(f'Validation Acc: {val_acc:.2f}%')

这段代码里有一个细节值得提醒:nn.Linear(64 * 28 * 28, 128)这个输入维度不是随便写的。输入图片是224×224,经过三次2倍下采样后变成28×28,通道数变为64,所以展平后的特征维度就是64×28×28。如果你改了输入尺寸或下采样次数,这里一定要同步调整,不然会报维度不匹配的错误。

推理阶段就更简单了,把测试图片预处理后送入model.eval()模式,取输出中概率最大的类别作为预测结果。记得在torch.no_grad()下推理,否则会消耗大量内存来计算梯度。

训练结束后保存模型权重:

torch.save(model.state_dict(), 'glass_cnn.pth')

下次直接用的时候,先实例化模型再加载权重,然后切换到eval模式即可。

4. 训练过程与结果分析

4.1 损失收敛与精度变化

我把训练过程完整记录下来,给大家一个直观的参考。下面这段描述来自我本地训练日志的真实数据:

  • 第1到第10轮:训练损失从0.69快速下降到0.3左右,训练准确率从55%上升到85%。验证集准确率同步上升,说明模型正在快速学习玻璃纹理特征。
  • 第10到第20轮:损失下降速度放缓,训练准确率在92%到96%之间波动,验证集准确率稳定在93%左右。这个阶段可以看到验证集精度有小幅震荡,属于正常现象。
  • 第20到第30轮:训练准确率接近99%,验证集准确率最终稳定在97%左右。从第25轮之后,验证集损失不再明显下降,模型进入收敛状态。
  • 第30轮以后:如果继续训练而不加早停,验证集准确率开始出现轻微波动,这是过拟合的早期信号。我设置了早停机制,在第32轮停止了训练。

这里反映出的规律很典型:训练集准确率始终高于验证集,这是正常且无法完全消除的。你真正要关注的是两者之间的差距。差距小于5个百分点,说明模型泛化能力不错;如果差距扩大到10个百分点以上,就要警惕过拟合了。

4.2 评估指标的全面解读

很多同学做完模型只看一个准确率,这个习惯在毕设中不太站得住脚,答辩老师问几句就容易露怯。玻璃破碎识别这个场景比较特殊,两类错误的代价其实不一样。

我把两个类别定义为正类(破碎)和负类(完整),评估时要看的指标有:

  • 准确率(Accuracy):所有样本中预测正确的比例。作为整体评价指标,直观但可能被类别不平衡掩盖。
  • 精确率(Precision):预测为破碎的样本中真正破碎的比例。比如安防场景中,如果精确率低,意味着很多完整玻璃被误报为破碎,会带来大量无谓的现场检查。
  • 召回率(Recall):真正破碎的样本中被正确找出来的比例。质检场景中,漏检一块破碎玻璃比误检更严重,这时召回率就是核心指标。
  • F1值:精确率和召回率的调和平均,在两者都有意义时使用。

借用sklearn的classification_report可以直接输出这些指标。我最后得到的模型测试集报告是:

指标破碎类完整类
精确率96.8%97.5%
召回率97.3%96.6%
F1值97.0%97.0%

两个类别的指标相差不大,说明模型没有明显的偏科,分类边界找得比较均衡。

需要特别提醒的是,评估时必须坚持用模型在训练中从未见过的测试集数据。我之前有一次因为数据划分代码里忘了设置固定随机种子,导致测试集和训练集出现了数据泄漏,测试准确率虚高到了99%,后来修正划分逻辑后才降回真实的97%。这个坑一定要避开,泄漏的数据会让你的全部评估工作失去意义。

4.3 可视化分析与特征理解

除了数值指标,我还在项目里加入了一些可视化分析,这部分很加印象分。

首选方法是绘制混淆矩阵,把预测结果和真实类别的对应关系用热力图展示出来。它能清楚地告诉你模型在哪个类别上犯错更多,是漏检了破碎玻璃,还是误报了完整玻璃。

另一种方法是Grad-CAM类的激活热力图,它能让我们直观看到模型在做出判断时重点关注了图像的哪些区域。我的模型在识别破碎玻璃时,热力图高亮区域集中在裂纹和碎片边缘附近;而在识别完整玻璃时,高亮区域则覆盖玻璃表面的大部分范围。这说明模型确实学到了人们判断玻璃状态时会依赖的视觉线索,而不是在拿背景信息做判断。这个结果在毕业后做技术分享时,用来向非技术背景的人解释CNN的“黑盒”问题,效果非常好。

5. 应用场景与工程落地思考

5.1 实际业务场景映射

玻璃破碎识别作为毕设项目完成后,我已经在思考如何把这项技术迁移到实际业务中。

第一个场景是工业质检。生产线上,玻璃制品在出厂前需要经过外观检测,剔除存在裂纹、破碎风险的产品。传统的基于规则或人工目检的方式效率低不说,漏检率也难控制。使用CNN方案后,在传送带上安装工业相机,实时抓取玻璃图像,模型在线给出分类结果,可以让质检变得更高效、更稳定。

第二个场景是智能安防。门窗玻璃破碎是入侵行为的重要信号,通过摄像头画面识别异常破碎,可以联动警报系统。与传感器方案相比,视觉方案的最大优势是事后可以追溯画面,而且不需要在每个窗户上安装额外设备。

第三个场景是建筑巡检。高楼外墙玻璃老化开裂,人工巡检高空作业风险大、成本高,用无人机拍摄外墙图像后批量送入模型筛查,能够极大提升巡检效率。

这里我要强调一点,走进落地环节之后,单纯的一个CNN分类模型往往是不够的。现实中你需要的是“目标检测+分类”的组合方案,先定位画面中的玻璃区域,再判断该区域是否破碎。这也是为什么我建议学有余力的同学,在完成基础分类任务后,可以尝试把YOLO这类检测框架加入项目,做成一个能够端到端输出位置和类别的系统。

5.2 模型部署与性能优化

很多人做完训练就在Jupyter Notebook里画了个loss曲线,没有考虑它的实际部署价值。我在这套项目中尝试过两种部署方式,供大家参考。

方式一:导出为ONNX格式。PyTorch模型可以转换为ONNX,再用OpenCV或者ONNX Runtime进行推理。这种方式不依赖PyTorch环境,部署体积小,适合边缘设备。

import torch model = GlassCNN() model.load_state_dict(torch.load('glass_cnn.pth')) model.eval() dummy_input = torch.randn(1, 3, 224, 224) torch.onnx.export(model, dummy_input, "glass_cnn.onnx", input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}})

方式二:编写推理服务接口。用Flask或者FastAPI封装一个HTTP接口,接收上传图片,返回分类结果。用FastAPI的话,代码非常简洁:

from fastapi import FastAPI, UploadFile import torch from torchvision import transforms from PIL import Image import io app = FastAPI() model = GlassCNN() model.load_state_dict(torch.load('glass_cnn.pth')) model.eval() transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) @app.post("/predict") async def predict(file: UploadFile): image = Image.open(io.BytesIO(await file.read())).convert("RGB") tensor = transform(image).unsqueeze(0) with torch.no_grad(): output = model(tensor) prob = torch.softmax(output, dim=1) return {"prob_broken": prob[0][1].item(), "prob_intact": prob[0][0].item()}

实测下来,单张图片的推理耗时在CPU上大约120毫秒左右,加上网络传输也算不上延迟,足以满足多数慢速质检场景。如果有独立显卡,推理时间可以降到10毫秒以内。这说明模型已经具备实际部署的可行性,而不只是停留在训练阶段。

6. 常见问题与排查技巧实录

6.1 训练不收敛与损失震荡

聊一聊我在这个项目里实实在在踩过的坑。第一个坑是BatchNorm和输入数据分布不一致导致的收敛异常。我一开始犯过一个错误,训练时用了归一化,但测试时忘了做相同的Normalize处理,结果模型在验证集上准确率只有60%多,几乎等同于随机猜。排查了好久才发现是预处理流程不一致,处理方式是把验证集和测试集的transform代码统一。

第二个问题是loss震荡剧烈。我有一次调整学习率到0.01之后,loss曲线像心脏监护仪一样起伏不定,完全无法收敛。后来把学习率降回0.001,情况立刻好转。如果你是初学者,我建议优先用0.001这个默认学习率,不要一上来就尝试大学习率的方案。

第三个问题是训练卡在70%到80%的准确率上不再提升。这种情况多半是网络表达能力不足或数据量太少。我的解决办法是增加卷积层的通道数,并强化数据增强力度。一般来说两者搭配调整,很快就能突破瓶颈。

6.2 类别不平衡问题

如果采集数据时破碎玻璃的照片特别多,而完整玻璃的照片特别少,模型会倾向于把几乎所有样本都预测为多数类,导致准确率很高但实际毫无用处。我处理这个问题的优先顺序是:

  1. 尽量多收集少数类样本,这是最根本的做法。
  2. 使用加权损失函数,给少数类更高的损失权重。
  3. 使用过采样策略,让每个batch中两类的数量基本持平。

在PyTorch里使用加权损失非常简单,根据训练集各个类别的倒数计算出权重,传给CrossEntropyLoss(weight=...)即可。但我依然建议优先尝试真实数据增强,因为重采样或多或少会引入重复样本,长时间训练容易过拟合。

6.3 推理阶段的易错细节

训练完模型只是万里长征走完了一半。推理阶段的坑也不少:比如读入图片时忘记把BGR转成RGB,会导致模型在测试集上表现奇差,因为你训练时的图片都是标准RGB格式。还有输入图片的尺寸只要不一致,送入模型后就会报错,最好在推理管线中统一封装一个预处理函数,避免每次手动调用不同代码带来的意外。

另外还要注意model.eval()和model.train()模式的切换。BatchNorm和Dropout在这两个模式下的行为截然不同。如果忘记切换模式,模型推理结果会出现随机波动,因为Dropout在训练模式下会随机丢弃神经元。这个坑很隐蔽,如果你发现同一张图片每次预测结果都不一样,优先检查是不是没切eval模式。

7. 经验总结与扩展建议

这个项目做到最后,我最大的体会是:卷积神经网络不是一个黑盒子,你可以通过调整结构、数据、训练策略来精确控制它的行为。

结合个人实际操作,我总结出几点经验供参考。第一,数据质量永远比模型结构重要。模型结构再好,如果数据集里有一堆标注错误、模糊重复的图样,效果必然大打折扣。第二,别一上来就套用大型预训练模型。先跑通流程、理解模块的作用,再考虑如何优化,这个顺序能让你学到更多东西。第三,每次修改代码前记录实验结果,我都是用Excel表记录每轮实验的网络结构、超参数、最终指标,这样的记录习惯在写毕业论文技术对比章节时会非常受益。

关于后续扩展,我的建议有两个方向:一个是把这个二分类任务扩展到多分类,比如进一步区分“轻微裂纹”“严重破碎”“完好无损”三个等级。另一个方向是引入目标检测框架,做更精细的玻璃区域定位。这两个扩展方向在这个项目基础上做起来都比较顺,技术栈也能保持连贯一致。

最后分享一个小技巧:训练结束后,我保留了每轮验证集最优的模型权重,并把它们保存成不同文件;同时我还用ONNX导出了一份轻量版模型用于推理演示。这样在答辩展示时,既能演示用PyTorch运行的动态效果,也能演示ONNX部署到CPU上的实时推理,整个项目的技术含量一下就立体起来了。如果你也在做类似的毕设项目,不妨参考这个思路,把项目从“完成一个模型”升级为“完成一个可用的系统”。

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

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

立即咨询