每年到毕设选题季,都有不少同学跑来找我参谋,问的无非就是"什么题目好过""什么题目能学到东西""什么题目不会被老师打回来"。我观察下来,很多热门题目要么早就被做烂了,要么难度对本科生来说完全是天坑。今天想认真聊一个我觉得非常适合作为深度学习方向毕设的选题——基于Python和CNN卷积神经网络对墙体有无污渍进行识别。这个题目听起来不大,但麻雀虽小五脏俱全,从数据采集、模型训练到结果可视化、界面演示,该有的环节一个不少,难度曲线又特别适合学生阶段。不管是想做图像分类入门的同学,还是想拿一个能写出亮点论文的题目,都有值得拆解的地方。
这个选题的核心其实就一句话:输入一张墙体图片,模型判断墙面是干净还是存在污渍。听起来像是一个普通的二分类任务,但它背后牵扯到数据采集环境、标注标准、模型部署甚至边缘设备推理等一连串问题。更关键的是,这类视觉检测任务在实际场景里是真有需求的——物业巡检、室内装修验收、建筑质量评估,都会涉及墙面状态的自动检查。所以它不只是一个"作业",而是一个能讲出落地价值的题目。下面我就从这个选题的定位开始,把完整的设计思路、踩坑过程和实用经验一次性讲透。
1. 选题定位与整体设计思路拆解
1.1 任务定义与核心需求解析
用一句话描述这个毕设的输入输出:输入为墙体表面的RGB图像,输出为一个二分类标签——"有污渍"或"无污渍"。它本质上是图像分类任务,属于计算机视觉里最基础但也是最核心的问题类别。
但作为毕设,导师通常不会满足于"你跑通了一个分类模型"这种程度。你需要把任务拆得更细:
- 数据层面:如何采集/构建墙体污渍数据集,类别怎么定义,污渍的种类有哪些(颜料污渍、水渍、霉斑、划痕),不同光照下污渍表现的差异如何覆盖。
- 模型层面:选用什么网络结构,是自建一个简易CNN还是用预训练的模型迁移学习,参数量如何控制。
- 训练层面:损失函数选择、学习率策略、数据增强方案、防止过拟合的手段。
- 评估层面:不能只看准确率,还要看精确率、召回率、F1值,以及不同光照条件下的鲁棒性。
- 应用层面:是否做一个简单Demo,比如上传图片返回检测结果,或者一个可交互的界面。
这五层需求,正好对应了一篇完整毕业论文的章节结构。所以我说这个题目"好写",不只是模型本身好跑,而是它的逻辑非常顺,从问题定义一路到应用验证,每一层都有明确的内容可写。
1.2 为什么这是综合性价比很高的毕设题
先说结论:这个选题在难度、工作量、创新空间三个维度上达到了比较理想的平衡。
第一,难度适中。二分类任务是CNN最容易上手的场景,核心知识只涉及卷积、池化、全连接、激活函数这些基础模块。对基础薄弱的同学来说,从零写一个简单的CNN网络(比如三四个卷积层)也可以跑出不错的效果;对能力强的同学,可以引入ResNet、EfficientNet甚至注意力机制,做对比实验,拔高论文深度。
第二,数据获取成本低。相比医学影像、遥感图像这些需要特殊渠道的数据集,墙体污渍图片可以自己拍摄,也可以利用公开的室内场景数据集进行裁剪筛选。我自己带过的项目里,有人一周就收集了两三千张有效图片,做二分类完全够用。
第三,有清晰的工程落地抓手。做完模型训练后,可以接一个Web页面或者简单的桌面程序,把"识别哪张图片有污渍"做成可视化Demo。答辩的时候,现场拖一张图片进去,模型输出结果,效果远比只放PPT里的曲线要直观。
第四,扩展空间大。如果说"有没有污渍"太简单,那么可以自然延伸到"污渍类型识别"的多分类任务,或者进一步做污渍区域的检测定位(比如用目标检测框架框出污渍位置)。这意味着后期想加码、想出创新点,都有现成的路径。
一句话总结:这不是一个能拿诺贝尔奖的题目,但它是那种能让普通学生稳稳当当走完全流程、还能做出亮点展示的题目。对大部分人来说,"稳"比"炫"重要得多。
2. 数据准备:墙体污渍数据集的构建与预处理
2.1 数据采集的三种可行方案
这块是整个项目的第一道坎,也是我见过翻车最多的地方。很多同学上来就想着直接从网上爬图,结果图像分辨率、角度、光照乱七八糟,模型训练出来在测试集上准确率高,一到真实场景就崩。数据采集我建议按以下三种方式组合进行。
方案一:实地拍摄。拿手机在小区、教学楼、地下车库、旧厂房等场景里拍摄墙面照片。拍摄时注意覆盖不同时间段、不同朝向的光线条件。墙体污渍很多是局部出现的,所以除了拍整面墙,还要多拍污渍区域的特写。这个方案最贴近真实场景,但需要花时间和体力。我建议每天拍两三百张,持续一周,基本能把常见场景覆盖住。
方案二:公开数据集筛选。室内场景数据集(比如各类室内图像数据集)里含有大量墙面区域,可以截取其中的墙面部分作为"无污渍"样本,然后利用图像编辑软件人为添加污渍纹理生成"有污渍"样本。这个方案效率高,但要注意生成样本和真实样本之间可能存在分布差异。
方案三:网络图片收集。可以用搜索引擎按"墙体污渍""墙面发霉""墙上有污点"等关键词收集。网络图片质量参差不齐,而且版权问题需要留意,建议只用于补充少数稀缺场景,不作为主力。
我比较推荐的配比是:实地拍摄占60%,公开数据集裁剪占20%,网络补充占20%。这样既有真实感,又有数量保障。
2.2 数据标注规范:别凭感觉打标签
标注是决定模型上限的工作。污渍识别看似是二分类,但实际上标注标准不统一会带来灾难性的后果。我在实际项目中遇到过这样的情况:某同学把墙面裂纹也标成了"污渍",而另一位标注者不认为裂纹算污渍,同一种特征在两个类里反复横跳,导致模型训练时loss一直降不下来。
建立统一的标注规范很有必要。建议明确以下几点:
- 污渍定义:颜色异常、沉积物、水渍痕迹、霉斑均算污渍;墙面本身的纹理(如腻子抹痕、轻微颜色不均)不算污渍。
- 判定阈值:污渍面积小于整张图片面积的5%时,这张图仍归为无污渍类。避免因微小的斑点导致类别混乱。
- 边界案例处理:拿不准的图片单独存到一个"待定"文件夹,不要硬塞到某个类别里。等积累一定数量后统一讨论或剔除。
标注工具方面,不需要太复杂。纯分类任务只需要文件夹分类法:clean/和stained/文件夹,对应图片放进去即可。如果后续要做目标检测定位,再用labelImg之类的工具画框。
2.3 数据增强的维度与参数经验
神经网络最怕的就是数据量不够和场景单一。墙面图片有一个特点:背景变化不大,但光照和角度变化很大。所以数据增强的重点应该放在模拟光照变化和视角变化上。
我用得比较顺的增强策略如下:
| 增强方式 | 参数范围 | 目的 |
|---|---|---|
| 随机水平翻转 | 概率0.5 | 增加样本多样性,模拟不同拍摄方向 |
| 随机旋转 | ±15度 | 容忍拍摄角度偏差 |
| 亮度调整 | 0.8~1.2倍 | 模拟不同环境光照 |
| 对比度调整 | 0.8~1.3倍 | 模拟阴影与强光反差 |
| 随机裁剪 | 尺度0.85~1.0 | 学习局部特征,防止过拟合 |
| 高斯噪声 | 标准差0.01 | 增强鲁棒性 |
这里有一个很容易被忽视的点:增强操作要在训练集上做,验证集和测试集绝不能做随机增强。刚入门的同学经常犯这个错误——用同样的增强管线处理所有数据,结果验证集里出现了训练集的"变形版本",评估结果虚高,答辩时被老师一问就露馅。正确做法是,验证集和测试集只用resize和归一化,保持原始分布。
2.4 数据集划分与目录结构
二分类任务,我建议最终数据集规模至少达到:每类1500~3000张图片。小于这个数量,模型的泛化能力很难保证;多于这个数量,对训练时间的压力又会变大,本科毕设没必要堆太多。
划分比例遵循经典经验值:训练集70%、验证集20%、测试集10%。但需要注意的是,划分前最好按"拍摄场景"而不是"单张图片"来做分组。比如你从A小区拍了500张,那这500张应该整体划分到训练或验证或测试其中一个集合里,而不是打散混分。否则同一个环境的图像会同时出现在训练集和测试集,模型记住了环境特征而不是污渍特征,测试精度虚高。这一点在论文的实验设计里可以专门拿出来写,是你认真做数据的一个证据。
目录结构可以参考:
dataset/ ├── train/ │ ├── clean/ │ └── stained/ ├── val/ │ ├── clean/ │ └── stained/ └── test/ ├── clean/ └── stained/配合PyTorch的ImageFolder接口,这种目录结构可以直接加载,省去自己写Dataset类的大量时间。
3. 模型设计与训练配置
3.1 网络结构选择:从小模型起步,再逐步升级
墙体验污渍识别是典型的二分类任务,不需要一上来就上大模型。我的建议是分两步走:
第一步,搭建一个3~4层卷积的简易CNN作为baseline。结构大致是:卷积层+ReLU+最大池化重复两三轮,最后接全连接层和softmax输出。这个baseline的作用是跑通全流程,验证数据和标签没问题。网络小、训练速度快,便于快速迭代。
第二步,在baseline跑通后,引入更强的网络做对比实验。常用的有:
- ResNet18:经典残差网络,50层以内的版本参数量适中,是迁移学习的主力。
- MobileNetV3:轻量化网络,适合后续部署到边缘设备,答辩时讲"轻量化部署"是个亮点。
- EfficientNet:精确率更高,但对显存要求稍高。
自建CNN和预训练模型的对比实验,会让论文的"实验与结果分析"章节非常充实——你可以展示baseline效果、迁移学习效果、不同网络在精度和速度上的差异,这一组对比数据,就是实实在在的论文内容。
3.2 迁移学习到底怎么用才正确
迁移学习是这个项目里最容易被用错的地方。常见误区是:加载了预训练权重,然后全网络一起训练,学习率还设得很大。这样做要么把预训练学到的通用特征冲毁,要么训练过程震荡严重。
我实测下来比较稳的方案是:
- 加载ImageNet预训练权重(比如ResNet18),锁定前几个阶段(即冻结backbone的前几层参数),只训练最后几层和分类头。
- 先用较小的学习率(1e-4)训练若干个epoch,让分类头收敛。
- 然后解冻部分backbone,用更小的学习率(1e-5)做全网络微调。
冻结层数怎么把握?一个可以参考的经验是:数据量越小,冻结的层越多;数据和ImageNet场景差异越大,需要解冻的层越多。墙体污渍图像的风格和自然图像差异不算特别大,冻结到网络的一半深度是一个合理的起点。
训练代码里冻结参数的写法如下:
import torch import torchvision.models as models model = models.resnet18(pretrained=True) # 冻结前两层的参数 freeze_layers = [model.conv1, model.bn1, model.layer1] for layer in freeze_layers: for param in layer.parameters(): param.requires_grad = False # 替换分类头,二分类输出维度为2 model.fc = torch.nn.Linear(model.fc.in_features, 2)3.3 训练超参数配置的实践经验
下面这套配置是我做了多轮实验后认为在墙体污渍识别任务上最稳的起点,同学们可以直接抄作业:
| 超参数 | 推荐值 | 说明 |
|---|---|---|
| 批量大小 batch_size | 32或64 | 显存不够就用32,注意批量大小影响BN层统计 |
| 初始学习率 | 1e-3(自建CNN)/ 1e-4(迁移学习) | 迁移学习要用更小的学习率 |
| 学习率调整 | 每10个epoch降为原来的0.1倍 | 阶梯下降比余弦退火更好调参 |
| 优化器 | Adam(β1=0.9, β2=0.999) | 收敛快,适合学生项目 |
| 损失函数 | 交叉熵损失CrossEntropyLoss | 二分类默认选择 |
| 训练轮数 | 30~50轮 | 配合早停机制,防止过拟合 |
| 图像尺寸 | 224×224 | 与ImageNet预训练模型的输入一致 |
关于batch_size有个细节值得注意:如果用了BatchNorm层,batch_size别设太小(不要低于16),否则BN层统计的均值和方差不稳定,模型精度波动会很大。
训练时记得保存最优模型而非最后一个epoch的模型。推荐按验证集准确率作为保存标准:
best_acc = 0.0 for epoch in range(num_epochs): # 训练和验证代码省略 val_acc = evaluate(model, val_loader) if val_acc > best_acc: best_acc = val_acc torch.save(model.state_dict(), 'best_model.pth') print(f'Epoch {epoch}: save best model, acc={val_acc:.4f}')很多人一路训练完,最后发现第39轮的验证精度不如第22轮,如果只保存最后一个epoch的模型,就亏了。这个细节在论文里也可以写一笔,体现工程思维。
4. 训练实战:高频踩坑点与排查思路
4.1 过拟合:验证集精度上不去怎么处理
墙体图像有个特性:墙面本身的纹理和颜色比较统一,如果数据集场景单一,模型很容易"死记硬背"某个环境的墙面特征,而不是真正学到"污渍"这个概念。表现就是训练集精度冲到98%以上,验证集精度却卡在80%以下。
处理过拟合,我按有效性排序的建议是:
- 加大数据增强力度,尤其是随机裁剪和色彩抖动。墙体污渍对位置不敏感(污渍出现在哪里都算有污渍),所以随机裁剪很适合这个任务。
- 降低模型复杂度。如果自建CNN已经用了五六层卷积还过拟合,考虑减少通道数而不是继续加深。
- 加Dropout。在全连接层前面加一个
nn.Dropout(p=0.5),简单有效。 - 早停。当验证集精度连续5~8个epoch不再提升时,停止训练并回退到最佳模型。
需要特别提醒的是:不要用测试集去反复验证调参。很多同学调参时习惯用测试集看效果,调几轮之后测试集实际上变成了第二个验证集,最终报告的数字已经"被污染"了。正确做法是只用验证集调参,测试集只在最后评估时碰一次。
4.2 类别不平衡:污渍样本明显偏少时怎么办
在实际采集过程中,"无污渍"样本很容易拍得多(满墙都是白的),"有污渍"样本反而难凑。如果无污渍样本数是有污渍的3倍以上,模型训练时会倾向于把所有图片都判成无污渍,因为这样整体准确率也不低。
解决类别不平衡的常用手段:
- 加权损失函数:在交叉熵损失里给少数类更大的权重。PyTorch里的
torch.nn.CrossEntropyLoss(weight=class_weights)可以直接实现,权重按"样本数反比"计算。 - 过采样:对少数类做带放回的抽样,让每个batch里两类样本数量大致平衡。
- 数据增强补齐:对有污渍样本做更强的增强,比如旋转、翻转各来一遍,生成更多变体。
我在项目里做过对比,加权损失函数效果立竿见影,F1值能从0.72升到0.85以上。缺点是有时候会矫枉过正,个别无污渍图片被误判为有污渍。这时候可以把权重比从反比调整为1.5倍反比,做一点妥协。
4.3 训练不收敛与损失异常排查
如果遭遇loss不降、甚至越涨越高的情况,不要急着调学习率,先从下面几条按顺序排查:
- 标签和数据是否对齐。Windows系统下用OpenCV读取图片时注意路径中文问题;
ImageFolder自动按文件夹顺序分配标签,确认0对应"clean"、1对应"stained"。 - 预训练模型的归一化方式。ImageNet预训练模型要求输入按RGB的mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]做标准化。这个mean和std是在千万张图上统计出来的,跟普通场景的mean差异很大。漏做归一化,迁移学习效果会大打折扣。
- 学习率设置过大的"梯度爆炸"。特征是loss出现NaN或者训练初期就在高位震荡。把学习率降到1e-5试试,如果恢复正常就说明初始学习率太高。
这里顺便说一句,每次跑实验前先做一个过拟合单批测试——只拿一个batch的数据反复训练,看loss能不能降到接近0。如果连一个batch都拟合不了,说明代码逻辑或数据加载有问题,这时候先别启动完整训练,不然纯属浪费时间。
5. 模型评估与可视化Demo搭建
5.1 评估指标:别只盯准确率
二分类任务里准确率是最直观的指标,但它有个坑:当类别不平衡时(我们前面提到的情况),准确率会虚高。一个全部预测为"无污渍"的模型,在无污渍样本占80%的数据集上也能拿到80%的准确率,看起来不差,实际毫无用处。
所以至少要看以下四个指标:
- 准确率Accuracy:所有预测正确的比例。
- 精确率Precision:预测为有污渍的样本里,真正有污渍的比例。精确率低了意味着太多误报。
- 召回率Recall:真实有污渍的样本里,被成功找出来的比例。召回率低了意味着漏报严重。
- F1分数:精确率和召回率的调和平均,类别不平衡场景下比准确率有参考价值得多。
此外,混淆矩阵一定要画。它直观显示哪些类别容易被混淆,答辩老师看一眼就能抓住你实验的亮点或短板。
精确率和召回率是一对需要权衡的指标:如果把判定阈值调低,模型会倾向于把更多边界样本判为有污渍,召回率上升但精确率下降。具体怎么取舍,取决于应用场景——如果是质量验收场景,漏掉污渍的成本更高,那宁可多误报也要保召回率。这个权衡讨论写进论文里,是很加分的。
5.2 特征与结果可视化
CNN被吐槽为"黑盒",所以做可视化是提升论文说服力、答辩讲解效果的最好手段。两种可视化方式我建议必做:
第一种是训练过程曲线——loss曲线和accuracy曲线放在同一张图里,分别画训练集和验证集。这张图能直观呈现模型收敛趋势和过拟合程度,是论文实验章节的标配。
第二种是Grad-CAM类激活热力图。它能把模型"关注图片的哪个区域"以热力图形式叠加在原图上。对于污渍识别来说,理想的热力图应该集中在污渍区域。如果模型关注到了踢脚线、墙角阴影这些无关位置,说明模型学偏了,需要调整数据或模型结构。讲解Grad-CAM本身也是一道经典的答辩题目,提前准备好原理说明,比现场支支吾吾强太多。
轻量实现Grad-CAM的库有很多,pytorch-grad-cam这类开源库可以直接调用,几行代码就能出热力图。但答辩时老师问起原理,你要能说出"通过最后一个卷积层的梯度加权平均来生成定位图"这个核心思路。
5.3 五分钟搭一个可交互演示界面
毕设有一个可视化的交互界面,答辩效果完全不同。这里不需要做多复杂的前端,用Python生态里最简单的方案就行。
我推荐使用Streamlit,几行命令就能启动一个网页应用,界面布局不需要写HTML:
import streamlit as st from PIL import Image import torch from torchvision import transforms # 加载模型 model = torch.load('best_model.pth', map_location='cuda' if torch.cuda.is_available() else 'cpu') model.eval() st.title('墙体污渍识别系统') uploaded = st.file_uploader('上传墙面图片', type=['jpg', 'png', 'jpeg']) if uploaded is not None: img = Image.open(uploaded).convert('RGB') st.image(img, caption='原始图片', use_column_width=True) # 预处理 transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) input_tensor = transform(img).unsqueeze(0) # 推理 with torch.no_grad(): output = model(input_tensor) prob = torch.softmax(output, dim=1) confidence, predicted = torch.max(prob, 1) label = '有污渍' if predicted.item() == 1 else '无污渍' st.write(f'识别结果:**{label}**') st.write(f'置信度:{confidence.item() * 100:.2f}%')运行streamlit run app.py,浏览器里就能拖拽上传图片了。这个Demo除了答辩演示,还能让你自己快速收集"模型在真实场景下表现如何"的反馈,非常实用。
6. 论文实验设计与答辩加分细节
6.1 实验对比方案怎么设计
毕设论文的实验部分,最忌平铺直叙只放一个模型结果。哪怕你的模型效果一般,只要实验设计完整,也能写出亮点。我建议至少设计以下三组对比:
对比一:不同网络结构的性能对比。自建简易CNN、ResNet18、MobileNetV3三者在同一数据集上的准确率、精确率、召回率、F1、参数量、推理耗时。这组对比要传达的核心是:轻量网络在保证精度的同时大幅减少算力消耗。
对比二:迁移学习与从零训练的对比。同一个ResNet18,一组加载ImageNet预训练权重微调,一组随机初始化从零训练。通常迁移学习在精度和收敛速度上都明显占优,这个对比可以证明"知识迁移在数据量有限的场景下的价值"。
对比三:数据增强消融实验。完整增强策略、无增强、只有基础增强三组训练结果。这一组最能体现你对数据处理的思考,也是很多学生论文里缺失的部分。
这三组对比做下来,实验章节至少能写出五六个结果表格加六七个可视化图表,篇幅和内容都能撑起来,答辩时讲实验设计思路也有东西可说。
6.2 答辩展示环节的三个关键准备
答辩不同于论文写作,重点在"讲清楚"和"答得上"。结合我评审项目的经验,三点建议供参考:
一是准备一个端到端的现场演示流程。不要现场写代码,提前把Streamlit应用启动好,图片路径准备好,鼠标点两下就能出结果。演示环节用真实拍摄的、不在训练集里的图片进行验证,效果最有说服力。
二是把"数据怎么来的"讲明白,这是最容易加分也最容易被忽略的点。明确交代数据来源构成、标注规范、数据清洗过程,会让评委感受到你对数据质量的重视。很多项目失败在数据,而非模型,你能主动聊数据,反而体现了工程经验。
三是准备好一两个"模型失败案例"。比如某张图片里污渍与阴影颜色接近,模型误判了。主动讲失败案例比藏着掖着要好得多,展示你会分析失败原因、知道局限在哪里,这是研究生才有的思维层次。比如你可以这样讲:"这张图里的黑色污渍与墙角阴影颜色几乎一致,模型在光照边界处出现混淆,后续可以考虑引入纹理特征或增大此类样本量来改进。"这一句话,回答了老师可能追问的很多问题。
写在最后的经验分享
这类图像分类项目的全部流程我完整走过几遍,最深的体会是:决定项目质量的往往不是模型有多新,而是数据有多扎实。同样的ResNet18,数据组织得好、增强到位、标签清晰,效果可以甩开那些随意爬图、随手标注的项目几条街。这个选题也是我眼中少数能把"数据工程"作为论文核心亮点的方向之一。
再分享一个最后阶段的小技巧:确定好模型后,留出一两天时间,专门用没见过的手机实拍照片去测试,把误判的案例收集起来归一下类。这个动作一方面能帮你找到模型的真实短板,另一方面也为你写"总结与展望"章节提供了真实素材。比如你发现了夜间灯光下污渍识别率明显下降,那"未来可以引入多光照条件扩充数据集"就不再是空话,而是有实测数据支撑的方向。论文里所有的展望都应该来自你实际发现的问题,而不是模板化的套话。这个细节,很多同学做不到,但做到了,整个项目的完成度就完全不一样了。