ARTICLE DETAIL

资讯详情

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

基于Jupyter Notebook的糖尿病视网膜病变AI诊断项目全流程解析

基于Jupyter Notebook的糖尿病视网膜病变AI诊断项目全流程解析 简介深度学习作为人工智能的核心技术通过构建多层神经网络模型能够从海量数据中自动学习并提取复杂特征。其核心原理在于利用反向传播算法优化网络参数使模型能够拟合输入与输出之间的复杂映射关系。这一技术价值在于极大地提升了计算机在图像识别、自然语言处理等领域的感知与决策能力尤其在医疗影像分析等专业场景中展现出巨大潜力。在工程实践中一个完整的深度学习项目远不止模型构建更涵盖了数据获取、预处理、模型训练与评估、以及最终部署的全链路。其中数据预处理与增强是决定模型性能的关键环节而迁移学习则成为在小规模专业数据集上快速构建高效模型的实用策略。本文以糖尿病视网膜病变自动筛查为具体应用场景深入剖析了如何利用Jupyter Notebook、PyTorch等工具结合公开数据集和预训练模型构建一个可复现、可展示的完整医疗AI诊断项目为相关领域的毕业设计或工程实践提供了详实的参考模板。1. 项目概述从毕业设计到可复现的医疗AI诊断工具如果你正在为计算机视觉或生物医学工程相关的毕业设计发愁手头需要一个既有理论深度又有实践价值还能完整展示从数据处理到模型部署全流程的项目那么这个基于Jupyter Notebook的糖尿病视网膜疾病诊断系统很可能就是你一直在找的“宝藏模板”。这不是一个简单的算法演示而是一个麻雀虽小、五脏俱全的微型工程实践。它涵盖了从公开数据集获取、预处理、深度学习模型构建与训练到最终性能评估与可视化的完整链路。对于本科生或研究生而言其价值不仅在于提供了一个可以直接运行、观察结果的代码包更在于它清晰地示范了如何将一个复杂的医学图像分析问题拆解为一系列可执行、可调试的标准化步骤。使用Jupyter Notebook作为载体更是将探索性数据分析与实验记录完美结合你的每一步思考、每一次参数调整、每一个中间结果都能被即时呈现和保存这本身就是一份极具说服力的毕业设计过程文档。这个项目的核心目标是利用眼底彩照来自动筛查糖尿病视网膜病变。糖尿病视网膜病变是工作年龄段人群致盲的首要原因但早期筛查率并不理想。通过AI辅助诊断可以极大提升筛查效率减轻医生负担。项目中通常会包含一个结构清晰的源码目录、一个或多个公开数据集如Kaggle上的APTOS 2019或MESSIDOR、预训练的深度学习模型如ResNet, DenseNet, EfficientNet等以及详细的文档说明。接下来我将为你彻底拆解这个项目的每一个环节分享我在复现和改进类似项目时积累的实操经验与避坑指南。2. 项目整体架构与核心思路拆解一个完整的AI诊断项目远不止“调个库、跑个模型”那么简单。其背后是一套严谨的工程化思维。这个毕业设计项目的典型架构可以理解为一条环环相扣的流水线。2.1 核心流程与模块划分整个项目通常遵循标准的数据科学工作流并针对医学图像特点进行了定制。核心流程可以概括为数据获取与理解 - 数据预处理与增强 - 模型选择与构建 - 模型训练与验证 - 结果评估与可视化。在代码层面这通常会对应几个清晰的Python模块或Jupyter Notebook单元环境配置与数据加载模块确保所有依赖库如PyTorch/TensorFlow, OpenCV, Pandas, Scikit-learn正确安装并编写数据加载器能够正确读取图像文件及其对应的病变等级标签。数据探索性分析模块这是理解数据的第一步也是毕业设计报告中体现你分析能力的关键。你需要统计不同病变等级的图像数量分布通常极不均衡可视化一些样本图像观察图像的质量、亮度、对比度差异以及病变的典型表现如微动脉瘤、出血、渗出等。数据预处理与增强管道模块这是影响模型性能最关键的步骤之一。医学图像数据通常存在尺寸不一、亮度不均、样本量少且类别不平衡的问题。预处理包括调整图像尺寸、标准化像素值、可能还需要进行图像增强以增加数据多样性并缓解过拟合同时必须采用针对类别不平衡的策略如过采样、欠采样或使用加权的损失函数。模型定义模块这里包含了深度学习模型的核心结构。常见的选择是迁移学习即使用在ImageNet等大型数据集上预训练好的模型如ResNet50、InceptionV3、EfficientNet-B4作为特征提取器替换掉其最后的全连接层以适应我们特定的分类任务如分为5个DR等级。训练循环与验证模块这个模块实现了模型的训练逻辑。包括定义损失函数对于多分类常用交叉熵损失、优化器如Adam、学习率调度器以及编写训练和验证的循环代码。每一步都需要记录损失和准确率等指标。评估与可视化模块模型训练完成后需要在独立的测试集上进行评估。除了准确率更要关注精确率、召回率、F1分数以及混淆矩阵因为医学诊断中漏诊召回率低的代价可能很高。可视化部分包括绘制训练曲线、显示模型对测试样本的预测结果以及使用Grad-CAM等工具可视化模型关注的图像区域这对于解释AI决策、增加模型可信度至关重要。2.2 技术选型背后的考量为什么是Jupyter Notebook Python为什么常用PyTorch或TensorFlow这些选择背后有充分的理由。Jupyter Notebook它是数据科学和机器学习领域的“实验记录本”。其交互式特性允许你逐块运行代码即时查看输出如图像、图表、数据框非常适合进行探索性数据分析和模型调试。对于毕业设计它生成的.ipynb文件可以直接作为可交互的报告提交清晰地展示你的工作流程和思考过程。不过当项目规模变大时建议将稳定的代码重构为标准的.py脚本模块以提高可维护性和运行效率。Python拥有最庞大、最成熟的AI和科学计算生态NumPy, Pandas, Matplotlib, Scikit-learn社区支持极好任何问题几乎都能找到解决方案。PyTorch vs. TensorFlow/Keras两者都是优秀的选择。PyTorch的动态计算图更灵活调试直观在研究社区和教学场景中更受欢迎。TensorFlow尤其是其Keras高级API在生产部署和移动端支持上可能更有优势。对于毕业设计我通常推荐PyTorch因为它的代码更“Pythonic”易于理解且相关教程和开源项目极其丰富。预训练模型从零开始训练一个深度卷积神经网络需要海量数据和计算资源。迁移学习让我们能够利用在大规模通用图像数据集上学到的通用特征如边缘、纹理快速适配到特定的医学图像任务上这是一种非常高效且实用的策略。注意不要盲目追求最复杂、最新的模型。对于毕业设计一个设计良好的数据预处理管道和一个正确实施的ResNet34其最终效果和展示的完整性可能远胜于一个数据处理粗糙但模型用了最新SOTA最先进技术的项目。清晰、可复现的流程比模型复杂度更重要。3. 核心环节深度解析与实操要点理解了整体架构我们深入到几个最容易出问题也最能体现功力的核心环节。3.1 数据准备不止是下载和解压数据集是项目的基石。糖尿病视网膜病变公开数据集如APTOS 2019或MESSIDOR通常以压缩包形式提供。下载后第一件事不是急着写模型代码而是彻底“认识”你的数据。步骤一解压与目录组织我习惯建立一个清晰的目录结构例如project/ ├── data/ │ ├── train/ # 训练集图像 │ ├── test/ # 测试集图像 │ └── labels.csv # 训练集标签文件包含图像名和病变等级 ├── src/ # 源代码 ├── models/ # 保存训练好的模型权重 └── notebooks/ # Jupyter Notebook文件标签文件通常是一个CSV包含id_code图像文件名和diagnosis病变等级0-4两列。步骤二探索性数据分析在Jupyter Notebook中使用Pandas加载labels.csv用value_counts().plot(kindbar)快速查看类别分布。你会发现健康等级0的样本可能远多于重症等级4的样本这就是典型的类别不平衡。接着用OpenCV或PIL随机加载几张不同等级的图片显示出来直观感受一下数据。import pandas as pd import matplotlib.pyplot as plt # 查看分布 df pd.read_csv(data/labels.csv) print(df[diagnosis].value_counts()) df[diagnosis].value_counts().plot(kindbar) plt.title(Class Distribution) plt.show()步骤三设计数据加载器这是连接数据和模型的桥梁。在PyTorch中你需要自定义一个继承自torch.utils.data.Dataset的类。这个类的核心是__getitem__方法它根据索引返回一对图像张量 标签。from torch.utils.data import Dataset from PIL import Image class RetinopathyDataset(Dataset): def __init__(self, csv_file, img_dir, transformNone): self.labels_df pd.read_csv(csv_file) self.img_dir img_dir self.transform transform # 预处理和增强变换 def __len__(self): return len(self.labels_df) def __getitem__(self, idx): img_name os.path.join(self.img_dir, self.labels_df.iloc[idx, 0] .png) image Image.open(img_name).convert(RGB) # 确保三通道 label self.labels_df.iloc[idx, 1] if self.transform: image self.transform(image) return image, label3.2 数据预处理与增强模型性能的“胜负手”医学图像预处理的目标是减少无关噪声突出关键特征并使数据分布更一致。基础预处理调整尺寸将所有图像缩放到固定尺寸如224x224或512x512以满足模型输入要求。注意保持长宽比通常用中心裁剪或填充黑边的方式。标准化将像素值从[0, 255]缩放到[0, 1]或进行标准化减去均值除以标准差。使用预训练模型时必须采用该模型在ImageNet上使用的均值和标准差例如对于PyTorch的Torchvision模型常用mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]。数据增强这是在小数据集上防止过拟合、提升模型泛化能力的核心技术。对于眼底图像有效的增强包括几何变换水平/垂直翻转、随机旋转小角度、平移。这些变换模拟了拍摄时角度的微小变化。像素变换亮度、对比度、饱和度微调添加高斯噪声。模拟不同设备、拍摄条件导致的图像质量差异。高级增强使用albumentations库它提供了更丰富且针对视觉任务的增强方法并且与PyTorch兼容性好。实操心得增强的强度要适中。过强的增强如大角度旋转、剧烈颜色抖动可能会破坏眼底图像中病变的结构信息反而损害性能。建议先从温和的增强组合开始实验。处理类别不平衡采样策略在DataLoader中设置sampler参数。可以使用WeightedRandomSampler给少数类样本更高的采样权重。损失函数加权在交叉熵损失函数中为每个类别设置不同的权重少数类的权重更大。权重可以设置为类别频率的倒数。一个结合了预处理、增强和采样策略的数据管道示例from torchvision import transforms from torch.utils.data import DataLoader, WeightedRandomSampler # 定义变换 train_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ColorJitter(brightness0.1, contrast0.1), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ # 验证集不需要增强 transforms.Resize((256, 256)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 创建数据集 train_dataset RetinopathyDataset(csv_filedata/train_labels.csv, img_dirdata/train/, transformtrain_transform) # 计算采样权重 class_counts df[diagnosis].value_counts().sort_index().values class_weights 1. / torch.tensor(class_counts, dtypetorch.float) sample_weights class_weights[train_dataset.labels_df[diagnosis].values] sampler WeightedRandomSampler(weightssample_weights, num_sampleslen(sample_weights), replacementTrue) # 创建DataLoader train_loader DataLoader(train_dataset, batch_size32, samplersampler) # 验证集loader通常使用顺序采样 val_loader DataLoader(val_dataset, batch_size32, shuffleFalse)3.3 模型构建与迁移学习实战这里以PyTorch和ResNet50为例展示如何快速构建一个分类模型。import torch import torch.nn as nn import torchvision.models as models class DRClassificationModel(nn.Module): def __init__(self, num_classes5, pretrainedTrue): super(DRClassificationModel, self).__init__() # 加载预训练的ResNet50 self.backbone models.resnet50(pretrainedpretrained) # 获取原始全连接层的输入特征数 num_features self.backbone.fc.in_features # 替换最后的全连接层以适应我们的分类任务 self.backbone.fc nn.Sequential( nn.Dropout(0.5), # 添加Dropout防止过拟合 nn.Linear(num_features, 256), nn.ReLU(), nn.Dropout(0.3), nn.Linear(256, num_classes) ) def forward(self, x): return self.backbone(x) # 实例化模型并移至GPU device torch.device(cuda if torch.cuda.is_available() else cpu) model DRClassificationModel(num_classes5).to(device)关键点解析pretrainedTrue加载在ImageNet上预训练的权重这是迁移学习的核心。替换fc层原始ResNet的fc层输出1000维对应ImageNet的1000类我们需要将其替换为输出5维对应DR的5个等级的新网络层。这里我添加了Dropout和额外的线性层这是一个常见的微调结构有助于模型学习数据集特定的特征。冻结部分层在训练初期可以先将backbone特征提取器的大部分层冻结只训练新添加的fc层。训练几轮后再解冻所有层进行微调。这是一种防止预训练权重被小数据带偏的有效策略。可以使用for param in model.backbone.parameters(): param.requires_grad False来实现。4. 模型训练、验证与调优全流程有了数据和模型训练过程是将两者结合并优化的关键。4.1 训练循环的编写一个标准的训练循环包括前向传播、损失计算、反向传播和参数更新。同时必须有一个独立的验证循环来监控模型在未见数据上的表现防止过拟合。import torch.optim as optim from tqdm import tqdm # 用于显示进度条 criterion nn.CrossEntropyLoss() # 损失函数 optimizer optim.Adam(model.parameters(), lr1e-4) # 优化器 scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, patience3, factor0.1) # 学习率调度 num_epochs 30 train_losses, val_losses [], [] train_accs, val_accs [], [] for epoch in range(num_epochs): # 训练阶段 model.train() running_loss 0.0 correct 0 total 0 loop tqdm(train_loader, descfEpoch [{epoch1}/{num_epochs}] Train) for images, labels in loop: images, labels images.to(device), labels.to(device) optimizer.zero_grad() # 梯度清零 outputs model(images) # 前向传播 loss criterion(outputs, labels) # 计算损失 loss.backward() # 反向传播 optimizer.step() # 参数更新 running_loss loss.item() _, predicted outputs.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() # 更新进度条信息 loop.set_postfix(lossloss.item()) epoch_train_loss running_loss / len(train_loader) epoch_train_acc 100. * correct / total train_losses.append(epoch_train_loss) train_accs.append(epoch_train_acc) # 验证阶段 model.eval() val_loss 0.0 val_correct 0 val_total 0 with torch.no_grad(): # 验证时不计算梯度节省内存和计算 for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) val_loss loss.item() _, predicted outputs.max(1) val_total labels.size(0) val_correct predicted.eq(labels).sum().item() epoch_val_loss val_loss / len(val_loader) epoch_val_acc 100. * val_correct / val_total val_losses.append(epoch_val_loss) val_accs.append(epoch_val_acc) scheduler.step(epoch_val_loss) # 根据验证损失调整学习率 print(fEpoch {epoch1}: Train Loss: {epoch_train_loss:.4f}, Train Acc: {epoch_train_acc:.2f}% | Val Loss: {epoch_val_loss:.4f}, Val Acc: {epoch_val_acc:.2f}%) # 保存最佳模型 if epoch_val_acc best_acc: best_acc epoch_val_acc torch.save(model.state_dict(), models/best_model.pth)4.2 超参数调优与实验管理训练过程中的超参数选择直接影响最终结果。以下是一些核心参数和我的调优经验超参数常见范围/选择调优建议学习率 (lr)1e-5 到 1e-3迁移学习初期建议较小如1e-4微调所有层时可稍大。使用ReduceLROnPlateau或CosineAnnealingLR调度器自动调整。批大小 (batch_size)16, 32, 64受GPU内存限制。太大会降低泛化能力太小会导致训练不稳定。32是一个不错的起点。优化器Adam, AdamW, SGDAdam自适应性强常作为首选。SGD配合动量如0.9和适当的学习率衰减有时能获得更好的最终精度。权重衰减1e-4, 1e-5一种正则化防止过拟合。从1e-4开始尝试。Dropout率0.3, 0.5在全连接层后添加随机丢弃神经元。0.5是常用值可根据模型过拟合程度调整。实验管理强烈建议使用TensorBoard或Weights Biases这类工具来记录每一次实验的超参数、训练曲线和验证指标。这能让你清晰地对比不同设置的效果是科学调优的基础。在Jupyter中你可以通过%load_ext tensorboard来集成TensorBoard。4.3 模型评估与结果可视化训练完成后在独立的测试集上进行最终评估。不要只看准确率。from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay # 加载最佳模型 model.load_state_dict(torch.load(models/best_model.pth)) model.eval() all_preds [] all_labels [] with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) # 生成分类报告 print(classification_report(all_labels, all_preds, target_names[0, 1, 2, 3, 4])) # 绘制混淆矩阵 cm confusion_matrix(all_labels, all_preds) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labels[0, 1, 2, 3, 4]) disp.plot(cmapplt.cm.Blues) plt.show()分类报告会给出每个类别的精确率、召回率、F1分数混淆矩阵能直观看出模型在哪些类别间容易混淆例如把等级2预测为等级1。可视化模型注意力Grad-CAM这对于医学AI的可解释性至关重要。Grad-CAM可以生成一个热力图显示模型在做决策时更关注图像的哪些区域。# 此处为Grad-CAM实现的核心思路伪代码实际需借助torchcam等库 # 1. 前向传播获取目标层的特征图。 # 2. 计算目标类别得分相对于该特征图的梯度。 # 3. 对梯度进行全局平均池化得到每个特征图的权重。 # 4. 将权重与特征图加权求和并通过ReLU激活得到粗略的注意力热力图。 # 5. 将热力图缩放到原图大小并叠加显示。使用torchcam库可以相对方便地实现。在报告中展示几个正确和错误预测案例的Grad-CAM热力图能极大提升工作的深度和说服力。5. 项目部署与文档编写指南一个优秀的毕业设计不仅要有好的代码和结果还要有清晰的文档和展示。5.1 代码整理与工程化在Jupyter Notebook中完成核心实验后应将代码重构以提高可读性和可复用性。模块化将数据加载、模型定义、训练循环、工具函数分别写入不同的.py文件如data_loader.py,model.py,train.py,utils.py。配置文件使用config.yaml或args.py来集中管理所有超参数和路径避免硬编码。主入口创建一个main.py或run.py作为项目启动的入口。依赖管理使用requirements.txt或environment.yml精确记录所有Python库的版本确保他人可以复现环境。5.2 文档说明撰写要点你的README.md文档是项目的门面应该包含项目标题与简介一句话说明项目是什么。主要特性列出项目的核心功能和技术亮点。环境依赖如何安装环境如pip install -r requirements.txt。数据集准备详细说明数据集的下载链接、存放路径结构。快速开始用最简单的命令说明如何训练和测试模型。代码结构用树状图展示项目目录说明每个文件/文件夹的作用。模型性能展示在测试集上的关键指标如准确率、F1分数最好有混淆矩阵或ROC曲线的图片。可视化结果展示几张模型预测结果和Grad-CAM热力图的示例图。参考文献引用使用到的数据集、预训练模型和相关论文。5.3 制作简易的本地演示界面为了让答辩更出彩可以做一个基于Gradio或Streamlit的简易Web演示界面。这能直观展示你的模型“工作”的过程。# 一个极简的Gradio示例 import gradio as gr import torch from PIL import Image from model import DRClassificationModel # 导入你定义好的模型 model DRClassificationModel() model.load_state_dict(torch.load(best_model.pth, map_locationcpu)) model.eval() def predict(image): # 将上传的图片进行与训练时相同的预处理 processed_img preprocess(image) # 你需要实现这个预处理函数 with torch.no_grad(): output model(processed_img) prob torch.nn.functional.softmax(output, dim1) pred_class torch.argmax(prob).item() return f预测病变等级: {pred_class}, 置信度: {prob[0][pred_class]:.2%} iface gr.Interface(fnpredict, inputsgr.Image(typepil), outputstext) iface.launch()运行这段代码会生成一个本地网页允许你上传眼底图片并实时看到预测结果。6. 常见问题排查与避坑实录在实际操作中你一定会遇到各种报错和不如预期的结果。这里记录了几个最典型的问题和我的解决思路。6.1 环境与依赖问题问题ImportError或ModuleNotFoundError。排查仔细检查错误信息确认缺失的库名。使用pip list查看已安装的包。解决使用pip install安装缺失的库。强烈建议使用虚拟环境如conda或venv管理项目依赖并通过pip freeze requirements.txt导出环境。问题Jupyter Notebook内核无法启动或导入PyTorch时报CUDA错误。排查在终端中运行python -c import torch; print(torch.cuda.is_available())检查CUDA是否可用。确认PyTorch版本与CUDA版本匹配。解决去PyTorch官网根据你的CUDA版本获取正确的安装命令。如果无GPU则安装CPU版本的PyTorch。6.2 数据加载与处理问题问题数据加载器报错提示图片路径错误或无法解码。排查在Dataset的__getitem__方法中添加print(img_name)检查路径拼接是否正确。确认图片格式jpg, png与代码中读取的扩展名一致。解决确保csv文件中的文件名与磁盘上的文件名完全一致包括扩展名。使用PIL的Image.open()时用try...except包裹跳过损坏的图片文件。问题训练时损失不下降或准确率始终在随机水平对于5分类约20%。排查数据问题检查数据标签是否正确预处理和增强是否破坏了图像信息可以可视化几个经过transform后的batch看看。模型问题检查模型输出层维度是否与类别数匹配。检查是否错误地冻结了所有层导致参数不更新。优化问题学习率是否设置过高或过低尝试一个更大的学习率如1e-3或更小的学习率如1e-5进行快速实验。解决这是最复杂的问题。建议采用“控制变量法”调试先在一个极小的、能过拟合的数据子集比如每类5张图上训练如果模型能快速达到接近100%的训练准确率说明模型和训练代码基本正确。然后再扩展到全量数据。6.3 训练过程问题问题训练损失变为NaN。排查通常是梯度爆炸导致。可能的原因有学习率太大、数据未标准化、网络层中有除零操作。解决降低学习率确保数据经过标准化像素值范围合理在训练循环中加入梯度裁剪torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)。问题验证准确率大幅波动。排查批大小可能太小验证集数据可能太少或分布与训练集差异大。解决在内存允许的情况下增大batch_size。确保训练集和验证集是随机划分的且分布大致相同。可以计算并对比两个集合的类别分布。6.4 模型性能不佳问题问题模型在测试集上表现远差于验证集。排查这是典型的数据泄露或过拟合。检查是否在预处理如标准化时错误地使用了测试集的数据来计算均值和标准差。或者训练集和测试集的数据分布本身差异很大。解决所有从数据中计算的统计量均值、标准差必须仅从训练集计算然后应用到验证集和测试集。确保训练、验证、测试集是独立同分布划分的。问题对于少数类如等级3、4的召回率极低。排查类别不平衡问题未处理好。解决回顾并加强类别不平衡处理策略。尝试更激进的过采样如SMOTE、使用Focal Loss替代标准交叉熵损失它对难分类样本给予更高权重或者收集/生成更多少数类样本。在整个项目过程中养成勤记录、多备份的习惯。每进行一次重要的参数修改或代码变更最好能保存一份对应的模型权重和日志。使用Git进行版本控制更是必不可少它能让你放心地尝试任何想法而无需担心无法回退。这个基于Jupyter的糖尿病视网膜病变诊断项目就像一套完整的“外科手术工具包”从解剖问题数据分析到选择工具模型选型再到精细操作训练调优和术后复盘评估可视化每一步都蕴藏着学问。希望这份超详细的拆解能帮你不仅顺利完成毕业设计更能真正理解一个AI项目从零到一的完整生命周期。当你亲手跑通整个流程看到模型输出的热力图精准地定位到眼底的出血点时那种成就感会是学习路上最好的奖励。本文还有配套的精品资源点击获取
返回列表