一张 CT 通常包含两三百个切片,每个切片要过器官分割、病灶检测、报告生成三段模型流水线,全流程推理 30 到 60 秒——而临床给的周转时限是 5 分钟。更麻烦的是医院特有的四条硬约束:精度不能打折、影像数据不能出内网、机房往往只有一两张 GPU 卡、急诊流量完全不可预测。在这个约束集里做提速,靠的不是某一个银弹,而是一套组合拳。
先算清账:慢在哪
串行处理 400 个切片、单片 50ms,光推理就要 20 秒,还没算前后处理。单切片推理时 GPU 利用率普遍不到三成,大量时间耗在 kernel 启动开销上——这说明两个方向:横向并行切片,纵向合并批次。
量化要小心:不是所有层都能省
FP32 转 INT8 理论上显存砍四分之三、速度翻两三倍,但医疗模型对数值精度极其敏感。工程上有个容易被忽略的坑:PyTorch 的动态量化只对 Linear、RNN 这类层生效,给 quantize_dynamic 传 Conv2d 参数它只会默默忽略——以为全模型量化了,实际卷积层纹丝不动,提速自然不及预期。卷积主导的视觉模型要走静态量化或 QAT 路线,用校准集统计激活分布。
量化后必须过精度回归:固定一批 DICOM 测试样本,对比分割结果的 Dice 系数,降幅超过 1% 就回退该层。病灶检测里的小目标对量化噪声格外敏感,稳妥做法是做逐层敏感度分析,把最敏感的几层留在 FP16。
TensorRT 编译:换卡即失效
TensorRT 把网络编译成针对具体 GPU 架构优化的 engine,这一步收益大(算子融合、半精度调度),代价是 engine 与设备绑定。换一张不同算力代次的卡,加载就报 invalid resource handle,只能重新 build。另外 API 在 8.x 到 10.x 之间变动不小:老的 max_workspace_size 字段换成了 memory pool 配置,build_engine 也被序列化构建接口取代,网上抄来的旧代码在新版本上第一行就挂。
生产建议是构建流程脚本化:每个 GPU 架构代次在 CI 里各 build 一份 engine,按启动时的设备型号自动选择,避免人工手滑加载错版本。
并行调度:多卡拆分与批合并
切片级并行不复杂:把 400 个切片均分给多张卡,各自批量推理,最后按原始序号合并结果。真正提收益的是批合并——把 8 到 16 个切片组成一个 batch 一次前向,GPU 利用率能从三成拉到八成以上。
with torch.cuda.device(gpu_id):
stacked = torch.stack(batch).cuda(gpu_id)
with torch.no_grad():
output = model(stacked)
急诊优先级要单独设计:急诊、住院、门诊分三级队列,严格先取高优先级,避免低价值批量任务把紧急检查堵在后面。队列实现里有个细节容易出错——空队列轮询要挂起等待而不是忙转,否则调度线程自己就把 CPU 吃满了。
部署形态:数据不出内网
医院内网用 Docker 加 NVIDIA runtime 就够了:调度器一个服务,GPU worker 按卡数起若干副本,Redis 做任务队列,PostgreSQL 存结果。影像数据全程不经过公网,满足等保和院方安全审计的要求。整套餐下来,单患者端到端处理时间可以从 60 秒压进 15 秒,5 分钟周转时限留出了充足余量——这余量不是浪费,是给急诊洪峰准备的。
验证环节建议固定三个指标反复测:端到端 P95 耗时、GPU 峰值利用率、Dice 降幅。三者任一超标就停止扩量,先定位再上线。

评论0