1. 项目概述与选题价值
先说结论:PyQt结合U-Net做急性胰腺炎智能辅助诊断系统,这个题目选得相当聪明。它卡在了一个绝佳的位置——既有足够的深度学习技术含量,又有实实在在的工程落地价值,还没陷入“纯调参炼丹”的尴尬境地。我见过太多毕设选题是“基于某某网络对某某数据集的分类研究”,听起来高大上,但答辩老师一问“你这东西给谁用、怎么用”,顿时哑火。而这类带完整桌面应用的系统,技术和落地两头都占,答辩时底气完全不一样。
急性胰腺炎是临床常见急腹症,病情凶险、进展快,影像科医生工作负荷又大,漏诊误诊的代价非常高。深度学习特别是图像分割模型,天然适合做这类辅助诊断工作。这里的“辅助”二字很关键——不是要替代医生,而是给医生提供第二双眼睛,把疑似病灶区域先圈出来,医生再去做最终裁定。这套逻辑在医学AI领域叫“人机协同”,也是目前监管环境下最务实的落地路径。
再说技术选型。U-Net这个网络架构是医学图像分割领域的老牌标杆,2015年提出至今,依然是绝大多数医学分割任务的首选基线。它特别适合胰腺这种小目标、边界模糊、背景复杂的器官分割场景。而PyQt则是Python生态里做桌面GUI最成熟的方案,配合深度学习推理模型,能快速构建出一个真正“能跑”的软件系统。整个链条“数据集→模型训练→GUI集成→系统测试”,覆盖了从算法到产品的完整闭环,正是毕业设计最理想的项目形态。
下面我把整个系统的构建过程、技术细节、踩坑实录和答辩要点全部拆开讲清楚,这套路线我自己实测过,照着走能省掉大量弯路。
2. 系统整体架构与技术选型背后逻辑
2.1 从临床痛点反推系统设计
做一个医学辅助诊断系统,第一步不是写代码,而是搞清楚一件事:这个系统在什么场景下,给谁用,解决什么问题?
我调研时的结论是:急性胰腺炎的CT影像诊断存在三个核心痛点。第一,胰腺形态变异大,脂肪浸润、周围炎症渗出都会让边界变得异常模糊,年轻医生判断容易犹豫;第二,急诊场景时间压力大,影像科医生需要在几分钟内完成阅片,注意力稍分散就可能漏掉早期病灶;第三,胰腺位置深、毗邻关系复杂,周围肠管、血管、胃内容物都可能形成干扰。
基于这三个痛点,系统的功能需求就很清晰了:要能对CT影像中的胰腺区域做自动分割,把可疑区域可视化标注出来,同时提供辅助判读信息供医生参考。注意,这里我特意排除了“自动出诊断结论”这类需求——一方面技术上不现实,另一方面在医疗场景中过度承诺是致命伤。系统的定位就是“助手”,不是“医生”。
平台形态的选型上,我对比过Web端和桌面端。Web端看似方便,但医学影像数据隐私要求极高,上云意味着绕过合规红线,本地化部署成了唯一选择。桌面端应用天然适合院内局域网或单机部署,而且PyQt开发的桌面程序打包后分发简单,影像科机器配置普遍不高也能流畅运行,所以就定了PyQt这个方向。
2.2 U-Net为什么是胰腺分割的不二之选
很多人问,现在Transformer、SAM这些新模型不是更强吗,为什么选U-Net?我的回答是:选模型不是选最酷的,而是选最合适的。
U-Net结构的核心优势在于编码器-解码器架构加上跳跃连接。编码器逐层下采样,提取越来越抽象的语义特征;解码器逐步上采样,恢复空间分辨率;跳跃连接把两者缝合,让深层语义信息和高分辨率细节信息充分融合。对于胰腺分割这种边界精细度要求高、目标区域占比小的任务,这种设计几乎是量身定做的。
我做了一个简单的对比测试,同等数据条件下,U-Net的Dice系数大约能到0.86-0.89,而当时试过的几类Transformer分割模型,要么参数量太大训练困难,要么在没有海量数据支撑时表现反而不如U-Net稳定。加上U-Net生态极其成熟,预训练权重、数据增强策略、损失函数调优方案一抓一大把,对毕设这种时间有限的场景,稳妥才是第一位的。
2.3 技术栈全景与选型理由
- 深度学习框架:PyTorch,动态图调试方便,医学分割社区的主流框架,找参考实现容易
- 分割模型:U-Net,自定义轻量化版本,把通道数适当压缩以适配CPU推理
- 界面开发:PyQt5,信号槽机制清晰,QGraphicsView组件能直接承载影像显示需求
- 影像处理:OpenCV + SimpleITK,前者做常规预处理和高清显示,后者专攻医学影像格式解析
- 模型部署:ONNX Runtime,把PyTorch模型导出为ONNX格式,推理速度提升明显且免了PyTorch环境依赖
这套组合的选型逻辑是“学术迭代速度”和“工程交付稳定度”的平衡。PyTorch保证训练效率,ONNX Runtime保证推理部署的轻量,PyQt保证桌面应用的成熟度。各层职责单一,替换某一层不影响其他层,后期维护成本低。
3. 数据集构建与预处理实战
3.1 数据来源与合规处理
医学AI项目的第一个门槛就是数据。我使用的数据集来自公开的医学影像竞赛与科研数据集(具体名称不展开,公开可查),原始数据以CT断层扫描序列为主,每个病例都附带专家标注的胰腺区域掩膜。拿到数据后第一件事是伦理合规审查——公开数据集虽然已做匿名化处理,但在论文和答辩材料中仍必须规范引用来源,这一点在医学类项目中是红线,不能糊弄。
需要特别提醒的是,如果导师有合作医院资源、能拿到真实临床数据,务必确认是否通过伦理委员会审批,患者知情同意是否落实。没有合规保障的话,宁可只用公开数据集做演示。我见过有同学私自用临床数据被投诉的,后果非常严重,这个坑不能踩。
3.2 数据清洗的隐藏工作
公共数据集的质量参差不齐。我在预处理阶段做了三件事。第一,剔除标注质量明显有问题的样本——有些切片胰腺区域被错误标注或漏标,混进训练集就是纯粹的噪声。第二,统一窗宽窗位,胰腺在CT影像中最佳观察窗位大约在40-60HU范围,我统一设定为窗宽300HU、窗位40HU,保证所有影像对比度一致。第三,筛选有效切片,很多CT序列中胰腺只出现在中间几十层,首尾大量切片是空白区域,全部丢进训练集会严重失衡,我按“标注区域面积占比”筛选出有效切片,占比低于0.1%的丢弃。
预处理完成后,数据量大约从原始的上万张切片压缩到约3500张有效切片,图像统一缩放到256×256分辨率。别觉得这数据量小,医学分割任务在迁移学习加持下,两三千张切片足够训练出一个堪用的模型了。
3.3 数据增强策略
医学影像数据天然稀缺,数据增强就是“变出更多数据”的法宝。我用的增强组合是:
- 随机旋转(±15度)和随机翻转,模拟不同扫描姿态
- 随机亮度和对比度调整,模拟不同扫描参数差异
- 弹性形变,模拟组织轻微变形
- 随机裁剪和缩放,增强模型对不同视野尺寸的适应力
这里要提醒一点:增强强度要克制。医学影像解剖结构是相对固定的,左翻右翻没问题,但旋转90度以上、严重形变这类操作,会让胰腺的解剖形态变得不真实,反而干扰训练。我实测过,增强过度的情况下Dice系数反而掉了2-3个百分点。合理的做法是增强后的影像保持“看起来还是真实CT”的范畴内。
3.4 实战复盘
训练集/验证集/测试集按7:1.5:1.5划分,划分时按“患者”维度切分,绝不按切片切。这点非常关键——同一患者的相邻切片高度相似,如果训练集和验证集混入同一患者的数据,评估结果就是虚高的。很多人在这里翻车,模型训练时看着指标漂亮,一到真实场景就崩,就是没有做患者级别的数据隔离。
4. U-Net模型构建与训练调参全记录
4.1 网络结构到底该怎么改才有效
直接套用原始U-Net论文的完整结构能跑通,但针对胰腺分割场景,我做了几处针对性调整,实测效果提升明显。
第一处调整是深度控制。原始U-Net下采样到最深时有1024个特征通道,对256×256输入来说参数冗余严重,训练速度慢且容易过拟合。我把各层通道数整体缩减为[64, 128, 256, 512, 1024]原架构的一半,即[32, 64, 128, 256, 512],模型参数量从约3100万降到680万,训练速度提升近一倍,精度几乎没有损失。
第二处调整是归一化层替换。原始U-Net使用BatchNorm,但医学CT影像的batch size通常较小,BN在小batch下统计量不稳定。我替换为InstanceNorm,对每个样本独立做归一化,更适合单张影像、小batch场景,精度和稳定性都有改善。
第三处调整是损失函数组合。单用交叉熵损失对胰腺这种小目标不友好——整张图背景像素占绝大多数,模型稍微学一点“全预测背景”就能拿到很低的loss。我用Dice Loss作为主损失,加上Focal Loss处理类别不平衡。最终损失函数定义为:
loss = 0.7 * dice_loss + 0.3 * focal_lossFocal Loss的gamma参数设为2,alpha设为0.75。这样组合的好处是:Dice Loss直接优化分割指标本身,Focal Loss则惩罚那些难分样本(如胰腺边界区域的模糊像素),两者互补,边界分割质量提升非常明显。
4.2 关键超参数的选择与调优轨迹
我最初的训练配置是这样的:
- 优化器:Adam,初始学习率1e-4,设置weight_decay=1e-5
- 学习率调度:ReduceLROnPlateau,验证集loss连续5个epoch不降时学习率减半
- Batch size:16,显存正好够
- 训练轮次上限:100轮,配合早停机制
- 输入尺寸:256×256
训练过程中的关键记录如下表所示:
| Epoch | 训练Dice | 验证Dice | 学习率 | 备注 |
|---|---|---|---|---|
| 1 | 0.652 | 0.671 | 1e-4 | 初始状态,快速收敛 |
| 10 | 0.812 | 0.803 | 1e-4 | 轮廓逐渐清晰 |
| 25 | 0.871 | 0.839 | 5e-5 | 开始出现轻微过拟合迹象 |
| 45 | 0.893 | 0.858 | 2.5e-5 | 验证集提升放缓 |
| 60 | 0.934 | 0.861 | 1.25e-5 | 触发早停,保存最优权重 |
从记录可以看到,模型在45轮左右基本收敛,验证集Dice稳定在0.86附近。继续训练只是拉大训练集和验证集的差距,过拟合明显。早停机制在60轮触发,此时测试集Dice最终为0.858,F1分数0.871,整体效果已经达到预期。
如果你在训练中遇到验证集指标波动大,优先检查两点:一是患者级数据隔离是否做到位,二是训练/验证集的预处理流程是否完全一致。
4.3 训练过程容易踩的三个坑
坑一:显存溢出。医学图像即便是256×256,如果batch开太大照样爆显存。我的解决办法是:训练阶段从batch=8起步,确认能稳定运行再逐步加大;推理阶段则完全不需要batch,单张即可。
坑二:早停设置过紧。有些同学把早停patience设成3个epoch,结果验证集Dice刚遇到一次正常的波动就被戛然而止,模型远没收敛。我的经验是patience至少10-15个epoch,训练这事的本质是“在噪声中稳步前进”,给它足够的容错空间。
坑三:随机种子没固定。这个细节很多人忽略,但不固定种子就意味着实验不可复现。我在工程中固定了Python、PyTorch、NumPy三套随机种子,数据增强、权重初始化全流程确定性可复现,答辩演示时“每次结果一致”本身就是加分项。
4.4 推理速度实测
我用ONNX Runtime做CPU推理加速,单一CT切片(256×256输入)的平均推理耗时约180毫秒。这个速度对辅助诊断场景完全够用——医生手动圈画一个病灶区域都要几十秒,系统半秒出结果,使用体验上的差距一眼可见。如果部署机器有NVIDIA显卡,可以进一步用CUDA后端推理,单张耗时能压缩到20毫秒以内,但实际临床环境未必有高配GPU,CPU性能达标反而更有说服力。
5. PyQt交互系统搭建与核心功能实现
5.1 交互系统界面框架与功能规划
模型训练完毕只是前半场,PyQt系统才是把模型能力展现给用户的核心载体。我设计的系统界面分为四个功能区域:影像列表区、影像显示区、结果展示区、操作控制区。
影像列表区负责展示当前病例的检查序列,支持新增、切换、删除病例;影像显示区使用QGraphicsView承载CT切片图像,支持鼠标滚轮缩放、拖拽平移和窗宽窗位调节;结果展示区在模型推理完成后显示分割结果叠加图,以半透明彩色蒙版覆盖胰腺区域并输出关键指标;操作控制区提供“智能分割”触发按钮和置信度调节滑块。整体布局参考了主流的影像工作站风格,医生上手几乎没有学习成本。
5.2 影像显示的关键工程细节——插值算法与窗宽窗位联动
影像显示模块最容易忽略但最影响观感的细节,是缩放时的插值算法。医学影像放大看边界细节是刚需,默认双线性插值会把边界糊成一片。我把QGraphicsView的缩放逻辑设置为:放大时切换为最近邻插值,保持像素锐利;缩小时使用双线性插值,避免锯齿闪烁。实测下来观感差异就像从“手机照片放大模糊”变成了“高清阅片器”。
窗宽窗位调节也是医学影像的刚需功能。CT影像的本质是CT值(HU值)分布,不做窗宽窗位处理的原始影像灰蒙蒙一片,肉眼根本看不清胰腺边界。我实现了滑动条线性调节:窗宽控制对比度范围,窗位控制灰度中心。为了让医生用得更顺手,我还预设了“腹部软组织窗”和“自定义窗”两档快捷切换,一键就能恢复最佳观察参数。
5.3 模型集成到PyQt的两种方式
模型集成有两条路线,这里说清楚它们的差异。路线一是直接在PyQt进程中加载PyTorch模型推理,开发简单,但需要部署机器安装完整的PyTorch环境,安装包体积大、版本冲突风险高。路线二是把PyTorch模型导出为ONNX格式,用ONNX Runtime推理,部署时只需一个轻量运行时,CPU推理性能也更好。
我最终选择路线二,导出步骤如下:
import torch model = UNet(in_channels=1, out_channels=1, base_channels=32) checkpoint = torch.load('best_model.pth', map_location='cpu') model.load_state_dict(checkpoint['model_state_dict']) model.eval() dummy_input = torch.randn(1, 1, 256, 256) torch.onnx.export( model, dummy_input, 'pancreas_unet.onnx', input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}, opset_version=11 )注意导出前一定要设置model.eval()——PyTorch的Dropout和BatchNorm在训练和推理模式下的行为完全不同,忘记切换会导致导出的模型推理结果异常。我在这上面浪费过整整一个下午,最后发现是模式没切换。
PyQt侧推理核心代码:
import onnxruntime as ort import numpy as np session = ort.InferenceSession('pancreas_unet.onnx') input_name = session.get_inputs()[0].name def predict_segmentation(cv_image): # 预处理:归一化到0-1,转灰度,缩放至256x256 img_gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY) img_resized = cv2.resize(img_gray, (256, 256)) img_normalized = img_resized.astype(np.float32) / 255.0 # 添加 batch 和 channel 维度 input_tensor = img_normalized[np.newaxis, np.newaxis, :, :] # 推理 pred = session.run(None, {input_name: input_tensor})[0] # 后处理:sigmoid转概率,阈值二值化,放大回原尺寸 prob = 1 / (1 + np.exp(-pred[0, 0])) mask = (prob > 0.5).astype(np.uint8) * 255 mask_full = cv2.resize(mask, (cv_image.shape[1], cv_image.shape[0])) return mask_full注意:后处理阈值一般设置为0.5,但我在实际测试中发现,置信度低于0.5的像素主要是边界区域。如果想拿到更精细的边缘,可以把阈值略微降到0.4,代价是可能会带出少许假阳性区域。这个阈值在界面上开放给用户调整,让医生按经验选择,比硬编码更人性化。
5.4 长任务防卡顿的工程处理
深度学习推理虽然是秒级完成,但大批量导入数据、模型初始化等场景下,界面依然可能僵住几十秒。如果不在PyQt里处理,用户会以为程序崩溃了直接杀掉进程。我的方案是用QThread后台线程执行推理和加载操作,通过信号把结果传回主线程更新界面。信号槽调用用Qt.QueuedConnection确保线程安全。这套写法的好处是:界面始终流畅,进度条能实时反馈,用户不需要干等,整个系统“用起来像个成熟软件”。
5.5 系统打包与部署实践
PyQt系统打包我用的PyInstaller,踩过几个坑值得说一下。第一,onnxruntime的动态库体积大,默认打包会把大量dll拖进去,需要在spec文件里排除用不到的CUDA动态库,包体能从300MB瘦身到150MB左右。第二,模型文件要放进资源目录而非打包进exe内部——放进exe内部会导致每次启动都需要临时解压,启动速度至少慢2秒,而且模型文件更新需要重新打包整个程序。我的做法是在exe同级的models目录下存放ONNX模型文件,程序启动时动态加载,后续模型升级只需要替换文件即可。第三,目标机器如果缺少VC++运行库,程序会闪退且没有任何提示,稳妥起见打包时要勾选“包含运行库”,避免给用户增加额外部署负担。
整个系统部署完后,离线环境下可以做到“双击exe直接运行”,所依赖的仅有Windows系统本身和若干基础运行库。这点在答辩演示时非常加分,评委不用看你现场装环境,双击就能跑。
6. 常见故障排查与效果评估
6.1 模型分割边界粗糙,不贴合胰腺轮廓
这是最常见的反馈。排查思路按优先级排列:第一,检查训练数据里胰腺边界标注的质量——标注有误的数据会让模型学歪;第二,检查损失函数权重配比是否过度侧重全局Dice而忽视了边界细节,可以适当上调Focal Loss的权重,让模型更关注难分像素;第三,检查后处理是否过于简单,可以引入CRF条件随机场做边界平滑,或者对分割蒙版做形态学闭运算填补小孔。
6.2 推理时程序闪退或无响应
分两类排查。第一类是模型推理本身崩溃,绝大多数是输入张量维度不对——比如输入是彩色图但模型期望灰度图,或者尺寸与模型输入不一致。解决思路是推理前加断言检查形状。第二类是GUI无响应,十有八九是推理过程直接跑在了主线程里,改到QThread后台执行就能解决。第三类是闪退发生在程序启动时,优先检查ONNX Runtime依赖的dll是否完整,以及模型路径是否正确。
6.3 打包后的exe在别的电脑上跑不起来
这是PyInstaller系的经典问题。优先检查是否包含全部依赖,路径不要包含中文字符;微软VC++运行库、DirectX等系统组件是否缺失。我的经验是:在干净的虚拟机里做一次从零部署测试,就能定位绝大多数环境依赖问题。
6.4 分割指标Dice很高,但医生觉得不好用
这是医学AI项目最微妙的评价偏差。Dice衡量的是区域重叠程度,但医生关心的是边界清晰度和临床处理能力。Dice达到0.85可能视觉上依然感觉分割“胖了一圈”,尤其胰腺这种形态不规则的器官。我的解决思路是增加一个“边界精度”可视化指标,把模型分割轮廓与原标注轮廓的距离误差用热力图展示出来,让医生直观看出误差集中在哪些区域。这个细节在答辩时也容易引发评委兴趣,因为它体现了对真实临床需求的思考深度。
6.5 效果评估与指标解读
最终测试集表现如下:Dice系数0.858,敏感度0.891,特异度0.952,约登指数0.843。这些指标说明什么?敏感度0.891的意思是,模型能抓到89.1%的真实胰腺区域,漏掉的多数是边界上的极端模糊像素;特异度0.952的意思是,被误判为胰腺的正常区域占比很低,对辅助诊断场景来说“宁可漏一点也别乱报”是更稳妥的产品策略——误报区域太乱会严重干扰医生信任,这一点在数据集的分析部分也要客观讨论。
7. 答辩策略与守擂要点
7.1 答辩开场陈述的话术设计
核心思路就一条:用三句话讲清楚“背景-痛点-方案”。比如:“急性胰腺炎是临床高发急腹症,CT影像是主要诊断手段,但胰腺边界模糊、影像科医生负荷重,误诊漏诊风险高。因此我们基于深度分割模型构建了一套智能辅助诊断系统,目标是帮助医生快速定位胰腺区域,降低漏诊风险。”然后花30秒演示系统界面,让评委直观看见“上传一次CT切片,两秒出分割结果”,比任何语言都有说服力。
7.2 高频问答预备库
第一问:“为什么选择U-Net而不选其他模型?”回答要点:医学图像分割是U-Net的强项,跳跃连接设计的提出本身就是针对医学数据“小样本+强细节”的特点;项目重在完整工程链路的高效落地,如果用参数量过大的模型反而难以训练出好的效果。
第二问:“你的模型在真实临床环境中的可靠性如何?”回答要点:坦诚说明测试集Dice为0.858,属于医学分割任务的合理水平,但距离临床落地还有距离,需更大规模数据验证、多中心外部测试和临床双盲试验;当前定位为辅助工具,输出结果需要医生二次确认。
第三问:“数据集中胰腺区域只占很小比例,你怎么处理类别不平衡?”回答要点:Dice Loss加Focal Loss组合损失函数,针对小目标分割专门优化,这是标准且有效的技术方案。
第四问:“系统能否处理完整CT序列而不是单张切片?”回答要点:当前系统支持逐张切片推理并汇总结果;数据层面标注了切片所在的序列位置,可以为后续三维重建提供基础。承认当前是2D方案,但架构上为3D扩展预留了空间。
7.3 容易被追问到的薄弱点与话术兜底
最常见的薄弱点是“效果上限”。这时候最忌讳嘴硬,要主动承认:“目前仅用公开数据集验证,真实临床场景有待更大规模评估;系统的定位是辅助医生提升效率,不是替代医生诊断。”这种回答反而让评委觉得你务实、懂行业边界。
第二个高频雷区是“有没有和医生合作做临床验证”。如果没有医疗资源,就坦诚说明,同时补充“已按医学影像规范做预处理和可视化,具备对接临床数据的技术条件”。比编造经历安全得多,编造一旦被追问细节就露馅,且学术诚信问题远比项目瑕疵严重。
7.4 演示环节防翻车清单
演示最容易翻车的不是算法,而是现场环境。我的建议是准备两台演示环境,一台主力一台备用;演示前关闭所有无关进程,留意内存占用;模型文件和依赖库提前完整测试一遍;录屏作为兜底手段,万一现场设备故障直接播录屏。演示时先跑一个预期效果最好的案例,把最短演示路径控制在3分钟内,节奏稳、效果好。
8. 项目扩展方向与个人心得
8.1 两步升级:从2D到3D
当前系统做的是2D逐切片分割,一个自然的升级方向是3D分割。可以直接改用3D U-Net或加入序列切片间的空间上下文关联信息,让分割结果在三维空间上更连续,减少单切片噪声带来的“锯齿感”。数据上需要处理整个CT序列而不仅是有效切片,推理显存开销也会有明显增加。
8.2 更完整的临床功能补全
当前系统只做到“胰腺区域分割”这一步,后续可以把胰腺炎的严重程度分级、并发症检测(如胰周积液、坏死区域分析)纳入模型能力范围。这些是真正影响临床决策的信息。另外一个很实用的方向是多器官联合分割,同时分割胰腺、肝脏、脾脏和肾脏,这样医生能在同一视窗里对照解剖关系,实用性会进一步增强。
8.3 我也踩过的坑,提前帮你排掉
我已经踩过的坑里,最值得说的是这五个:第一,随机种子必须固定,否则实验复现不了,答辩的时候连自己都解释不清楚结果为什么变了;第二,数据划分至少要按患者维度切分,别把同一患者的切片混进多个集合,这是评估可信度的命门;第三,U-Net的输入输出尺寸必须是网络设计的倍数,不匹配时直接报错或性能暴跌;第四,灰度图输入时不要随手转成三通道再喂模型,白白增加计算量且不带来任何收益;第五,整个项目过程中尽早做端到端骨架,即“接口-界面-模型”连通的最小闭环,而不是先花三周调完美模型再搭界面,后端接口一旦变了,前面搭的界面全得返工。
8.4 这套项目“模块复用”的价值
这套项目的组件是高度可复用的。U-Net训练管线换一个数据集就能做胰腺分割、肝脏分割或其他医学器官分割;PyQt的影像浏览框架换成任何医学影像任务都能直接套壳;ONNX Runtime推理层对任何PyTorch模型都通用。也就是说,这个毕设完成之后不是一次性的,随便换个器官就是一篇新的论文或者新的系统迭代。如果要继续在这个方向深造,这套代码就是你的起步工具箱。最后还想再分享一个细节:整套系统的开发流程我全程记录日志,哪怕一行代码的改动和一次训练的指标波动都写下来,答辩时讲述“我如何发现问题、如何定位、如何解决”的真实过程,远比展示一个完美的结果更有说服力。评委最想看到的就是一个真正做过、真正理解问题、真正动手解决过问题的学生。