☰
基于Python的CNN卷积神经网络玻璃破碎识别实战解析
2026/10/22 4:14:25 网站建设 项目流程

1. 项目概述

搞计算机视觉方向的毕设,你是不是也遇到过这种困境:人脸检测、车牌识别、垃圾分类这些题目,网上现成代码一抓一大把,交上去老师看一眼就知道是抄的,答辩的时候一个问题就能把你问住。

这个“基于Python的CNN卷积神经网络识别玻璃是否破碎”的选题,我觉得就属于那种看起来不难、但认真做完非常有含金量的方向。玻璃破碎检测听起来是个小众场景,但它背后其实是典型的图像二分类问题,而且这个分类问题里包含了足够多的坑:玻璃本身透明、反光、背景复杂、破碎纹理不固定,要做到稳定识别并没有想象中那么简单。换句话说,它比MNIST手写数字、CIFAR-10那种纯入门数据集要贴近真实业务,但又比工业检测里那种需要精密打光的场景更容易在宿舍和实验室复现。

这篇文章会按照我实际做一个完整项目的顺序,从选题拆解、数据集构建、模型选型、训练调参到常见坑位排查,完整走一遍。适合正在做毕设、或者想在一个小场景里完整跑通深度学习流程的同学参考。就算你之前没怎么碰过深度学习,只要有一点Python基础,按这套思路也是能复现出来的。

1.1 这个选题解决什么问题

玻璃破碎识别,在现实里对应的需求很多。比如汽车玻璃的出厂质检,产线上玻璃有没有裂纹、有没有炸点,现在很多工厂还是靠老师傅肉眼去看,效率和稳定性都有上限;再比如高层建筑外墙玻璃的定期巡检,碎裂的玻璃如果不及时更换,坠落风险很高;还有安防监控场景,玻璃破裂本身就是入侵事件的强信号。

这些场景对算法的需求,本质上分两类:一类是位置敏感的,既要判断有没有破碎,还要给出破碎区域在哪;另一类就是你毕设要做的分类层面的判断,只关心每一张图片对应的是“完整玻璃”还是“破碎玻璃”。后者作为毕设题目,技术栈干净,评估指标明确,工作量可控,而且可以在后期很容易地扩展成目标检测任务,给你的毕设“加戏”。

1.2 为什么选CNN而不是传统图像处理

我知道很多人第一反应是用OpenCV做边缘检测,比如Canny算子提取边缘,再看边缘密度或者连通域特征。听起来很合理,毕竟破碎玻璃的边缘确实多。但实际做一下你就会发现传统方法在这类问题上非常脆弱:完整玻璃的边缘信息本身就不少,比如窗户边框、窗外景物、地面的反射纹理,这些都会被Canny检出来;再加上光照一变化,玻璃的透光属性直接导致边缘响应剧烈波动,阈值怎么调都压不住误检。

而CNN,尤其是卷积神经网络,解决的是“特征自动学习”的问题。你不需要手工定义“什么是裂纹”,网络会自己从数据里学出能区分完整和破碎的纹理模式、局部反差模式。这也是深度学习在视觉任务上战胜传统特征工程的根本逻辑——特征提取这件事被端到端地交给了网络。关键是你得给它足够好的数据,而这就牵出了我做这个项目时踩的第一个大坑:数据集。

2. 数据集构建:这个项目最容易翻车的环节

说实话,玻璃破碎识别这个题目,模型层面几乎没有门槛,用现成的ResNet就能起步。真正的分水岭在数据。很多同学上来先写网络结构,写完才发现图片素材根本不够,只能从网上东拼西凑几百张,训练出来的模型可能连自己专门留出来的测试图都认不对。我建议你反过来做:先用至少一周时间把数据问题彻底解决,模型代码半天就能跑通。

2.1 数据来源与合理性评估

最理想的情况,是找到可以直接用的公开数据集。但玻璃破碎检测这个方向,公开数据并不像猫狗识别那样随手可得。我在做的时候整理了三个可行来源,你可以根据自己的条件组合使用:

  1. 开源平台检索:在国内外开源数据集网站上搜crack/glass/defect相关关键词,把能找到的玻璃裂纹、工业缺陷图片全部下载下来。但注意,很多裂纹数据集是混凝土、金属表面的,和玻璃材质有很大区别,如果直接用会带偏模型。你需要人工筛一遍。
  2. 自己构建拍摄:找一个干净的背景,准备几块普通平板玻璃。完整状态的玻璃直接拍;破损的玻璃可以手动敲出裂纹,或者把碎玻璃拼回原来的形状再拍。拍摄时多换几个角度和光照条件。这是性价比很高的方式,大概拍两三百张就能用。
  3. 从学术论文的公开附件里收集:有些论文会附带少量样例图片,你不需要用里面的算法,但可以借鉴他们的图片拍摄角度和数据分布。

我自己当时的做法是:公开来源筛出大概600张,自拍200张,合起来800张,按类别平分为“完整”和“破碎”。这里有一个很重要的观念要提前讲清楚——毕设项目不是工业级产品,800到1200张图完全够做出一个有余量的基线系统。别被“深度学习需要百万数据”的说法吓到,二分类任务加上迁移学习和数据增强,这个数据量已经能支撑你写出一份扎实的毕设说明了。

2.2 标注与类别平衡的注意事项

标注看起来简单,就两类:完整(intact)、破碎(broken)。但实际标注过程中有几种图你需要特别小心:

  • 玻璃上只有轻微划痕,没有裂纹,算哪类?我建议归为完整。因为轻微划痕在实际场景中不属于需要判定为失效的状态。
  • 玻璃掉了一个角但主体完好,算哪类?这种模糊样本建议直接丢弃,别让它干扰边界学习。
  • 图片里同时出现两块玻璃,一块完整一块破碎,算哪类?你训练的是单目标分类器,这种多目标图会让网络无所适从,同样建议裁剪掉或者标注成破碎(如果主体是碎的那块)。

类别平衡方面,尽量保证完整和破碎各占50%。如果破碎图特别难找,至少也要做到6比4以内。差距太悬殊的话,网络会倾向于把所有图都预测成数量多的那一类,直观表现就是准确率看着还行,但少数类的召回率惨不忍睹。这个问题我在后面调优章节还会专门说。

2.3 预处理与数据增强:别让网络学到背景

图像分类的预处理常规操作就三件套:缩放、归一化、标准化。我用的输入尺寸是224×224,这是ResNet系列的标准要求。归一化简单说就是把0到255的像素值压到0到1之间,标准化则是对每个通道减去均值再除以标准差,让数据分布落在激活函数比较容易响应的工作区间。具体数值直接用预训练模型配套的ImageNet均值方差即可,不用自己重新统计。

数据增强才是真正值得花心思的地方。我的经验是这四个增强手段在这个项目上收益最明显:

  • 随机水平翻转和垂直翻转:玻璃纹理没有方向性,翻转不会破坏语义,这是最安全的增强方式。
  • 随机旋转(90度、180度、270度):对于裂纹这种各向异性的纹理,旋转能极大丰富样本空间。
  • 亮度对比度扰动:有效模拟不同光照条件,提高模型对光线变化的鲁棒性。
  • 随机裁剪缩放:模拟玻璃在不同距离下的成像尺度。

但有一个增强手段我强烈建议你不要用:弹性形变(Elastic Transform)。裂纹本身是细长结构,弹性形变会把一条连续裂纹扭成波浪状,破坏真实的物理形态。你也不希望模型学习到“破碎=弯曲的线”这种伪规律吧。另外高斯噪声加不加都行,加了可以提升一点鲁棒性,但过头会让细裂纹被噪声淹没,建议幅度调小。

提示:增强操作一定要在“训练集”上做,验证集和测试集只做缩放和归一化。否则验证集和训练集分布重叠,测出来的指标虚高,答辩时一测真实场景就露馅。

3. 模型选型与训练配置:直接用ResNet还是自己搭网络

网络结构是这个项目最容易被过度设计的地方。有人喜欢自己写一个五层六层的CNN,觉得这样“原创性高”。但说句得罪人的话,在没有数据量支撑的情况下,自研小网络的效果通常不如标准网络的迁移学习,你的毕设论文也不因为网络复杂性而加分。评委会更关心你“为什么这么选”和“实验结果说明了什么”。

3.1 结构选择:ResNet18是性价比最优的基线

我最终选的是ResNet18。比较如下:

网络结构参数量推理速度在中等数据集上的表现适合程度
自研3层CNN少最快欠拟合风险高,特征表达能力弱不推荐
VGG16很大慢效果可以,但训练和解码都吃力不推荐
ResNet18适中快稳定,迁移学习效果好推荐
ResNet50较大较慢效果略好,但对显存要求高有条件可尝试
MobileNetV3小极快效果略低于ResNet18,但效率高可作后期轻量化对比

为什么不是VGG16?VGG的卷积层设计很规整,但全连接层占用了大量参数,在只有一千张图的项目里,这种高容量模型很容易在训练集上过拟合,反而降低泛化能力。ResNet18的残差连接解决了深层网络的梯度消失问题,网络相对浅,容量适中,最关键的是ImageNet预训练权重容易获取、迁移效果稳定。

如果你有余力,可以在论文里加一个对照组,把ResNet18换到ResNet50跑一遍,结论大概率是ResNet50在测试集上提升有限但训练时间明显变长。这个对比写进毕设是加分项,说明你理解了模型容量和样本量之间的匹配关系。

3.2 迁移学习与冻结策略

从零训练一个ResNet18,在千级数据量下基本等于白给。数据太少,网络学不到通用的边缘、纹理、形状等基础特征,最后的精度可能只有80%上下,而且训练极不稳定。我直接加载了ImageNet预训练权重,然后做了两步:

  1. 替换最后的全连接层:把默认的1000分类输出改成2分类输出。
  2. 分阶段微调:前10个epoch冻结Backbone所有参数,只训练新增的全连接层,让随机初始化的分类头先收敛;达到平台期后,再解冻最后几个残差块,以更小的学习率微调整个网络。

这个“先训头、再微调”的策略,看起来简单,实际效果非常明显。它避免了一上来就用大学习率更新预训练参数导致的特征破坏。很多人迁移学习效果不好,就是因为在第一阶段就把整个网络一起更新了,预训练特征还没来得及发挥就被冲乱了。

3.3 关键训练参数设置

我在训练时用的核心超参,你可以直接抄:

BATCH_SIZE = 32 EPOCHS = 30 LEARNING_RATE = 1e-4 # 微调阶段用3e-5 OPTIMIZER = "AdamW" WEIGHT_DECAY = 1e-4 SCHEDULER = "CosineAnnealingLR"

选择AdamW而不是SGD,是因为AdamW在微调场景下收敛快、对学习率不敏感,小白用起来不容易翻车。SGD加上合适的学习率策略其实上限更高,但对参数调节的精度要求高,在毕设时间有限的情况下不建议作为主线。权重衰减设成1e-4,可以抑制大权重,缓解过拟合。

学习率调度器我用的是余弦退火,它在训练后半段可以把学习率平滑降下来,比StepLR那种阶梯式下降更稳定,也不容易出现后期震荡。

还有一个容易被忽略但极其重要的设置:固定随机种子。PyTorch里要做三件事,torch.manual_seed、random.seed、numpy.random.seed,再把cudnn的deterministic=True打开。不固定种子的后果是,你跑两次训练,最终指标差了三个点。你根本说不清是模型问题还是运气问题,写进论文里也经不起复现检验。

4. 训练核心实现与踩坑实录

这章我直接给出一个能跑通的最小实现框架,然后逐个说明关键点。这里用的是PyTorch,因为PyTorch的API设计对研究调试友好,断点查看中间张量很方便,社区资料也多,毕设答辩被问到底层实现时你也容易答上来。TensorFlow当然也能做,但PyTorch是现在学术界的主流选择,建议优先考虑。

4.1 核心训练代码框架

import torch import torch.nn as nn from torchvision import models, transforms, datasets from torch.utils.data import DataLoader # 数据变换 train_transforms = transforms.Compose([ transforms.Resize(256), transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.RandomVerticalFlip(), transforms.ColorJitter(brightness=0.3, contrast=0.3), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) val_transforms = transforms.Compose([ transforms.Resize(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_transforms) val_dataset = datasets.ImageFolder('data/val', transform=val_transforms) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4, pin_memory=True) val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=4, pin_memory=True) # 加载预训练模型 model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) num_features = model.fc.in_features model.fc = nn.Linear(num_features, 2) model = model.to(device) # 损失函数:类别不平衡时加上类别权重 weights = torch.tensor([1.0, 1.0]).to(device) criterion = nn.CrossEntropyLoss(weight=weights) # 优化器 + 调度器 optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30) # 训练循环 for epoch in range(30): 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() optimizer.step() running_loss += loss.item() * images.size(0) scheduler.step() # 每个epoch后在验证集上评估 model.eval() correct, total = 0, 0 with torch.no_grad(): for images, labels in val_loader: images, labels = images.to(device), labels.to(device) outputs = model(images) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() val_acc = 100 * correct / total print(f"Epoch {epoch + 1}: val_acc = {val_acc:.2f}%")

这段代码看着不长,但有几个细节我提醒一下。RandomResizedCrop会先随机裁剪再缩放到224,它比单纯的Resize加CenterCrop猛得多,能够迫使模型学习玻璃的局部纹理特征。验证集的ToTensor和Normalize必须和训练集完全一致,这是新手最容易漏的——漏了以后输入分布不一致,指标直接崩掉。

还有就是DataLoader的num_workers。很多人喜欢设成0,省事,但遇到Windows系统,设成4容易在子进程里重复加载模型导致内存爆掉;设高了反而会因为进程切换开销让训练变慢。我建议Windows上设2,Linux上设4到8。另外pin_memory=True在显存充裕时能减少CPU到GPU的数据拷贝时间,对训练速度有实打实的提升。

4.2 冻结与微调的具体实现

如果你想严格复现我前面说的“先训头、再微调”策略,训练循环里要加一点控制逻辑:

# 第一阶段:冻结backbone,只训练分类头 for param in model.parameters(): param.requires_grad = False for param in model.fc.parameters(): param.requires_grad = True # 训练10个epoch # 第二阶段:解冻最后两层 for name, param in model.named_parameters(): if name.startswith('layer4') or name.startswith('fc'): param.requires_grad = True else: param.requires_grad = False # 把优化器里的学习率改成3e-5,再训练20个epoch

第二阶段只解冻layer4和fc,是因为ResNet越靠近输入的层学到的是通用低级特征(边缘、色块),这些特征在ImageNet上已经学得足够好了,不需要在玻璃数据上大改;而靠近输出的高层特征更偏向纹理组合和目标局部结构,与玻璃裂纹强相关,值得重点微调。这完全符合迁移学习的基本规律。

5. 评估指标与调优方向:不要只看准确率

很多毕设论文里,实验部分就放一个最终准确率,比如“模型在测试集上达到96%”。这个数字在本质上没什么说服力,因为你不知道96%到底是把完整玻璃都认成了破碎,还是两项都还可以。玻璃破碎识别里,漏检和误检的代价完全不一样。漏检意味着破碎玻璃当成完好玻璃放行,这在质检场景里可能出安全事故;误检意味着完好的玻璃被当成破碎报废,增加的是返工成本。

5.1 从准确率到混淆矩阵

我建议每个做过分类项目的同学,在最终测试集上至少算四样东西:混淆矩阵、精确率、召回率、F1分数。代码很简单:

from sklearn.metrics import confusion_matrix, classification_report with torch.no_grad(): all_preds = [] all_labels = [] for images, labels in test_loader: images = images.to(device) outputs = model(images) _, predicted = torch.max(outputs, 1) all_preds.extend(predicted.cpu().tolist()) all_labels.extend(labels.tolist()) cm = confusion_matrix(all_labels, all_preds) print(cm) print(classification_report(all_labels, all_preds))

如果你发现破碎类(broken)的召回率只有85%,而精确率有97%,说明模型相当保守,宁可把一些完整玻璃误判成破碎也不愿意漏掉任何一块碎玻璃。有些场景这么做是对的(比如安检),有些场景不对(比如产线质检)。至于怎么调,逻辑也很清晰:如果你更看重召回率,可以降低分类阈值,不需要改网络结构。PyTorch里直接调整判断逻辑:

probs = torch.softmax(outputs, dim=1) predicted = (probs[:, 1] > 0.35).long() # 原本默认阈值是0.5

这个0.35就是“对破碎类更宽容”的阈值。别小看这么一点改动,在很多二分类项目里,它的效果可能比换模型还明显。

5.2 过拟合与欠拟合的识别

训练过程中最典型的两条曲线特征:

  • 训练集准确率非常高,验证集准确率上不去:过拟合。最直接的应对是增强数据多样性、增加权重衰减、或者early stopping。别急着换个更大的模型,那会让过拟合更严重。
  • 训练集和验证集准确率都很低:欠拟合。说明模型容量不够或者学习率不合适。先把学习率调大一点,如果还没变化,再考虑升级到ResNet50。

我的项目在第一次训练时就出现过拟合特征:训练集99.2%,验证集91.5%。当时我做了三件事:把RandomResizedCrop的比例范围调大,让模型看到更多局部细节;把权重衰减从1e-5加到1e-4;提前终止训练,把最优模型权重保存下来,而不是等30个epoch全部跑完。这三个操作加起来,验证集最终稳到了95%以上。

提示:训练时一定要在验证集准确率达到新高度时保存模型权重,推荐用torch.save(model.state_dict(), 'best_model.pth')。不要只保存最后一个epoch的模型,因为最后阶段模型往往已经开始过拟合了。

5.3 玻璃材质特有的误判模式与处理

我做这个项目时发现一个只有玻璃才有的经典问题:模型把背景当成了前景。玻璃是透明的,如果照片背景里有很多条纹状物体(比如百叶窗、拉丝不锈钢、砖墙缝隙),网络很可能学到“有条纹就是破碎”的假规律,在验证集上表现还不错,一到你自拍的背景干净的新图片上就翻车。

这就是为什么我坚持强调训练数据里背景要多样化。你要有意识地让训练集里的完整玻璃图片也出现各种复杂背景,让模型明白“背景even复杂也不是破碎”。如果你没有条件拍大量新图,退而求其次的办法是在预处理时做外部区域遮挡,也就是先用简单的分割或边缘检测把玻璃区域抠出来,再输入到网络里。这个方法在强反光场景下很实用,代价是增加了预处理复杂度。

另外,玻璃的镜面反射和眩光也坑了不少模型。同一块玻璃,某个角度拍出来完全透明无痕,另一角度拍出来一道亮斑正好横跨中间,像素级特征和裂纹非常相似。缓解办法是在训练图片里多加入带高光、带反光的完整玻璃样本,让网络学会区分高光和裂纹在形状连续性上的差异。裂纹一般是树状发散的,高光通常是条带状的,CNN理论上能学到这种区别,前提是你把这种样本喂给它。

6. 常见问题排查与调试实录速查

真正花时间的不是写代码,而是排查各种“看着正常、实际不对”的问题。下面这张表是我在这个项目以及类似图像分类项目里总结的高频问题清单,你遇到类似症状可以直接按这个方向排查。

现象可能原因解决思路
训练Loss不下降学习率过大或过小;数据预处理错误观察Loss初始值,如果不变则调大lr重新起步;检查归一化是否生效
准确率前期正常,某个epoch后骤降学习率过大导致震荡用余弦退火或ReduceLROnPlateau平滑学习率变化
训练集准确率高,验证集低过拟合增强数据、提高权重衰减、early stopping、保存最佳模型
训练和验证准确率都凑合,自拍图预测全错背景混淆,模型学了背景纹理增加背景多样的完整玻璃样本;考虑预处理抠出玻璃区域
程序内存不断上涨DataLoader子进程泄漏限制num_workers,在循环里显式调用torch.cuda.empty_cache()
显存不足(CUDA OOM)BatchSize过大或输入尺寸过大把batch_size降到8或16;用gradient accumulation模拟大batch
同一代码跑两次结果差异大随机种子未固定设置torch.manual_seed、random.seed、numpy.random.seed
数据加载极慢,GPU利用率低磁盘IO瓶颈或单进程读取采用image_dataset_from_directory前先转为内存读取或使用SSD;调高num_workers

第2条我多说两句。曾有一次训练到15个epoch时,验证集准确率从94%直接掉到88%,过一会又涨回来,这是典型的学习率阶梯下降时瞬间幅度过大引起的。我当时数据增强里还加了RandomRotation(30),旋转幅度有点大,裂纹在这种大角度旋转下会被裁剪截断,相当于给模型喂了带噪声的标签。后来把旋转幅度降到10度,验证集波动的幅度明显减小了。

还有一个容易踩的坑是类别文件命名不匹配。ImageFolder读数据的时候,类别标签是文件夹名称按字母序编码的。如果你的完整玻璃文件夹叫broken,破碎玻璃文件夹叫intact,编码出来intact是0、broken是1,看起来没什么,但如果你把文件夹重命名后再跑一次,所有标签就反过来了。我做项目时特意写了个脚本打乱预测结果反向验证标签映射,确认0对应完整、1对应破碎才放心。

关于梯度累积,如果你想用大batch训练但显存不够,可以这样:把目标batch设为64,显卡只能装16张,那就每4个batch累积一次梯度再更新参数。效果接近直接用64的batch,代码改动也不复杂:

accumulate_steps = 4 for i, (images, labels) in enumerate(train_loader): loss = criterion(model(images), labels) / accumulate_steps loss.backward() if (i + 1) % accumulate_steps == 0: optimizer.step() optimizer.zero_grad()

7. 从毕设到演示Demo:给模型做个简单的推理接口

如果你想让整个项目看起来更完整,或者想给自己一个更直观的验证手段,可以给训练好的模型包一层简单推理。不需要做前后端,一个脚本加几张测试图片就够了。

import torch from PIL import Image from torchvision import models, transforms model = models.resnet18(weights=None) model.fc = torch.nn.Linear(model.fc.in_features, 2) model.load_state_dict(torch.load('best_model.pth', map_location='cpu')) model.eval() transform = transforms.Compose([ transforms.Resize(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) classes = ["完整", "破碎"] img = Image.open("test.jpg").convert("RGB") input_tensor = transform(img).unsqueeze(0) with torch.no_grad(): output = model(input_tensor) prob = torch.softmax(output, dim=1)[0] pred_idx = torch.argmax(prob).item() print(f"预测结果: {classes[pred_idx]}, 置信度: {prob[pred_idx].item():.4f}")

再进一步的话,你可以用Tkinter或者Flask写一个上传图片返回结果的网页界面。别小看这个部分,答辩演示时往浏览器里拖一张图,模型实时给出“完整/破碎”和置信度,比你现场敲命令运行脚本直观得多。这个Demo代码不加多少工作量,但展示效果是一个量级的差别。如果时间充裕,还可以顺手试用一下ONNX导出,把模型导出成.onnx格式,然后可以脱离PyTorch环境推理,这在论文里可以作为一项“模型部署可行性验证”。

我做这个项目时,最后导出ONNX后顺便测了一下推理速度,同样的CPU环境下,PyTorch模型推理一张224×224的图大概需要40毫秒,转成ONNX后能压到20多毫秒。这个数据放进论文里的“效率分析”一节,会很有说服力。

8. 最后提醒:毕设答辩时的三个追问

因为我见过太多人栽在答辩环节,这里特意把评委最可能问的几个问题提前梳理一下,你在写论文和准备答辩前要能做到心里有数。

第一个追问:为什么用ResNet18而不是其他网络?你要回答的是:参数量适中、迁移学习预训练权重完善、残差结构缓解梯度消失、在千级数据集上不容易过拟合。同时你要能说出“V16参数比它大,MobileNet更适合落地点”这种对比,体现你做过功课。

第二个追问:数据集规模很小,模型是否可靠?这里你要展示自己的数据增强组合,以及验证集、测试集划分的独立性。你还可以强调,玻璃破碎识别本质上是纹理差异的二分类任务,其复杂度低于上千类的通用物体识别,所以千级数据量在这个约束下是合理的。

第三个追问:怎么避免误判?你要把前面说的混淆矩阵、阈值调整、背景干扰处理都讲一遍,最好现场打开一张在训练集中没有出现过的玻璃图片,让模型做推理演示。只要你能现场跑通一张图,基本这个环节就稳了。

我自己的体会是,这类图像分类的毕设就像搭积木,数据集是地基,网络结构是框架,训练调参是装修,部署Demo是摆件。你对每一层的熟悉程度,决定了答辩时你被追问之后能讲出多少细节。哪怕你最终只用了最朴素的ResNet18,只要能把为什么选它、数据怎么准备、踩过什么坑完全讲清楚,这就是一个合格的毕设,因为它体现的是你解决问题的完整思路,而不是背下来的代码。

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

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

立即咨询