ARTICLE DETAIL

资讯详情

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

基于深度学习的舌苔检测毕设全流程:数据集构建、YOLOv8训练到部署

基于深度学习的舌苔检测毕设全流程:数据集构建、YOLOv8训练到部署 简介基于深度学习的舌苔检测毕业设计完整留档面向人工智能、深度学习方向的本科生或研究生适合作为医学图像处理、目标检测类课题的参考项目。包含从数据标注、模型训练到结果可视化的全流程代码。压缩包共109个文件以Python脚本py/pyc、模型权重pth、配置文件json和运行界面ui为主另有部分图片样本及TensorBoard训练日志整体约105.39MB。已有829人浏览学习被用于毕设参考和实际项目复现。资源内包含训练好的模型权重、完整Python源码及多次实验的TensorBoard事件文件便于对比训练曲线与调参记录同时提供UI界面和文档说明可帮助快速理解系统架构并在此基础上进行改进对于需要完成类似医学图像检测课题的同学具有较强参考价值。1. 基于深度学习的舌苔检测毕设留档 zip 里到底该有什么“基于深度学习的舌苔检测毕设 留档.zip”这类标题在计算机视觉方向的毕设里出现频率不低。它本质上不是一个新算法题而是把通用目标检测迁移到中医舌诊场景输入一张自然光下拍的舌头照片模型在画面里定位舌头区域再判断舌苔的类型、面积或者有无。这个题目能解决的真实需求是健康管理 App、中医体质辨识和远程问诊里的自动初筛。适合选它的人是手里有一段完整毕设周期、希望用一个看得见效果又能讲清原理的深度学习流程来交付课题的读者。它比通用目标检测好做在指标明确、样本可自采难点则集中在数据分布、标注口径和真实拍摄条件上。2. 先定数据再选模型舌苔检测的数据规范与基线骨架不管你是打算解压别人留档的 zip 做二次开发还是准备从零攒一套自己的工程动手前都得先把三件事定下来数据从哪来、标注成什么格式、基线模型用什么。这三件事互相牵连顺序错了后面全是返工。2.1 舌苔影像数据从哪来三个能落地的采集路径舌苔检测不像通用物体检测那样有海量公开数据可以直接下载。常见做法是三条路并行。第一条路是找公开的舌象相关图像集。中医舌诊数字化有一些研究性质的图片散见于论文附带数据和少数开放平台但大多没有统一标注需要自己清洗。这类数据的优点是拍摄条件相对规范缺点是数量少、类别分布偏科直接当训练集容易让模型过拟合到某一种打光风格上。第二条路是自采。找实验室、宿舍或者家里的人帮忙拍手机摄像头就够用。拍摄时要求自然伸出舌头、嘴巴张开、光线均匀每张图尽量只保留嘴部区域。我一般会按“正常舌苔、厚腻苔、黄苔、少苔/无苔”四个粗类去收每个类至少收 150 到 200 张。自采数据最大的价值是验证集和测试集可以单独留一批“真实手机照”用来证明模型没在公开图上过拟合。第三条路是爬取公开图片平台但这一步清洗成本很高。网络图普遍存在分辨率混乱、脸部被裁切、舌头区域占比过小的问题而且版权和质量都没法保证。我的建议是爬来的图只做预训练风格的补充或者困难样本挖掘不要让它占训练集的太大部分。数据量不需要贪多。做舌苔检测这种单目标、背景相对固定的任务500 到 800 张精标图足够把基线跑起来后续通过增强和自采扩充到 1500 张左右效果会有明显提升。核心原则是“宁缺毋滥”一张框得不干净的图比十张干净图的破坏力还大。2.2 标注格式选 VOC 还是 COCO影响后面所有代码标注格式直接决定你用哪套工具、跑哪个框架、后处理要不要写转换脚本。目前在舌苔检测里最常见的是两种VOC 的 XML 格式和 COCO 的 JSON 格式。格式视觉标注工具对 YOLO 系列的适配适合场景VOC XMLLabelImg需要转成 YOLO 的 txt数据量小、想逐文件检查COCO JSONlabelme、X-AnyLabeling很多框架原生支持数据量大、需要统一管理毕设场景我一般推荐直接标 COCO因为 YOLO 系的框架对 COCO 的适配最顺后处理也少。但很多同学早期已经用 LabelImg 标了一批 XML这时候不要手工重标写一个脚本把 XML 转成 YOLO 训练用的 txt 即可。下面是常用的转换脚本按“类别名到 id 的映射 归一化坐标”输出。import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, class_map): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) out_lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in class_map: continue # 跳过不在类别表里的标签 bnd obj.find(bndbox) x1 float(bnd.find(xmin).text) y1 float(bnd.find(ymin).text) x2 float(bnd.find(xmax).text) y2 float(bnd.find(ymax).text) x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h box_w (x2 - x1) / img_w box_h (y2 - y1) / img_h out_lines.append( f{class_map[cls]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f} ) out_file os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(out_file, w, encodingutf-8) as f: f.write(\n.join(out_lines)) if __name__ __main__: # 类别名需要和标注时保持一致 class_map {coating: 0, no_coating: 1} voc_to_yolo(annotations/001.xml, labels/train, class_map)这个脚本的逻辑不复杂先把 XML 里的绝对坐标读出来除以图片宽高归一化再按“类别 id 中心点 x 中心点 y 宽度 高度”的格式写进 txt。需要注意class_map必须和训练配置里的names顺序一致否则训练出来的预测结果会张冠李戴。另外YOLO 要求 txt 文件和对应图片同名转换时不要改动文件名主体只替换扩展名。2.3 基线模型选型YOLOv8 还是 RTMDet数据定完后模型选型要结合你的机器和毕设周期。很多同学一上来就想堆最重的模型其实舌苔检测的难点不在“网络不够深”而在“数据噪声太大”。如果机器只有一张 8GB 显存的显卡首选 YOLOv8s 或者 YOLO11n。这两个模型在单卡上训练速度快官方预训练权重对特征提取的初始化帮助很大能让你在两周内看到可演示的结果。RTMDet 的精度上限稍高尤其在多尺度目标上更强但它依赖 mmdetection 那套环境版本兼容问题多光配环境就能耗掉好几天。我自己的习惯是毕设阶段用 YOLOv8s 跑通全流程时间富余再去试 RTMDet 或者 YOLO-seg 做对比实验。模型参数量级显存压力上手难度舌苔检测适配度YOLOv8n小低低能跑轻微漏检YOLOv8s中低低推荐基线RTMDet-s中中高上限略高环境折腾还要说一个容易被忽视的点舌苔检测的“目标”到底框什么。有人框整个舌头有人只框舌苔覆盖区域两者任务难度完全不一样。框中舌头的做法更稳因为舌头边界相对清晰只框舌苔区域则要面对舌苔和舌体颜色接近、边界模糊的问题模型容易学成“纹理分割”。建议基线阶段只做“舌头区域检测 粗分类”把“舌苔面积占比”这类细粒度任务放到第 6 章讲的进阶方向里。3. 用 YOLOv8 在本地跑通舌苔检测的最小训练流程环境、命令与收敛判断选好基线和数据下面进入最容易被卡住的环节本地环境配置和训练命令。舌苔检测的输入尺寸不用太大640 分辨率完全够用关键是数据目录写对、训练参数别太激进。3.1 环境准备与数据目录组织环境方面最省心的组合是 Python 3.10 ultralytics。下面是创建环境的常用命令。conda create -n tongue python3.10 -y conda activate tongue pip install ultralytics # 验证安装是否成功能正常输出版本号即可 python -c import ultralytics; print(ultralytics.__version__)这里不需要手动装 PyTorch 和 CUDAultralytics 会拉起当前环境需要的 torch 版本。如果你机器里有 NVIDIA 显卡建议单独确认一下torch.cuda.is_available()是不是 True否则训练会默认走 CPU速度慢到怀疑人生。数据目录我建议固定成下面的结构这是 YOLO 系最不容易出错的布局。data/tongue/ ├── images/ │ ├── train/ # 600 张 │ └── val/ # 80 张 ├── labels/ │ ├── train/ # 和 images/train 同名 │ └── val/ # 和 images/val 同名 └── tongue.yaml # 数据集配置目录结构的坑在于YOLO 训练时只认 images 和 labels 的对应关系不认子文件夹里的类别归档。也就是说你不能在 labels/train 里建“coating”“no_coating”子目录所有 txt 必须平铺在 labels/train 下txt 内容里的第一个数字才是类别 id。3.2 训练命令与关键参数轮数、批次、分辨率这么定训练前先写好数据集配置文件 tongue.yaml内容如下。# data/tongue/tongue.yaml path: /home/user/data/tongue # 改成你的实际路径 train: images/train val: images/val nc: 2 names: 0: coating 1: no_coating配置文件的path是根目录train和val是相对于根目录的子路径。这里容易踩的坑是把train写成绝对路径或者把图片后缀也写进去都会导致训练时报 Dataset not found。nc是类别数必须和标签文件里的最大类别 id 加一对上否则推理时类别名是空的。然后执行训练这是最常用的最小命令。yolo detect train \ datadata/tongue/tongue.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ device0 \ patience20 \ cacheTrue参数解释如下modelyolov8s.pt表示加载官方预训练权重继续训练epochs150是最大训练轮数配合patience20表示验证集指标连续 20 轮不提升就提前停止imgsz640让图片统一缩放到 640×640batch16在 8GB 显存下基本能跑动如果爆显存就先降到 8不要改imgsz。cacheTrue是把数据集提前缓存到内存里第一次跑会慢几分钟之后每轮训练速度会明显变快。3.3 一轮训练后怎么判断有没有收敛训练跑完去runs/detect/train目录下看几个关键文件。results.png里画了 box_loss、cls_loss、dfl_loss 三条曲线如果三条曲线都趋于平坦且没有在最后几轮突然抬头说明训练基本收敛。confusion_matrix.png能直观看到舌苔类别之间有没有互相混淆如果“coating”被大量错分成“no_coating”说明类别不平衡或者标注不一致。还有一个文件容易忽略val_batch0_pred.jpg。这是验证集第一张图在训练结束后的预测结果框画得准不准、有没有漏框肉眼看比指标更直接。我每次训练完都会先翻这张图如果明显发现“框到了嘴唇上”这种低级错误说明训练集里混了不合格标注。后续不再加轮数而是回过去清洗数据。4. 舌苔检测避坑五个翻车场景与对应解法舌苔检测的翻车点和通用检测不完全一样。下面这几条都是做这类题目时反复出现的坑按“现象→原因→解决”列出来希望能帮你少走弯路。4.1 口腔暗光环境下漏检严重现象模型在打光均匀的训练图上表现很好但拿到真实手机拍摄、暗光或者逆光的图片舌头区域直接漏检。原因训练集太“干净”全是受控光照下拍摄的图片模型学到的是亮度分布而不是舌头的结构特征。深度学习模型在这种任务上很容易把亮度当捷径暗光对它来说是一种没见过的分布。解决在训练集里加入曝光扰动数据增强并专门采一批暗光照片放进训练集。YOLOv8 默认的增强策略没有很强的亮度扰动可以在训练命令里加上hsv_v0.5这类参数或者直接把暗光图少量复制进训练集让模型见过这种分布。4.2 苔色类别严重不均导致过拟合现象黄苔样本很多、少苔样本很少训练出的模型对少苔图片基本都预测成多数类精确率很高但召回率很低。原因类别不平衡是老问题舌象数据里不同苔质出现频率天然不均教练和自采都难做到均衡。解决先做数据层面的多采样把少类图片复制几份放入训练集或者用拼接增强的方式让少类样本以不同背景出现。如果数据实在补不齐再考虑在损失函数里给少数类加权。对毕设来说用简单的“按类别比例重复少类样本”就够了不需要上复杂算法答辩时也能讲清楚。4.3 随机旋转增强把舌头转成“横着长”现象训练集里出现大量旋转 90 度、舌头横在画面里的样本真实场景里几乎不可能出现模型收敛变慢。原因YOLO 默认的数据增强里有随机旋转对通用物体问题不大但舌头是人体器官姿态有天然的物理约束。旋转角度太大相当于在制造噪声。解决在训练配置里限制旋转角度。用 ultralytics 的话可以直接构造一个自定义的data_augment参数或者简单一点在训练前对图片做水平翻转和 ±15 度以内的随机旋转不开 90 度级别的增强。本质上就是让增强后的样本仍然符合舌头的真实拍摄姿态。4.4 验证集指标最好真实测试却一塌糊涂现象训练时 val 集 mAP50 到了 0.9 以上但拿自己预留的一批真实手机照一测漏检和误检都很严重。原因验证集和训练集来自同一个数据源分布太接近。代码里随机拆验证集时同一批人的不同照片可能同时进了训练集和验证集模型相当于见过答案。解决采集时按“人”划分数据同一个人的所有照片要么全进训练集要么全进验证集不要随机打乱。最后再单独留 30 到 50 张完全没参与过训练和验证的照片做测试集。这个坑在舌苔检测里尤其常见因为数据集往往只有几百张随机划分很容易泄题。4.5 推理时预处理和训练不一致现象训练时 mAP 正常导出模型后单独写 Python 脚本推理检测框整体偏移或者小目标丢失。原因YOLO 训练和推理时默认会对图片做 letterbox 等比缩放同时把边长补到 640 的倍数。自己写 OpenCV 推理时如果直接cv2.resize到 640×640会破坏图片长宽比导致框坐标偏斜。解决推理时不要自己 resize直接用 ultralytics 的model.predict()接口它会自动做 letterbox。如果非要手写预处理需要把 letterbox 的 padding 参数记录下来在还原框坐标时减回去。这类问题属于典型的“训练一条龙部署一条虫”排查时先打印预处理后的图片尺寸和模型输入尺寸是否一致。5. 把留档 zip 变成可演示的交付ONNX 导出、推理脚本与界面毕设交付和实验室 Demo 的区别在于你必须让导师或者评审老师能直接看到效果。这一步的核心是导出模型、写一个稳定推理脚本、再套一个简单界面。很多项目 zip 解压后跑不起来问题都出在交付物组织混乱。5.1 导出 ONNX把 PyTorch 权重变成通用模型文件训练完的.pt权重只能在 PyTorch 环境里跑评审老师的电脑上不一定有同样的环境。常见做法是用 ultralytics 自带的导出命令把权重转成 ONNX 格式。yolo export \ modelruns/detect/train/weights/best.pt \ formatonnx \ opset12 \ dynamicTrue \ simplifyTrueformatonnx指定导出格式opset12是 ONNX 算子版本一般不用调太高。simplifyTrue会调用 onnxsim 对图结构做简化去掉一些冗余节点推理时能快一点点。dynamicTrue允许动态输入尺寸但如果你后面要接 TensorRT 或者写 C 部署动态尺寸会带来额外麻烦导出时建议先固定 640×640等部署流程跑通了再考虑动态。导出后同一个目录下会生成best.onnx。注意这个文件不包含类别名推理时需要自己维护一份类别映射表这个信息容易在留档 zip 里丢建议写在 README 里。5.2 最小推理脚本单张图片和视频流都能跑ONNX 模型既可以用 ONNX Runtime 推理也可以直接用 ultralytics 的接口加载。用 ultralytics 的好处是它能自动完成 letterbox 预处理和坐标还原下面是单张图片推理的脚本骨架。from ultralytics import YOLO import cv2 # ONNX 模型不内嵌类别名这里手动指定 model YOLO(runs/detect/train/weights/best.onnx) model.names {0: coating, 1: no_coating} img cv2.imread(test_tongue.jpg) results model(img, conf0.35, iou0.45, imgsz640) for r in results: for box in r.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() cls int(box.cls[0]) conf float(box.conf[0]) label f{model.names[cls]} {conf:.2f} cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(img, label, (int(x1), int(y1) - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imwrite(output.jpg, img)代码里conf0.35表示置信度低于 0.35 的框会被过滤掉iou0.45是 NMS 的 IoU 阈值。这两个参数在舌苔检测里值得单独调如果漏检多就把conf降到 0.25如果同一个舌头出现三四个重叠框就把iou调到 0.5。对于视频流把循环里的图片换成摄像头帧每处理一帧显示结果即可代码思路完全一样。5.3 用 Gradio 快速搭一个演示界面如果不想写太多前端代码Gradio 是性价比最高的方案。一个函数调用就能生成网页端演示界面本地跑起来后局域网内也能访问答辩时直接现场截图或者录屏都可以。import gradio as gr from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.onnx) model.names {0: coating, 1: no_coating} def detect(img): results model(img, conf0.35, iou0.45) # results[0].plot() 会生成带标注框的图像 return results[0].plot() gr.Interface( fndetect, inputsgr.Image(typenumpy), outputsgr.Image(typenumpy), title基于深度学习的舌苔检测演示 ).launch()gr.Image(typenumpy)会把上传的图片转成 NumPy 数组传给函数results[0].plot()返回的是画好框的 BGR 图像Gradio 会对输出做格式转换。这个界面虽然简陋但足够支撑“现场上传一张图、立刻看到检测结果”的演示需求。如果你的电脑没有显卡ONNX Runtime 的 CPU 推理在这个场景也够用单张图一般在零点几秒到一两秒之间。到这里一个完整的交付 zip 应该包含训练权重 best.pt、导出的 best.onnx、推理脚本、Gradio 演示代码、数据配置 yaml 以及一份说明文档。文档里写清楚 Python 版本、依赖安装命令和启动方式别把依赖版本藏在一堆代码里这是留档 zip 最有价值的“后悔药”。6. 答辩前的最后验证指标口径、误检归因与三个进阶点答辩前我习惯做三件事重新算一遍测试集指标、给误检案例归因、想清楚一个能讲明白的进阶方向。这三件事都做完这个题目才真正算闭环。第一件事指标口径。舌苔检测里最常用的是 mAP50 和 mAP50-95。mAP50 只要求预测框和真实框的 IoU 超过 0.5 就算命中对毕设足够友好mAP50-95 则考核更严框稍微偏一点就会掉分。如果 mAP50 高但 mAP50-95 低说明模型“大概能框中舌头但边界不精细”这在舌苔区域边缘模糊的情况下很常见不用慌能解释清楚就是加分点。另外建议报一组 Precision 和 Recall医疗辅助场景里漏检比错检更危险所以我会倾向于把置信度阈值调低一点保 Recall。第二件事误检归因。把测试集里所有错图分成三类漏框、框错位置、框得过大或过小。漏框多一般说明目标太小或者对比度低框错位置很可能混入了“照到牙齿”的坏标注框得过小则往往是因为标注时只框了舌苔中心区没有包含整个舌头。这类归因在答辩时特别有说服力比单说一句“模型精度达到 0.9”扎实得多。第三件事进阶方向。舌苔检测最自然的三个延伸点是用 YOLO-seg 做舌苔像素级分割直接输出舌苔和舌体的面积比把苔色细分为黄、白、灰等多个类别配合类别加权损失训练对输出置信度做校准让置信度数字更接近真实概率。你不用全做挑一个做一个对比实验就行。我的教训是别把进阶点塞进同一个模型里比如一边加分割一边加细分类最后指标波动时根本说不清是哪个改动起了作用。做这个题目最有价值的收获是一套从数据采集、标注、训练到交付的完整链路。你自己亲手跑完一遍下次遇到任何检测类题目都不会慌。希望这篇记录能帮到你也祝你的舌苔检测毕设一次跑通。本文还有配套的精品资源点击获取
返回列表