舌苔图像分类实战:ResNet50+Attention落地教程

发布时间:2026/10/7 12:55:01
舌苔图像分类实战:ResNet50+Attention落地教程 简介本资源是一套面向高校计算机与医学信息交叉方向本科生的毕业设计级项目聚焦中医舌诊数字化实践提供基于深度学习的舌苔识别、检测与鉴定全流程解决方案。资源包含可直接运行的Python源码含PyQt5开发的GUI交互界面、训练完成的.pth模型文件、配套论文报告含方法设计、实验分析与结果可视化、多组真实舌象运行截图及关键日志文件覆盖数据预处理、YOLOv5目标检测、ResNet分类模块及界面集成等核心环节。压缩包共110个文件以26个.py源码、6个.pth模型、7个.jpg舌象样本、5个.json配置与日志、2个.ui界面定义为主整体大小105.46MB结构清晰便于分模块学习与调试。目前已有188人学习下载代码经本地编译验证评审得分95分以上助教审定通过适合深度学习入门者开展医学图像项目实战、课程设计或毕设参考。1. 舌苔识别不是玄学一个能跑通、能改、能交毕设的深度学习落地闭环你拍一张舌苔照片系统3秒内标出厚薄、润燥、腐腻、偏色——这不是中医APP里的营销话术而是这个资源包里真实可运行的Python工程。它不依赖云端API所有模型推理在本地完成GUI界面用PyQt5写成双击main.py就能启动训练好的ResNet50Attention轻量模型已固化为.pth文件输入尺寸固定为224×224支持单图检测和批量预测配套论文报告含数据采集规范共1276张标注舌象图分7类薄白、厚白、黄腻、灰黑、红绛、淡白、剥苔、消融实验对比表、混淆矩阵热力图。适合两类人一是大三/大四做毕业设计的学生代码结构清晰、注释完整、有答辩PPT素材二是想快速验证医学图像分类pipeline的工程师它把数据预处理→模型训练→GUI封装→结果可视化这条链路全打通了连TensorBoard日志文件那堆events.out.tfevents.*都原样保留方便你回溯训练曲线。别被“中医AI”吓退——底层就是标准的CNN分类任务只是数据域特殊而这个包已经帮你绕过了最耗时的脏活舌体分割、光照归一化、类别不平衡采样。2. 从解压到运行五步走通GUI主程序看清每个模块的职责边界2.1 解压后目录结构解析哪些文件必须保留哪些可以删解压得到的根目录下共14个一级子项按功能划分为四类类型文件/目录名说明是否可删核心代码main.py,inference.py,train.py,utils/,models/GUI入口、推理逻辑、训练脚本、工具函数、网络定义❌ 不可删模型权重best_model.pth,model_final.pth训练收敛后的最佳权重与最终权重inference.py默认加载best_model.pth❌ 不可删GUI资源ui/,icons/,screenshots/.ui文件Qt Designer生成、图标文件、运行截图含舌苔分类结果弹窗✅screenshots/可删其余保留文档与日志report.pdf,README.md,events.out.tfevents.*论文报告、使用说明、TensorBoard训练日志共10个对应10次训练实验✅ 日志文件可删除非你要复现训练过程提示events.out.tfevents.*文件是TensorBoard生成的二进制日志总大小约18MB。若仅需推理删除它们可节省空间若要查看loss/acc曲线用tensorboard --logdir./ --port6006启动服务后访问http://localhost:6006即可加载全部日志。2.2 环境配置为什么用conda而非pip装torch以及CUDA版本陷阱该项目明确要求Python 3.8requirements.txt中指定且依赖torch1.10.0cu113CUDA 11.3。直接pip install torch会默认安装CPU版导致GUI启动时报错ModuleNotFoundError: No module named torch.cuda。正确做法是# 创建独立环境避免污染主环境 conda create -n tongue-env python3.8 conda activate tongue-env # 指定CUDA版本安装PyTorch关键 conda install pytorch1.10.0 torchvision0.11.0 torchaudio0.10.0 cudatoolkit11.3 -c pytorch # 安装其余依赖注意opencv-python-headless不能用于GUI显示 pip install -r requirements.txtrequirements.txt中opencv-python-headless4.5.5.64是训练时用的无GUI但main.py调用cv2.imshow()需要GUI支持因此必须额外安装带GUI的OpenCVpip uninstall opencv-python-headless -y pip install opencv-python4.5.5.64注意torch1.10.0与CUDA 11.3强绑定。如果你的NVIDIA驱动版本低于465.19对应CUDA 11.3最低驱动import torch会报错libcudnn.so.8: cannot open shared object file。此时不要降级torch——应升级显卡驱动或改用torch1.9.0cu111需同步更换cudatoolkit11.1。2.3 启动GUI并加载模型三行命令验证核心流程进入解压目录后执行以下命令cd /path/to/unzipped/folder conda activate tongue-env python main.py成功启动后界面显示左侧区域图片加载区支持拖拽或点击“选择图片”按钮中间区域舌苔原图叠加的热力图Grad-CAM生成标出模型关注区域右侧区域分类结果置信度Top3、诊断建议文本框如“厚白苔提示寒湿内盛建议温阳化湿”逻辑说明main.py调用inference.py中的predict_image()函数该函数完成三件事① 用transforms.Compose对输入图像做标准化均值[0.485,0.456,0.406]、标准差[0.229,0.224,0.225]② 将图像送入models/resnet50_attention.py定义的网络前向传播③ 对输出logits做softmax取argmax获得类别ID并映射到utils/class_names.py中的中文标签。2.4 批量预测脚本如何绕过GUI直接处理文件夹若需对整批舌象图做统计分析如某医院1000张舌诊图的分类分布直接修改inference.py中的batch_predict()函数def batch_predict(image_folder: str, output_csv: str results.csv): model load_model(best_model.pth) # 加载模型 transform get_transform() # 获取预处理变换 class_names load_class_names() # 加载类别名 results [] for img_path in Path(image_folder).glob(*.jpg): try: img cv2.imread(str(img_path)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR→RGB img_tensor transform(Image.fromarray(img)).unsqueeze(0) # 转tensor并加batch维度 with torch.no_grad(): output model(img_tensor) probs torch.nn.functional.softmax(output, dim1) top_prob, top_class torch.topk(probs, k1) results.append({ filename: img_path.name, predicted_class: class_names[top_class.item()], confidence: top_prob.item() }) except Exception as e: print(fError processing {img_path}: {e}) results.append({filename: img_path.name, error: str(e)}) pd.DataFrame(results).to_csv(output_csv, indexFalse) print(fResults saved to {output_csv})调用方式python -c from inference import batch_predict; batch_predict(data/test_images, batch_results.csv)参数说明image_folder为待测图片文件夹路径只读.jpgoutput_csv为结果保存路径。输出CSV含三列文件名、预测类别、置信度。3. 模型结构与训练细节为什么用ResNet50Attention而不是ViT或EfficientNet3.1 网络架构选择依据舌苔纹理的局部性与全局语义矛盾舌苔识别的核心挑战在于关键判别特征既分散又微弱——厚苔的颗粒感、黄腻苔的油亮反光、剥苔的边界锯齿都集中在舌体表面毫米级区域但同时需结合舌质颜色红绛/淡白判断整体证候。纯CNN如ResNet擅长局部纹理提取但易丢失长程依赖纯Transformer如ViT建模全局关系强却对微小纹理敏感度不足。本项目采用ResNet50主干CBAM注意力模块的混合架构原因如下ResNet50的Stage3/Stage4特征图28×28, 14×14分辨率足够捕获舌苔颗粒CBAMConvolutional Block Attention Module在通道与空间两个维度施加注意力通道注意力校准各特征图重要性如增强“颗粒度”通道抑制“背景噪声”通道空间注意力聚焦舌体区域自动忽略镜面反光、嘴唇边缘等干扰对比实验显示在相同数据集上ResNet50CBAM的Top-1 Acc达92.3%比纯ResNet50高3.7%比ViT-Tiny低1.2%但推理速度快2.1倍RTX3060实测。3.2 数据预处理流水线解决舌象图三大痛点原始舌象图存在三大干扰源① 镜面反光导致局部过曝② 不同设备白平衡差异造成色偏③ 舌体位置不居中、比例不一致。预处理脚本utils/preprocess.py采用四级清洗def preprocess_tongue(img: np.ndarray) - np.ndarray: # Step1: 基于HSV的反光去除抑制H通道高亮区域 hsv cv2.cvtColor(img, cv2.COLOR_RGB2HSV) h, s, v cv2.split(hsv) v cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8)).apply(v) # 自适应直方图均衡 hsv cv2.merge([h, s, v]) img cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB) # Step2: 色彩校正用灰卡基准图计算白平衡增益 gray_card_roi img[100:150, 100:150] # 假设左上角有灰卡 avg_rgb np.mean(gray_card_roi, axis(0,1)) gain 128 / avg_rgb # 使灰卡RGB均值趋近128 img np.clip(img * gain, 0, 255).astype(np.uint8) # Step3: 舌体ROI裁剪基于Otsu阈值轮廓面积筛选 gray cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU) contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) tongue_contour max(contours, keycv2.contourArea) # 取最大连通域 x, y, w, h cv2.boundingRect(tongue_contour) img img[y:yh, x:xw] # Step4: 尺寸归一化保持宽高比缩放再中心填充至224×224 h, w img.shape[:2] scale 224 / max(h, w) new_h, new_w int(h*scale), int(w*scale) img cv2.resize(img, (new_w, new_h)) pad_h, pad_w (224-new_h)//2, (224-new_w)//2 img cv2.copyMakeBorder(img, pad_h, 224-new_h-pad_h, pad_w, 224-new_w-pad_w, cv2.BORDER_CONSTANT, value[128,128,128]) return img参数说明Step1中clipLimit2.0防止过增强Step2假设灰卡位于固定ROI实际使用时需根据拍摄规范调整坐标Step3的cv2.RETR_EXTERNAL确保只提取最外层轮廓避免舌乳头干扰Step4的BORDER_CONSTANT填充值128中性灰比0更符合舌象图背景特性。3.3 训练超参配置为什么学习率0.001、batch_size32是甜点train.py中关键超参如下# 数据加载 train_loader DataLoader( datasettrain_dataset, batch_size32, # 太小16导致梯度噪声大太大64显存溢出RTX3060 12GB shuffleTrue, num_workers4, # 4个进程预加载避免GPU等待I/O pin_memoryTrue # 锁页内存加速GPU传输 ) # 优化器 optimizer torch.optim.AdamW( model.parameters(), lr0.001, # 初始学习率ResNet50微调常用值比SGD更稳定 weight_decay1e-4 # L2正则抑制过拟合舌苔数据量仅1276张 ) # 学习率调度 scheduler torch.optim.lr_scheduler.OneCycleLR( optimizer, max_lr0.001, epochs50, steps_per_epochlen(train_loader), pct_start0.3, # 前30% epoch上升学习率后70%下降 anneal_strategycos # 余弦退火平滑收敛 )为什么选AdamW舌苔类别间样本量差异大厚白苔421张剥苔仅87张AdamW的自适应学习率能更好处理类别不平衡weight_decay1e-4经网格搜索确定——比1e-3泛化更好比1e-5正则不足。3.4 模型评估指标混淆矩阵揭示的临床误判模式训练完成后train.py自动生成confusion_matrix.png其中关键发现真实类别 → 预测类别薄白厚白黄腻灰黑红绛淡白剥苔薄白92%5%0%0%1%2%0%厚白3%88%6%0%0%2%1%黄腻0%12%79%2%3%2%2%灰黑0%0%8%85%3%2%2%红绛1%0%2%1%91%3%2%淡白2%3%1%0%4%87%3%剥苔0%1%2%1%1%3%92%分析可知厚白与黄腻的混淆率最高12%源于部分厚白苔因湿度高呈现油亮反光被误判为黄腻淡白与红绛各有4%互判因两者均属舌质颜色变化模型过度依赖RGB值而忽略苔质厚度。这提示后续改进方向增加舌质-舌苔分离模块或引入多模态输入如添加近红外图像增强血管纹理。4. 避坑指南十个真实翻车现场与血泪修复方案4.1 现象GUI启动后黑屏控制台无报错鼠标悬停按钮无响应原因PyQt5与高分屏缩放冲突Windows 125%缩放率下QWidget渲染异常解决在main.py开头添加缩放适配代码必须在QApplication创建前import os os.environ[QT_SCALE_FACTOR] 1 # 强制禁用系统缩放 # 或者启用高DPI适配推荐 os.environ[QT_ENABLE_HIGHDPI_SCALING] 14.2 现象点击“选择图片”后程序崩溃报错cv2.error: OpenCV(4.5.5) ... error: (-215:Assertion failed) !_src.empty()原因用户选择了非图像文件如.txt或空文件cv2.imread()返回None解决在main.py的load_image()函数中增加健壮性检查def load_image(self): path, _ QFileDialog.getOpenFileName(self, 选择舌象图, , Image Files (*.jpg *.jpeg *.png)) if not path: # 用户取消选择 return img cv2.imread(path) if img is None: # 文件不可读或非图像格式 QMessageBox.critical(self, 错误, f无法加载图片{path}\n请确认文件格式为JPG/PNG且未损坏) return # 后续处理...4.3 现象模型预测结果全为“未知类”class_names.py中类别数与模型输出维度不匹配原因models/resnet50_attention.py中num_classes7被误改为其他值或class_names.py中列表长度≠7解决严格校验三处一致性models/resnet50_attention.py第42行self.fc nn.Linear(2048, 7)utils/class_names.py中CLASS_NAMES [薄白, 厚白, ..., 剥苔]长度必须为7inference.py中load_class_names()返回的列表长度4.4 现象TensorBoard打开后显示“No dashboards are active”日志文件为空原因events.out.tfevents.*文件被移动或权限不足TensorBoard无法读取解决确认日志文件在当前目录下且执行tensorboard --logdir. --bind_all--bind_all允许远程访问若仍无效用file events.out.tfevents.*检查文件类型非二进制文件需重新训练生成。4.5 现象批量预测时部分图片报错ValueError: Expected more than one value per channel when training原因BatchNorm层在eval()模式下遇到batch_size1的输入当文件夹中只剩1张图时解决在batch_predict()函数中强制设置model.eval()后添加# 确保BN层使用运行统计量而非batch统计量 for module in model.modules(): if isinstance(module, torch.nn.BatchNorm2d): module.track_running_stats True5. 进阶技巧三招让模型从“能跑”升级为“可用”附诊断建议生成逻辑5.1 模型轻量化用TorchScript导出ONNX部署到Jetson Nano原始.pth模型大小为98MB无法在边缘设备运行。通过TorchScript转ONNX可压缩至22MB并提升推理速度# 在train.py末尾添加导出代码 model.eval() dummy_input torch.randn(1, 3, 224, 224) # 构造假输入 traced_model torch.jit.trace(model, dummy_input) # 脚本化 traced_model.save(tongue_model.pt) # 保存为TorchScript # 导出ONNX需安装onnx torch.onnx.export( traced_model, dummy_input, tongue_model.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch_size}, output: {0: batch_size}}, opset_version12 )部署到Jetson Nano步骤① 安装onnxruntime② 用onnxruntime.InferenceSession(tongue_model.onnx)加载③ 输入预处理同inference.py输出为[1,7]概率向量。实测FPS从CPU的3.2提升至Jetson Nano的11.7。5.2 诊断建议生成基于规则引擎的可解释性增强模型输出仅为类别ID但临床需要文字建议。utils/diagnosis_rules.py实现规则映射DIAGNOSIS_RULES { 薄白: 舌苔薄白而润为正常舌象或表证初起建议观察3日若无发热咽痛可暂不干预。, 厚白: 苔厚白滑主寒湿内盛常伴畏寒、便溏推荐藿香正气丸艾灸足三里。, 黄腻: 苔黄而黏腻主湿热蕴结常见口苦、尿黄建议龙胆泻肝汤加减。, 灰黑: 苔灰黑干燥主热极伤阴多见高热、便秘需急予增液承气汤。, 红绛: 舌质红绛少苔主阴虚火旺伴潮热盗汗宜六味地黄丸滋阴。, 淡白: 舌质淡白主气血两虚常见乏力、心悸推荐归脾汤益气养血。, 剥苔: 舌苔部分剥落主胃阴不足症见口干、饥不欲食可用益胃汤。 } def generate_advice(predicted_class: str) - str: 根据预测类别生成中医诊断建议 if predicted_class not in DIAGNOSIS_RULES: return 模型置信度不足建议由执业中医师面诊确认。 return DIAGNOSIS_RULES[predicted_class]关键设计规则库与模型解耦医生可直接修改DIAGNOSIS_RULES字典更新诊疗方案无需重训练模型——这是临床落地的关键可维护性设计。5.3 Grad-CAM热力图调试定位模型决策依据是否符合中医理论GUI中显示的热力图由inference.py的generate_gradcam()生成其原理是计算目标类别对最后卷积层输出的梯度加权平均。但初始版本存在偏差模型过度关注舌体边缘因训练数据中边缘标注不一致。修复方法是在generate_gradcam()中加入舌体掩膜约束def generate_gradcam(model, img_tensor, target_class, cam_layerlayer4): # ... 原有梯度计算代码 ... # 新增用舌体分割掩膜过滤无关区域 tongue_mask get_tongue_mask(img_tensor) # 返回0/1二值图1为舌体区域 cam cam * tongue_mask.cpu().numpy() # 仅保留舌体内的热力值 return cam def get_tongue_mask(img_tensor: torch.Tensor) - torch.Tensor: 基于HSV阈值的快速舌体粗分割无需额外模型 img_np img_tensor.squeeze(0).permute(1,2,0).cpu().numpy() hsv cv2.cvtColor((img_np * 255).astype(np.uint8), cv2.COLOR_RGB2HSV) # 舌体HSV范围H∈[0,30]∪[150,180], S50, V50 mask1 cv2.inRange(hsv, (0,50,50), (30,255,255)) mask2 cv2.inRange(hsv, (150,50,50), (180,255,255)) tongue_mask cv2.bitwise_or(mask1, mask2) tongue_mask cv2.morphologyEx(tongue_mask, cv2.MORPH_CLOSE, np.ones((5,5))) return torch.from_numpy(tongue_mask).float().unsqueeze(0) / 255.0效果修复后热力图90%以上权重落在舌苔区域而非嘴唇或背景符合“望舌诊病”的中医逻辑。这步虽增加毫秒级计算但极大提升医生对AI的信任度。从那以后我每次交付医疗AI项目都强制走一遍Grad-CAM掩膜校验——哪怕客户没提可解释性需求。因为真正的临床信任从来不是靠准确率数字堆出来的而是当医生指着热力图说“这里确实是病灶”时你心里那句“嗯模型没瞎猜”的笃定。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询