资讯详情 基于YOLO的舌象诊断系统:从数据标注到推理部署全流程实战
📅 2026/10/11 20:42:52
简介这份资源面向计算机、人工智能及中医药信息化方向的本科生与研究生提供一套可直接用于毕业设计、期末大作业或课程设计的舌象诊断系统完整方案。项目以Python为开发语言结合YOLO系列深度学习目标检测算法实现舌象图像的采集、识别与诊断分析界面美观、操作简单、功能完善适合具备一定Python基础但希望快速完成高质量毕设的学习者。压缩包共184个文件约42.67MB其中包含54个py源码文件、61张jpg舌象样本图片、40个pyc编译文件、14个txt说明、7个json配置、2个ui界面文件及2个md文档另附docx学习路线与png、ttf等辅助素材代码注释详尽新手也能看懂。目前已有390人学习下载项目经严格调试个人手打98分并获导师认可。下载后简单部署即可运行读者可获得完整源码、数据集、界面文件与文档说明快速搭建可演示的舌象诊断系统并据此撰写论文与答辩材料。1. 从一张舌头照片到一份诊断报告这套 YOLO 舌象系统到底能跑出什么去年帮一个做中医数字化的朋友看项目他手里攒了三千多张舌象照片想做个自动分类的小工具结果卡在标注格式转换上整整两周。这不是个例——舌象诊断这类任务难点从来不在模型本身而在数据怎么组织、类别怎么定义、推理结果怎么落到业务上。这套基于 Python YOLO 的舌象诊断系统源码核心就是解决这条链路从舌象图片输入到舌质、舌苔、舌形等特征的检测与分类输出。它适合两类人一是做毕设需要完整可跑工程的学生二是想快速验证中医图像识别可行性的开发者。数据集、训练脚本、推理接口、文档说明都在包里不是那种只给一个模型权重的半成品。下面我按实际拆包和复现的顺序把关键环节和容易翻车的地方讲清楚。2. 拆开资源包先看什么目录结构与技术栈的真实构成2.1 拿到源码先确认三件事框架版本、数据格式、权重文件很多人下载完压缩包第一反应是直接python train.py然后报一堆错。我的习惯是先花十分钟把目录扫一遍。这套工程常见结构是datasets/、models/、utils/、runs/、weights/加几个入口脚本。先确认三件事第一requirements.txt里 torch 和 ultralytics 的版本YOLOv5 和 YOLOv8 的 API 差异很大混用必炸第二datasets/下是 YOLO 格式的images/labels/还是 COCO JSON这决定你要不要写转换脚本第三weights/里有没有预训练权重没有的话训练从零开始小数据集上效果会很差。# 查看工程目录结构重点关注数据与权重 find . -maxdepth 2 -type d | sort # 输出示例 # ./datasets # ./datasets/images/train # ./datasets/images/val # ./datasets/labels/train # ./datasets/labels/val # ./models # ./utils # ./weights # ./runs这段命令的作用是快速定位数据目录和权重目录。maxdepth 2避免递归太深刷屏sort让输出有序。如果你看到labels目录下是.txt文件每行格式为class_id x_center y_center width height那就是标准 YOLO 格式可以直接用如果是.json或.xml就需要先转换。2.2 舌象数据的类别定义舌质、舌苔、舌形怎么拆标签舌象诊断的标签体系和通用目标检测不一样。常见做法是把舌质颜色淡红、红、绛、紫、舌苔颜色白、黄、灰黑、舌苔厚薄薄、厚、舌形胖大、瘦小、齿痕、裂纹拆成多个维度。但 YOLO 是单标签多类别检测所以实际工程里通常把每个组合定义为一个独立类别比如danhong_baotai、hong_huangtai这种。这套源码的类别定义一般在data.yaml或classes.txt里。# data.yaml 典型配置 path: ./datasets train: images/train val: images/val nc: 12 # 类别数根据实际舌象特征组合调整 names: 0: danhong_baotai 1: danhong_huangtai 2: hong_baotai 3: hong_huangtai 4: jiang_baotai 5: zi_baotai 6: danhong_boobai 7: hong_boobai 8: pangda_chihen 9: shouxiao_liewen 10: baitai_hou 11: huangtai_hounc必须和names里的条目数一致否则训练时类别索引会越界。path建议用相对路径换机器时不用改。如果你的数据集类别和这里不一样直接改names和nc但要注意标签文件里的class_id也要同步改否则模型学到的映射是错的。2.3 环境安装Python 版本、CUDA 匹配与依赖冲突排查深度学习环境最烦的就是版本匹配。这套工程一般要求 Python 3.8 以上PyTorch 1.10 到 2.0 之间ultralytics 用 8.x 版本。CUDA 版本要和 PyTorch 对应比如 CUDA 11.8 对应torch2.0.1cu118。我一般用 conda 建独立环境避免和系统 Python 打架。conda create -n tongue-yolo python3.9 -y conda activate tongue-yolo pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics8.0.200 opencv-python pillow matplotlib pyyaml tqdm第一行建环境第二行激活。第三行装 PyTorch--index-url指定 CUDA 11.8 的 wheel 源如果你机器是 CUDA 12.x把cu118换成cu121。第四行装 ultralytics 和其他依赖。装完用python -c import torch; print(torch.cuda.is_available())验证 GPU 是否可用返回True才算对。如果返回False大概率是 CUDA 版本和驱动不匹配先nvidia-smi看驱动支持的 CUDA 版本再重装对应 torch。提示不要混用 pip 和 conda 装同一个包容易出现动态库冲突。要么全 pip要么全 conda。3. 训练与推理全流程从标注数据到舌象检测结果3.1 数据标注格式转换LabelImg 到 YOLO txt 的脚本实现如果你拿到的原始标注是 LabelImg 生成的 XML需要转成 YOLO 的 txt。转换逻辑不复杂但有几个边界坑图片尺寸要读对坐标要归一化类别名要映射成 id。import os import xml.etree.ElementTree as ET from PIL import Image # 类别名到 id 的映射必须和 data.yaml 一致 class_map { danhong_baotai: 0, danhong_huangtai: 1, hong_baotai: 2, hong_huangtai: 3, jiang_baotai: 4, zi_baotai: 5, danhong_boobai: 6, hong_boobai: 7, pangda_chihen: 8, shouxiao_liewen: 9, baitai_hou: 10, huangtai_hou: 11 } def xml_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 图片文件名和 xml 同名扩展名可能是 jpg 或 png img_name os.path.splitext(xml_file)[0] img_path None for ext in [.jpg, .png, .jpeg]: candidate os.path.join(img_dir, img_name ext) if os.path.exists(candidate): img_path candidate break if img_path is None: print(f跳过 {xml_file}找不到对应图片) continue with Image.open(img_path) as im: w, h im.size lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in class_map: print(f未知类别 {cls_name}跳过) continue cls_id class_map[cls_name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 归一化并转为中心点宽高 x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h # 边界裁剪防止坐标越界 x_center min(max(x_center, 0), 1) y_center min(max(y_center, 0), 1) bw min(max(bw, 0), 1) bh min(max(bh, 0), 1) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_path os.path.join(out_dir, img_name .txt) with open(out_path, w) as f: f.write(\n.join(lines)) xml_to_yolo(./raw/xml, ./raw/images, ./datasets/labels/train)这段脚本的核心逻辑遍历 XML 文件找到对应图片读取宽高把绝对坐标转成归一化的中心点坐标。class_map必须和data.yaml的names完全对应否则训练时类别全乱。边界裁剪那几行是防止标注框超出图片范围导致归一化后大于 1YOLO 虽然能容忍但会影响训练稳定性。最后输出到labels/train目录文件名和图片保持一致只是扩展名换成.txt。3.2 训练参数怎么设epochs、batch size、学习率的实操取值训练脚本一般长这样关键是几个参数怎么定。舌象数据集通常不大几千张图类别十几类属于小样本多类别任务。from ultralytics import YOLO # 加载预训练模型没有预训练权重就从 yaml 建 model YOLO(yolov8n.pt) # 或 yolov8s.ptn 最快s 精度略高 results model.train( datadata.yaml, epochs150, # 小数据集 100-200 够用太多会过拟合 imgsz640, # 舌象细节多640 是底线有条件上 800 batch16, # 显存 8G 用 164G 用 8爆显存就减半 lr00.01, # 初始学习率预训练模型用 0.01从零训练用 0.001 lrf0.01, # 最终学习率 lr0 * lrf patience30, # 30 轮没提升就早停省时间 augmentTrue, # 开启数据增强小数据集必开 mosaic1.0, # mosaic 增强概率1.0 表示全开 mixup0.1, # mixup 增强太高会模糊舌象特征 device0, # 用 GPU 0没有 GPU 写 cpu projectruns/tongue, nameexp1 )epochs设 150 是因为舌象数据集通常几千张100 轮左右 loss 就趋于平稳150 留点余量。imgsz640是 YOLO 的默认值但舌象的齿痕、裂纹这些细节在 640 下可能看不清如果显存够建议上 800 或 1024。batch16是 8G 显存的保守值爆显存就降到 8。lr00.01配合预训练权重比较稳如果你从零训练改成 0.001。patience30是早停机制30 轮验证集指标不提升就停避免浪费时间。mosaic1.0和mixup0.1是数据增强小数据集上能显著提升泛化但 mixup 太高会让舌象颜色失真0.1 到 0.2 比较合适。训练过程中重点看runs/tongue/exp1/results.csv里面记录了每轮的 loss 和 mAP。如果val/box_loss持续下降但val/cls_loss上升说明分类过拟合了要么加数据要么减类别。3.3 推理与结果解析如何把检测框映射回舌象诊断结论训练完拿到best.pt推理脚本要做的不仅是画框还要把检测结果转成可读的诊断描述。from ultralytics import YOLO import cv2 model YOLO(runs/tongue/exp1/weights/best.pt) class_names model.names # 从模型里读类别名不用手动维护 img cv2.imread(test_tongue.jpg) results model(img, conf0.25, iou0.45) for r in results: boxes r.boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() label class_names[cls_id] print(f检测到{label}置信度{conf:.2f}位置{xyxy}) # 画框 x1, y1, x2, y2 map(int, xyxy) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f{label} {conf:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(result.jpg, img)conf0.25是置信度阈值低于这个值的框不输出。舌象检测里如果漏检比误检更严重可以降到 0.15如果误检太多提到 0.4。iou0.45是 NMS 的 IoU 阈值控制重叠框的合并。model.names直接从模型读类别名避免手动维护class_names和data.yaml不一致。输出结果里cls_id对应类别conf是置信度xyxy是左上角和右下角坐标。实际业务里你可以根据检测到的类别组合生成诊断文本比如检测到hong_huangtai和pangda_chihen就输出「舌红黄苔舌体胖大伴齿痕」这类描述。注意推理时图片尺寸要和训练时一致训练用 640 推理也用 640否则检测框会偏移。4. 避坑与排查舌象检测工程里最容易翻车的五个地方4.1 现象训练 loss 不下降mAP 一直是 0原因通常是标签格式不对。YOLO 要求每行class_id x_center y_center width height且坐标是归一化到 0 到 1 的。如果你直接填了像素坐标或者 class_id 从 1 开始而不是 0模型就学不到东西。另一个可能是data.yaml里的nc和实际类别数不一致导致类别索引越界。解决用python -c from ultralytics.utils import check_dataset; check_dataset(data.yaml)检查数据集它会报出格式错误。然后手动打开一个 label 文件确认坐标都在 0 到 1 之间class_id 从 0 开始。4.2 现象训练到一半显存爆了报 CUDA out of memory原因一般是batch设太大或者imgsz太高。舌象图片如果分辨率是 1024 以上imgsz640时显存占用已经不小再开mosaic增强会额外占显存。解决先把batch减半比如从 16 降到 8。如果还爆把imgsz降到 512或者关掉mosaic设mosaic0.0。另外workers设太大会导致数据加载占用过多内存设成 4 或 8 就行。4.3 现象推理时检测框位置偏移框到了舌头外面原因通常是训练和推理的图片尺寸不一致。YOLO 在推理时会自动 resize 到imgsz但如果训练时用了 letterbox 填充推理时没对齐坐标就会偏。另一个可能是图片 EXIF 方向问题手机拍的舌象照片经常带旋转信息OpenCV 读进来是旋转前的。解决推理时显式指定imgsz和训练一致。对于 EXIF 问题用PIL.ImageOps.exif_transpose先纠正方向再转成 OpenCV 格式。from PIL import Image, ImageOps import numpy as np import cv2 img_pil Image.open(test_tongue.jpg) img_pil ImageOps.exif_transpose(img_pil) # 纠正 EXIF 旋转 img cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)4.4 现象某些类别检测效果特别差其他类别正常原因通常是类别样本不均衡。舌象数据里淡红舌、薄白苔这类常见舌象样本多紫舌、灰黑苔这类少见样本少模型会偏向多数类。解决在data.yaml里给少数类加权重或者用copy_paste增强少数类。另一个办法是分层采样保证每个 batch 里各类别都有。如果某个类别样本少于 50 张建议先合并到相近类别或者单独收集数据。4.5 现象换一台机器跑报 ultralytics 版本不兼容原因是你用的best.pt是用某个版本的 ultralytics 训练的换机器后装了新版本API 变了。YOLOv5 和 YOLOv8 的模型格式不通用v8 的best.pt在 v5 里加载会报错。解决在requirements.txt里锁定版本比如ultralytics8.0.200。如果已经换了版本用pip install ultralytics8.0.200回退。另外best.pt里保存了训练时的配置加载时用YOLO(best.pt)会自动读配置不要手动改。5. 进阶技巧用 TTA 和类别阈值微调把 mAP 再拉几个点训练完模型只是起点实际部署前还有两个技巧能明显提升效果。第一个是 TTATest Time Augmentation推理时对同一张图做多种变换翻转、缩放把结果融合。YOLO 自带 TTA 接口一行代码就能开。results model(img, augmentTrue, conf0.25, iou0.45)augmentTrue就是开启 TTA它会跑三次推理原图、水平翻转、垂直翻转然后 NMS 融合。代价是推理速度慢三倍但 mAP 通常能涨 1 到 3 个点。如果业务对延迟不敏感建议开。第二个是类别阈值微调。默认conf0.25对所有类别一视同仁但舌象里有些类别容易混比如danhong_baotai和danhong_boobai颜色接近只是舌苔厚薄不同。你可以对容易混的类别单独设阈值。# 对每个类别设不同置信度阈值 class_conf { danhong_baotai: 0.3, danhong_boobai: 0.35, hong_baotai: 0.25, hong_huangtai: 0.25, zi_baotai: 0.2, # 少见类别降低阈值减少漏检 huangtai_hou: 0.3 } results model(img, conf0.1, iou0.45) # 先低阈值全检出来 for r in results: for box in r.boxes: cls_name model.names[int(box.cls[0])] if float(box.conf[0]) class_conf.get(cls_name, 0.25): continue # 低于该类阈值就丢弃 # 保留有效检测这段逻辑是先设一个全局低阈值conf0.1把所有候选框检出来然后按类别名查表低于该类阈值的丢弃。这样对少见类别可以放宽对容易混的类别可以收紧。实际调的时候拿验证集跑一遍看每个类别的 PR 曲线找到 F1 最高的阈值点。还有一个细节是 NMS 的iou阈值。舌象检测里舌体和舌苔的框经常重叠iou0.45可能会把舌苔框误删。如果发现舌苔漏检把iou提到 0.5 或 0.6让重叠框保留更多。提示TTA 和类别阈值微调可以叠加使用但 TTA 会让推理变慢如果部署在边缘设备上建议只在服务端开 TTA边缘端用普通推理。从那以后我每次拿到新的检测工程都强制先跑一遍check_dataset再拿 10 张图做推理可视化确认框的位置和类别都对才敢开完整训练。这个习惯帮我省了至少三次通宵重训。希望帮到你。本文还有配套的精品资源点击获取
相关阅读
Happier CLI与Daemon架构完整指南:本地如何管理多AI Agent进程
【免费下载链接】happier Web, Desktop & Mobile client and orchestrator for Codex, Claude Code, OpenCode, Pi, Cursor, Grok, Antigravity, Kimi, Augment Code, Qwen, fully end-to-end encrypted 项目地址: https://gitcode.com/gh_mirrors/hap/happier …
2026/10/11 21:47:57 阅读全文 →