公司动态

深度学习舌苔检测:从U-Net分割到ResNet分类的完整毕设指南

📅 2026/9/3 6:00:38
深度学习舌苔检测:从U-Net分割到ResNet分类的完整毕设指南
简介本资源是一套完整的基于深度学习的舌苔图像检测毕业设计实现方案面向计算机、人工智能、自动化及医学信息工程等专业的本科生与研究生解决中医舌诊数字化中舌苔区域自动识别与分割的关键问题适用于毕业设计、课程大作业及科研入门实践。压缩包共110个文件大小105.46MB包含26个核心Python训练/推理脚本、6个预训练.pth模型权重、47个标注与处理后的舌苔图像jpg/png、10个编译字节码文件、5个配置与标签映射json、2个docx格式的毕设论文与开题报告以及TensorBoard日志文件events.out.tfevents用于训练过程可视化分析。已有329人学习下载项目经答辩评审获98分高分所有代码均调试通过并附详细文档说明涵盖数据预处理、UNet模型构建、训练调参、结果评估及部署建议结构清晰、注释完整既可直接运行复现也便于进阶者二次开发与功能拓展。1. 项目概述从“舌苔检测”到一套完整的毕业设计解决方案最近几年带我的几个本科生在做毕业设计时总会在选题和实现之间反复横跳最后要么代码跑不通要么论文写不下去。如果你也正为“基于深度学习的舌苔检测”这类题目头疼觉得既要搞懂算法、又要写代码、还得凑出一篇像样的论文和开题报告工作量巨大无从下手那这篇分享可能就是为你准备的。这不仅仅是一个技术实现更是一套从零到一完成一个合格、甚至优秀毕业设计的完整方法论和资源参考。我会结合我带学生做项目的实际经验拆解这个项目的每一个环节从为什么选择这个方向到如何搭建环境、处理数据、训练模型再到如何将你的工作系统地整理成代码、论文和文档。我的目标是你读完这篇文章不仅能获得一套可运行的源码参考更能掌握独立完成一个类似AI医疗交叉领域毕业设计的核心能力。简单来说“基于深度学习的舌苔检测”项目核心是利用卷积神经网络CNN等深度学习模型对采集到的舌头图像进行自动分析识别并分类舌苔的性状如颜色白、黄、腻、厚薄、润燥等。这在中医数字化、健康管理前置筛查等领域有明确的应用场景。对于本科生而言这个选题的优势在于技术栈主流Python深度学习框架、有公开数据集可供研究、问题定义清晰图像分类/分割、且兼具一定的学术价值和实用意义容易出成果。难点则在于数据的获取与标注、模型的选择与调优以及如何将技术过程严谨地转化为学术论文。接下来我将把这套“毕设源码论文文档”的组合拳拆解成可一步步跟随操作的详细指南。2. 项目核心思路与整体设计拆解做一个毕业设计最怕的就是思路不清东一榔头西一棒子。对于“舌苔检测”项目我们必须先建立一个清晰的顶层设计。这个设计需要回答几个关键问题我们要解决的具体问题是什么是舌苔颜色分类还是舌苔区域分割或是二者结合我们的技术路线是什么我们需要准备哪些“弹药”数据、工具、知识整个项目的里程碑如何规划2.1 问题定义与方案选型首先我们需要精确界定任务。舌苔检测可以细分为几个子任务舌体分割从包含人脸或口腔的图像中精准地分割出舌头区域。这是后续分析的基础能排除背景、牙齿、嘴唇的干扰。舌苔属性分类对分割后的舌苔区域判断其颜色白、淡白、黄、焦黄等、厚薄薄、厚、润燥润、燥、腻。舌苔区域分割更进一步在舌体上分割出覆盖有舌苔的特定区域并对其属性进行描述。对于本科毕设我通常建议采用“舌体分割 舌苔多标签分类”的方案。这个方案难度适中既有图像分割U-Net这样的经典任务也有多标签分类ResNet, EfficientNet这样的核心应用技术含量足够且流程清晰。分割任务保证输入质量分类任务给出最终判断逻辑闭环完整。为什么选择这个组合方案可行性高U-Net结构简单在医学图像分割上久经考验易于理解和实现。多标签分类是CNN的经典应用资料丰富。数据要求相对明确分割需要舌体的像素级标注框分类需要医生或专家对舌苔属性的标签。虽然标注费时但任务明确。成果展示性好分割结果可以直观地用彩色掩膜图展示分类结果可以做成表格或可视化热力图论文和答辩时非常直观。易于扩展完成基础版后可以尝试引入注意力机制如CBAM改进分割网络或用更先进的分类网络如Vision Transformer提升准确率作为论文的创新点。2.2 技术栈与工具选型工欲善其事必先利其器。以下是经过多个项目验证的、稳定且高效的工具组合编程语言Python 3.8。这是深度学习领域的事实标准拥有最丰富的库生态。深度学习框架PyTorch。我强烈推荐PyTorch而非TensorFlowKeras用于毕设。原因在于PyTorch的动态图机制更符合编程直觉调试非常方便对于需要快速实验和迭代的学术项目来说效率更高。而且其社区活跃很多最新的模型实现都以PyTorch为首选。关键Python库torchtorchvision: 核心框架。opencv-python(cv2): 图像读取、预处理、增强的核心工具。Pillow(PIL): 图像处理备用库。scikit-learn(sklearn): 用于数据划分、评估指标计算如准确率、精确率、召回率、F1-score。pandasnumpy: 数据处理和分析。matplotlibseaborn: 结果可视化绘制损失曲线、混淆矩阵、特征图等。albumentations: 功能强大且速度快的图像增强库特别适合分割任务。tqdm: 在循环中显示进度条提升体验。开发环境本地开发推荐使用Anaconda创建独立的Python环境避免包冲突。编辑器可用VS Code或PyCharm。云端GPU如果本地无显卡AutoDL或Google Colab。AutoDL国内访问稳定性价比高环境配置简单Colab免费但有使用限制。对于毕设周期租用按量计费的云端GPU是更经济的选择。版本控制GitGitHub/Gitee。从第一天就开始使用Git管理你的代码和论文这是必备的工程素养也能防止代码丢失。2.3 项目目录结构设计一个清晰的项目结构是良好工程实践的起点。建议你的项目根目录如下Tongue_Detection_DL/ ├── data/ # 数据相关 │ ├── raw/ # 原始图像数据 │ ├── annotations/ # 标注文件JSON, PNG掩膜等 │ ├── processed/ # 处理后的数据如裁剪、归一化后的图像 │ └── dataset.py # 自定义Dataset类 ├── models/ # 模型定义 │ ├── unet.py # U-Net分割模型 │ ├── classifier.py # 分类模型如ResNet │ └── utils.py # 模型工具函数如权重初始化 ├── configs/ # 配置文件 │ └── config.yaml # 超参数、路径等配置 ├── scripts/ # 脚本文件 │ ├── preprocess.py # 数据预处理脚本 │ ├── train_seg.py # 训练分割模型 │ ├── train_cls.py # 训练分类模型 │ ├── evaluate.py # 评估脚本 │ └── inference.py # 推理/预测脚本 ├── utils/ # 通用工具 │ ├── logger.py # 日志记录 │ ├── metrics.py # 自定义评估指标 │ └── visualize.py # 可视化工具 ├── outputs/ # 输出目录 │ ├── checkpoints/ # 模型权重保存 │ ├── logs/ # 训练日志TensorBoard │ └── results/ # 预测结果、图表 ├── docs/ # 文档 │ ├── requirements.txt # 项目依赖 │ └── README.md # 项目说明 ├── paper/ # 论文相关 │ ├── figures/ # 论文图片 │ └── main.tex # LaTeX论文主文件或Word文档 └── README.md # 项目总览这个结构将数据、代码、配置、输出、文档清晰分离便于管理和协作。3. 数据获取、处理与增强实战深度学习项目数据是基石。舌苔图像数据是项目的第一个拦路虎。3.1 数据来源与采集理想的数据来源是医院合作但这对本科生通常不现实。因此我们转向公开数据集和模拟采集公开数据集Tongue Image Database (TD)一些研究机构会公开小规模的舌像数据集可通过学术论文的附录或作者主页查找。GitHub搜索关键词“tongue dataset”、“tongue segmentation dataset”有时能找到研究者分享的数据。Kaggle偶尔会有相关的竞赛或数据集。注意公开数据集通常规模有限几百张且标注标准不一。使用时务必仔细阅读其许可协议License确保可用于学术研究。自行采集与标注推荐采集使用手机或单反在光线均匀避免强光直射和阴影、背景简洁如白色墙壁的条件下让被采集者自然伸出舌头拍摄正面清晰图像。建议统一分辨率如1024x768。可采集50-100位同学、朋友或家人的图像建立一个小型私有数据集。务必事先获得被采集者的知情同意并承诺仅用于学术研究。标注分割标注使用Labelme、CVAT或Make Sense等工具对舌体区域进行多边形Polygon标注导出为JSONLabelme格式或PNG掩膜图像。这是最耗时但最关键的一步。分类标注邀请一位中医学背景的同学或老师或自己查阅资料后谨慎判断为每张舌像的舌苔属性打上标签。例如可以定义三个维度color0:白1:淡黄2:黄thickness0:薄1:厚moisture0:润1:燥2:腻。每个维度独立判断形成多标签。3.2 数据预处理流程原始数据不能直接喂给模型必须经过预处理。# scripts/preprocess.py 示例片段 import cv2 import json import numpy as np from pathlib import Path import albumentations as A def load_and_process_image(image_path, mask_pathNone, target_size(256, 256)): 加载并预处理图像和掩膜 # 1. 读取 image cv2.imread(str(image_path)) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # OpenCV默认BGR转为RGB if mask_path: # 对于分割任务读取掩膜 mask cv2.imread(str(mask_path), cv2.IMREAD_GRAYSCALE) # 确保掩膜是二值图0和255 _, mask cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY) else: mask None # 2. 调整大小 (保持长宽比或直接拉伸根据任务定) # 这里使用简单的拉伸更优的方法是padding或中心裁剪 transform A.Compose([ A.Resize(target_size[0], target_size[1], interpolationcv2.INTER_LINEAR), ]) augmented transform(imageimage, maskmask) if mask is not None else transform(imageimage) # 3. 归一化 (对图像像素值) image_processed augmented[image] / 255.0 # 归一化到[0,1] image_processed (image_processed - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225] # ImageNet均值和标准差 if mask is not None: mask_processed augmented[mask] / 255 # 掩膜归一化到[0,1] mask_processed mask_processed.astype(np.float32) # 如果是多分类分割可能需要做one-hot编码 return image_processed, mask_processed else: return image_processed def split_dataset(image_dir, mask_dir, label_csv, val_ratio0.2, test_ratio0.1): 划分训练集、验证集、测试集 from sklearn.model_selection import train_test_split # 获取所有图像ID image_ids [p.stem for p in Path(image_dir).glob(*.jpg)] # 第一次分分出训练验证 和 测试集 train_val_ids, test_ids train_test_split(image_ids, test_sizetest_ratio, random_state42) # 第二次分从训练验证中分出验证集 train_ids, val_ids train_test_split(train_val_ids, test_sizeval_ratio/(1-test_ratio), random_state42) # 保存划分结果到文件确保可复现 # ... (保存为txt或json) return train_ids, val_ids, test_ids关键操作解析归一化使用ImageNet的均值和标准差是一种常见做法因为预训练模型是在此基础上训练的有助于模型收敛。如果你的数据分布与ImageNet差异极大可以计算自己数据集的均值和标准差。数据划分务必使用固定的随机种子如random_state42以确保每次划分结果一致这是实验结果可复现性的基础。绝对不能在划分后手动调整否则会导致数据泄露。3.3 数据增强策略数据量小是常态数据增强是提升模型泛化能力的利器。对于图像分类和分割增强策略略有不同。# 在Dataset类的__getitem__中或预处理管道中使用 import albumentations as A # 训练时的增强更强 train_transform A.Compose([ A.HorizontalFlip(p0.5), A.VerticalFlip(p0.1), # 舌像上下翻转需谨慎可根据实际情况调整概率 A.Rotate(limit15, p0.5), # 小角度旋转 A.RandomBrightnessContrast(brightness_limit0.1, contrast_limit0.1, p0.3), A.GaussNoise(var_limit(10.0, 30.0), p0.2), A.CoarseDropout(max_holes8, max_height16, max_width16, fill_value0, p0.2), # 模拟遮挡 # 对于分割任务必须同时对图像和掩膜进行相同的空间变换 ], additional_targets{mask: mask} if is_segmentation else None) # 验证/测试时的增强通常只有Resize和归一化 val_transform A.Compose([ A.Resize(256, 256), ]) def augment(image, maskNone): if mask is not None: augmented train_transform(imageimage, maskmask) return augmented[image], augmented[mask] else: augmented train_transform(imageimage) return augmented[image]注意事项与心得空间变换的一致性对于分割任务任何几何变换翻转、旋转、缩放都必须同步应用于图像和其对应的掩膜标签。albumentations库通过additional_targets参数完美支持这一点。颜色变换的适用性亮度、对比度、噪声等增强对舌苔颜色分类任务有益但需注意幅度不宜过大以免改变舌苔颜色的医学判断依据。保留原始数据增强应在数据加载时实时进行而不是预先处理好保存到磁盘以节省空间并增加样本多样性。4. 核心模型构建从U-Net分割到ResNet分类这是项目的技术核心。我们将分别实现舌体分割模型和舌苔分类模型。4.1 U-Net舌体分割模型实现U-Net因其编码器-解码器结构和跳跃连接在医学图像分割中表现出色。下面是一个简化版的PyTorch实现# models/unet.py import torch import torch.nn as nn import torch.nn.functional as F class DoubleConv(nn.Module): (卷积 BN ReLU) * 2 def __init__(self, in_channels, out_channels): super().__init__() self.double_conv nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size3, padding1), nn.BatchNorm2d(out_channels), nn.ReLU(inplaceTrue), nn.Conv2d(out_channels, out_channels, kernel_size3, padding1), nn.BatchNorm2d(out_channels), nn.ReLU(inplaceTrue) ) def forward(self, x): return self.double_conv(x) class Down(nn.Module): 下采样MaxPool DoubleConv def __init__(self, in_channels, out_channels): super().__init__() self.maxpool_conv nn.Sequential( nn.MaxPool2d(2), DoubleConv(in_channels, out_channels) ) def forward(self, x): return self.maxpool_conv(x) class Up(nn.Module): 上采样转置卷积 跳跃连接 DoubleConv def __init__(self, in_channels, out_channels): super().__init__() self.up nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size2, stride2) self.conv DoubleConv(in_channels, out_channels) # 跳跃连接后通道数相加 def forward(self, x1, x2): # x1: 上一层的特征图 x2: 跳跃连接的特征图 x1 self.up(x1) # 处理尺寸可能不匹配的情况由于池化舍入 diffY x2.size()[2] - x1.size()[2] diffX x2.size()[3] - x1.size()[3] x1 F.pad(x1, [diffX // 2, diffX - diffX // 2, diffY // 2, diffY - diffY // 2]) # 拼接跳跃连接 x torch.cat([x2, x1], dim1) return self.conv(x) class OutConv(nn.Module): 输出层1x1卷积将通道数映射到类别数 def __init__(self, in_channels, out_channels): super().__init__() self.conv nn.Conv2d(in_channels, out_channels, kernel_size1) def forward(self, x): return self.conv(x) class UNet(nn.Module): def __init__(self, n_channels3, n_classes1): super(UNet, self).__init__() self.n_channels n_channels self.n_classes n_classes self.inc DoubleConv(n_channels, 64) self.down1 Down(64, 128) self.down2 Down(128, 256) self.down3 Down(256, 512) self.down4 Down(512, 1024) self.up1 Up(1024, 512) self.up2 Up(512, 256) self.up3 Up(256, 128) self.up4 Up(128, 64) self.outc OutConv(64, n_classes) def forward(self, x): x1 self.inc(x) x2 self.down1(x1) x3 self.down2(x2) x4 self.down3(x3) x5 self.down4(x4) x self.up1(x5, x4) x self.up2(x, x3) x self.up3(x, x2) x self.up4(x, x1) logits self.outc(x) return logits # 输出是每个像素的logits二分类用sigmoid多分类用softmax关键点解析输出通道n_classes二值分割舌体/背景设为1使用Sigmoid激活和BCE损失。如果是多类别分割如舌苔、舌质、背景则设为类别数使用Softmax和CrossEntropy损失。跳跃连接Skip Connection这是U-Net的核心它将编码器下采样中同分辨率的特征图与解码器上采样的特征图进行通道拼接保留了细节信息有助于精准定位边界。尺寸对齐上采样转置卷积或插值后特征图尺寸可能与跳跃连接的特征图有1-2个像素的差异需要通过填充F.pad来对齐。这是实现中常见的细节。4.2 基于ResNet的舌苔多标签分类模型对于分类任务我们通常使用在ImageNet上预训练好的模型进行微调Fine-tuning这能极大加速收敛并提升性能。这里以ResNet50为例。# models/classifier.py import torch import torch.nn as nn import torchvision.models as models class TongueClassifier(nn.Module): def __init__(self, num_color_classes3, num_thickness_classes2, num_moisture_classes3, pretrainedTrue): super().__init__() # 加载预训练的ResNet50骨干网络 backbone models.resnet50(pretrainedpretrained) # 移除原始的顶层全连接层 modules list(backbone.children())[:-1] # 去掉最后的avgpool和fc层 self.feature_extractor nn.Sequential(*modules) # 获取特征维度 num_ftrs backbone.fc.in_features # 对于ResNet50是2048 # 为每个舌苔属性构建一个独立的分类头多任务学习 self.color_head nn.Sequential( nn.Linear(num_ftrs, 512), nn.ReLU(), nn.Dropout(0.5), nn.Linear(512, num_color_classes) ) self.thickness_head nn.Sequential( nn.Linear(num_ftrs, 512), nn.ReLU(), nn.Dropout(0.5), nn.Linear(512, num_thickness_classes) ) self.moisture_head nn.Sequential( nn.Linear(num_ftrs, 512), nn.ReLU(), nn.Dropout(0.5), nn.Linear(512, num_moisture_classes) ) def forward(self, x): # 提取通用特征 features self.feature_extractor(x) features features.view(features.size(0), -1) # 展平 # 各个头独立预测 color_out self.color_head(features) thickness_out self.thickness_head(features) moisture_out self.moisture_head(features) return color_out, thickness_out, moisture_out为什么用多任务学习Multi-task Learning将颜色、厚薄、润燥三个任务用一个共享特征提取器ResNet骨干来学习然后分别用不同的“头”Head进行预测这种方式称为多任务学习。它的优势在于特征共享骨干网络学习到的通用舌像特征纹理、形状可以被所有任务利用比训练三个独立模型更高效。正则化效应多个任务同时学习相当于对共享参数施加了额外的约束可能提升模型的泛化能力防止过拟合。效率高一次前向传播得到所有属性的预测推理速度快。微调技巧分层学习率骨干网络预训练部分的学习率应设置得比新添加的分类头小一个数量级例如骨干lr1e-5分类头lr1e-4。因为骨干网络已经学到了通用的低级特征边缘、纹理我们只需要微调它以适应舌像的特定分布而分类头需要从头学习。冻结部分层在训练初期可以完全冻结骨干网络只训练分类头。待分类头初步收敛后再解冻骨干网络的后几层或全部进行微调。这是一种更稳妥的策略。5. 模型训练、评估与优化全流程有了数据和模型接下来就是训练和调优。这是最考验耐心和实验能力的环节。5.1 训练流程与代码组织一个健壮的训练循环应包括数据加载、前向传播、损失计算、反向传播、参数更新、日志记录和模型保存。# scripts/train_cls.py 核心训练循环片段 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter from models.classifier import TongueClassifier from utils.metrics import calculate_metrics import yaml def train_one_epoch(model, dataloader, criterion, optimizer, device, epoch, writer): model.train() running_loss 0.0 all_preds {color: [], thickness: [], moisture: []} all_labels {color: [], thickness: [], moisture: []} for batch_idx, (images, labels) in enumerate(dataloader): images, labels images.to(device), {k: v.to(device) for k, v in labels.items()} optimizer.zero_grad() # 前向传播 color_pred, thick_pred, moist_pred model(images) # 计算各任务损失 loss_color criterion(color_pred, labels[color]) loss_thick criterion(thick_pred, labels[thickness]) loss_moist criterion(moist_pred, labels[moisture]) # 总损失可以加权求和 total_loss loss_color loss_thick loss_moist # 反向传播 total_loss.backward() optimizer.step() running_loss total_loss.item() # 收集预测和标签用于后续计算指标 all_preds[color].append(torch.argmax(color_pred, dim1).cpu()) all_labels[color].append(labels[color].cpu()) # ... 其他任务类似 if batch_idx % 10 0: print(fEpoch [{epoch}] Batch [{batch_idx}/{len(dataloader)}] Loss: {total_loss.item():.4f}) avg_loss running_loss / len(dataloader) # 计算整个epoch的指标 epoch_metrics {} for task in [color, thickness, moisture]: preds torch.cat(all_preds[task]) gts torch.cat(all_labels[task]) acc, precision, recall, f1 calculate_metrics(preds, gts) epoch_metrics[task] {acc: acc, pre: precision, rec: recall, f1: f1} # 记录到TensorBoard writer.add_scalar(f{task}/accuracy, acc, epoch) writer.add_scalar(f{task}/loss, avg_loss, epoch) # 这里简化了实际应记录各自任务的损失 writer.add_scalar(Loss/train, avg_loss, epoch) return avg_loss, epoch_metrics def main(config): device torch.device(cuda if torch.cuda.is_available() else cpu) # 1. 加载数据 train_loader, val_loader get_dataloaders(config) # 2. 初始化模型、损失函数、优化器 model TongueClassifier(...).to(device) criterion nn.CrossEntropyLoss() # 多分类任务 optimizer optim.Adam([ {params: model.feature_extractor.parameters(), lr: config[lr_backbone]}, {params: model.color_head.parameters(), lr: config[lr_head]}, {params: model.thickness_head.parameters(), lr: config[lr_head]}, {params: model.moisture_head.parameters(), lr: config[lr_head]}, ]) scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, patience5) # 3. 训练循环 writer SummaryWriter(log_dirconfig[log_dir]) best_val_f1 0.0 for epoch in range(config[epochs]): train_loss, train_metrics train_one_epoch(...) val_loss, val_metrics validate_one_epoch(...) scheduler.step(val_loss) # 4. 保存最佳模型 current_val_f1 val_metrics[color][f1] # 或以综合F1为准 if current_val_f1 best_val_f1: best_val_f1 current_val_f1 torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), val_f1: best_val_f1, }, f{config[checkpoint_dir]}/best_model.pth) # 5. 早停判断 (可选) # ... writer.close()5.2 损失函数与评估指标选择分割任务U-Net损失函数二值分割常用二元交叉熵损失BCEWithLogitsLoss或Dice Loss。Dice Loss直接优化分割区域的重叠度对类别不平衡背景远大于舌体问题更鲁棒。实践中可以将两者结合Loss BCE Loss Dice Loss。评估指标交并比IoU和Dice系数是核心指标。此外还可以计算精确率Precision、召回率Recall和F1-score。分类任务ResNet损失函数每个多分类任务使用交叉熵损失CrossEntropyLoss。总损失是各任务损失的加权和权重可以根据任务重要性或数据平衡性来调整。评估指标对于每个属性计算准确率Accuracy、精确率Precision、召回率Recall和F1-score。由于类别可能不平衡例如“黄苔”样本少F1-score比准确率更能反映模型性能。可以计算每个类别的指标再计算宏平均Macro-average或加权平均。5.3 超参数调优与实验管理学习率LR最重要的超参数。可以从1e-4或1e-3开始尝试使用学习率预热Warmup和余弦退火Cosine Annealing等调度策略。批量大小Batch Size在GPU内存允许的情况下尽可能大如16, 32。大的Batch Size使梯度估计更稳定但可能降低泛化性。可以尝试梯度累积Gradient Accumulation来模拟大Batch。优化器Adam或AdamW是默认的起点。AdamW对权重衰减的处理更好通常能获得更优的泛化性能。实验记录务必使用TensorBoard或Weights Biases (WB)记录每一轮训练和验证的损失、指标曲线。同时在代码中记录本次实验的所有超参数、数据划分、模型结构改动。一个简单的做法是每次实验创建一个以时间戳或描述命名的文件夹里面保存配置文件、模型权重和日志。6. 从代码到论文毕设文档与写作心法完成模型训练和评估后你需要将整个工作系统化地整理出来形成毕设要求的“源码论文文档”。这是将你的技术能力转化为学术成果的关键一步。6.1 工程文档与源码整理一个优秀的毕设源码仓库本身就是一份重要的文档。README.md项目的门面。必须包含项目标题与简介。环境要求Python版本、主要依赖库。快速开始数据准备、训练、推理的简明步骤。项目结构说明即我们之前设计的目录树。模型性能在测试集上的主要指标。引用与致谢。requirements.txt使用pip freeze requirements.txt生成确保他人能复现环境。详细的代码注释在关键函数、类和方法前添加文档字符串Docstring说明其功能、参数和返回值。复杂的逻辑处添加行内注释。配置化将模型超参数、路径、数据增强参数等全部写入一个配置文件如config.yaml或config.py避免在代码中硬编码。这极大提升了代码的可维护性和可复现性。提供预训练模型将你训练好的最佳模型权重.pth文件上传到云盘如Google Drive, 百度网盘并在README中提供下载链接方便评审老师或他人快速体验效果。6.2 毕设论文写作框架与核心章节论文不是代码的罗列而是对研究问题的系统阐述、方法论的严谨论证以及结果的客观分析。以下是一个建议的章节结构第一章 绪论研究背景与意义中医数字化、AI辅助诊断的趋势。国内外研究现状文献综述简要介绍传统舌诊方法和现有的AI舌诊研究指出其不足。本文主要研究内容与贡献明确提出本文要解决的“舌苔多属性自动检测”问题以及采用“分割分类”两阶段方法的创新性。论文组织结构。第二章 相关理论与技术基础舌诊理论基础简要介绍舌苔颜色、厚薄、润燥的医学含义。深度学习与卷积神经网络基础CNN原理、ResNet, U-Net等经典网络结构。图像分割与分类任务概述损失函数、评估指标。第三章 基于深度学习的舌苔检测系统设计整体系统架构图清晰地画出数据流输入-舌体分割-ROI提取-多属性分类-输出。数据集构建数据采集、标注方法、数据增强策略、划分比例。舌体分割模型设计U-Net结构详解为何适用于医学图像。舌苔多属性分类模型设计多任务学习的ResNet模型共享骨干与独立头的设计思想。第四章 实验与结果分析实验环境软硬件配置。实验设置超参数详细列表学习率、优化器、Batch Size、Epoch等。评价指标明确定义IoU, Dice, Accuracy, Precision, Recall, F1-score。结果与分析核心章节舌体分割结果展示分割效果图原图、真值掩膜、预测掩膜给出在测试集上的IoU和Dice系数表格。分析分割错误案例如边界模糊、舌头形状特殊。舌苔属性分类结果以表格形式展示各属性在测试集上的准确率、精确率、召回率、F1-score。绘制混淆矩阵分析哪些类别容易混淆如“白苔”与“淡白苔”。消融实验Ablation Study证明你设计的关键模块的有效性。例如对比“仅用原始图像分类” vs “先分割再分类”的效果证明分割预处理的重要性。对比“单任务独立模型” vs “多任务学习模型”的效果证明多任务学习的优势。对比不同骨干网络ResNet34 vs ResNet50 vs EfficientNet的效果。与现有方法对比如果能在公开数据集上找到可比较的文献结果。第五章 总结与展望工作总结简要回顾研究内容和取得的主要成果。不足与展望坦诚说明本工作的局限性如数据量小、标注主观性强、模型泛化能力有待提升等并提出未来可改进的方向如引入更大规模数据集、尝试Transformer模型、开发轻量化移动端应用等。写作心得图表胜千言多用图表展示系统框架、模型结构、实验结果。流程图、网络结构图、混淆矩阵、PR曲线、损失曲线等都要精心绘制推荐使用draw.io或PPT绘图导出为矢量图。数据说话所有结论都要有实验数据支撑避免主观臆断。“模型表现良好”不如“模型在测试集上取得了平均92.5%的准确率”。引用规范所有引用的他人工作方法、数据、观点都必须规范标注参考文献。使用学校规定的引用格式如GB/T 7714。反复修改论文是改出来的。写完初稿后要反复通读检查逻辑是否连贯语句是否通顺专业术语是否准确。可以请同学或导师帮忙审阅。6.3 开题报告与详细设计说明书开题报告可以看作是论文第一章“绪论”的扩展版。重点在于选题依据详细阐述研究背景、理论意义和实际应用价值。文献综述更全面、系统地总结国内外相关研究进展并指出现有研究的空白或不足从而引出你的研究课题。研究方案初步提出你的技术路线、拟采用的方法、实验设计以及可行性分析。预期成果说明预期完成的模型性能指标、系统原型或论文成果。进度安排制定详细的时间计划表如第1-2周文献调研第3-4周数据采集与处理...。详细设计说明书这部分对应论文的第三章但更偏向工程实现。需要详细描述系统总体设计架构图、模块划分。数据库设计如果涉及数据存储。每个核心模块的详细设计如数据预处理模块的流程图、分割/分类模块的类图、接口说明。用户界面设计如果开发了可视化系统需给出界面原型图。系统部署方案。完成以上所有内容你的“基于深度学习的舌苔检测”毕业设计就从最初的一个想法变成了一个拥有完整代码、详尽文档、严谨论文和清晰开题报告的扎实作品。这个过程本身就是对科研与工程能力的一次全面锻炼。本文还有配套的精品资源点击获取