🧠 第十一章 医疗与法律:重塑精准诊疗与服务流程
📄 一句话概括

一个灵敏度 50.2%、特异度 93.3% 的检查,单看是个不错的检查。 但放到患病率 1% 的人群里,阳性预测值只有 7.0%。二十万人里, 13246 个健康人被误判成阳性,989 个病人被漏掉。 同一个阈值,患病率换成 50%,阳性预测值就是 88.2%;换成 0.1%,只剩 0.8%。

📝 本节和前面几节的关系

11.1 讲的是「模型怎么练出来、放在哪」。 这一节开始讲「它进了诊室之后,那几个指标该怎么读」。 这是医疗 AI 落地时最容易出事的一步,而且出事的方式很安静。

一、两类指标,说的不是一件事

评价一个检查有两组常用指标,很多人会混着用,而这正是问题的起点。 灵敏度=病人里查出阳性的比例。特异度=健康人里判为阴性的比例。 这两个都是「模型自己的属性」——换人群,它们基本不变。 阳性预测值=查出阳性的人里真正有病的比例。 这个不是模型属性,是场景属性——同一个模型,换个人群,它是另一个数。 分清这一点非常关键:厂商报的永远是前两个(好看、稳定),而医生真正想知道的是第三个。 这三者之间差一个患病率,而患病率是使用方决定的。 它们的关系用贝叶斯公式就能写出来: 阳性预测值 = 灵敏度 × 患病率 ÷(灵敏度 × 患病率 + (1 − 特异度) × (1 − 患病率))。 分母的第二项是「健康人被误判的比例」,而健康人的基数通常大得多。

二、患病率把同一个模型变成两个模型

Demo 造 20 万人、患病率 1%,患者分数 N(1.5,1)、非患者 N(0,1)。阈值取 1.5,得到第一张表:

阈值 灵敏度 特异度 阳性预测值 误诊数 漏诊数
0.5 85.5% 69.0% 2.7% 61317 288
1.0 71.5% 84.1% 4.3% 31559 566
1.5 50.2% 93.3% 7.0% 13246 989
2.0 31.5% 97.7% 12.1% 4523 1362
2.5 16.5% 99.4% 20.7% 1252 1660
先看阈值 1.5 那一行:灵敏度 50.2%、特异度 93.3%。
单看这两个数,这是一个相当不错的检查。
但阳性预测值只有 7.0%——查出来阳性的人里,九成以上是虚惊一场。
具体到人数:误诊 13246 人,漏诊 989 人。
为什么?因为患病率只有 1%。二十万人里 2000 个病人、198000 个健康人。
特异度 93.3% 意味着健康人里有 13246 个被误判——比真病人多好几倍。
误诊的数量由「健康人的基数」决定,不是由「模型准不准」决定。
把阈值往下调会怎样?
灵敏度上去了,误诊暴涨:阈值 0.5 时灵敏度 85.5%,但误诊 61317 人,
阳性预测值掉到 2.7%——等于「查出来阳性几乎都是没事」。
往上调呢?阈值 2.5 时阳性预测值 20.7%,但漏诊 1660 人——2000 个病人里放走 83%。
这就是阈值选择在低患病率人群里的残酷性:你只能在「大量虚惊」和「大量漏诊」之间选一个。

三、换成高患病率人群,同一个阈值就变好了

Demo 第二张表固定阈值 1.5,只改患病率:

患病率 阳性人数 其中真病 阳性预测值
50.0% 112998 99685 88.2%
10.0% 44343 20196 45.5%
1.0% 28275 1981 7.0%
0.1% 26984 213 0.8%
(这一张换了 40 万人的样本来算,所以 1% 那行的阳性人数和表一不同。)
同一个阈值、同一个模型、同一个灵敏度和特异度——患病率从 50% 掉到 0.1%,
阳性预测值从 88.2% 掉到 0.8%。
检查本身一点没变,变的只是「在谁身上用」。
中间那两行更值得看:50% 到 10%,阳性预测值从 88.2% 掉到 45.5%,直接砍半;
10% 到 1%,再从 45.5% 掉到 7.0%,砍到不足六分之一。
下降不是线性的——患病率每降一个数量级,阳性预测值就塌一层。
最后一行的 0.1% 最能说明问题:26984 个阳性里只有 213 个真病,阳性预测值 0.8%。
换句话说,这个检查在 0.1% 的人群里基本等于抛硬币——而且是一枚 99.2% 概率抛错的硬币。
这就是为什么「AI 诊断准确率 95%」这句话必须带上「在什么人群里」。
同样是 95%,用在三甲医院专科门诊和用在社区筛查,临床意义差几十倍。
反过来说,这也是分级诊疗的统计学理由:
先在社区做问卷筛出高风险人群(把验前概率抬上去),再让他们去大医院做贵的检查。
这不是流程繁琐,这是在把患病率调到模型能工作的区间。
这个道理不只适用于医疗。风控里的欺诈检测、工业里的缺陷检测、
安全里的入侵检测——凡是「正例极其稀有」的场景,都会撞上同一堵墙。
「模型准确率 99%」在 0.1% 的阳性率下,阳性预测值可能只有个位数。
所以看这类报告,有一个固定的追问顺序:
① 你的测试集里正例占多少?② 落地场景里正例占多少?③ 这两者差多少?
第三个问题答不上来,前两个数字就没有意义。

四、落地:报告和流程各改一处

报告侧:阳性预测值必须报,而且要报「在什么患病率下」。只报灵敏度和特异度的报告, 在低患病率场景里等于把最重要的信息藏起来了。 流程侧:把「谁来做这个检查」当成参数。同一个模型,给专科门诊用和给体检中心用, 阈值应该不一样——因为两边的验前概率不一样。 所以部署时不能只给一个模型,要给一套「按人群分档的阈值」。 再加一道人机分工: 模型负责「不放过」,医生负责「别冤枉」。具体做法是把阈值调低(灵敏度优先), 接受大量假阳性,然后用医生的时间去做二次判读。 筛查场景本来就该这样设计——因为漏掉一个病人的代价,远高于多叫一个人回来复查。 最后一句:在医疗 AI 里,「准确率」不是终点,「阳性预测值 × 患病率」才是。 前者是模型的自评,后者是它在诊室里的实际表现。

五、动手 Demo:诊断阈值:特异度 93% 的检查,在 1% 患病率下阳性只有 7% 是真病

造 20 万人,按 1% 患病率标标签;患者的检验分数取 N(1.5,1)、非患者取 N(0,1),分数高于阈值判为阳性。 先扫五个阈值,算灵敏度、特异度、阳性预测值和误诊/漏诊人数; 再固定阈值 1.5,把患病率换成 50%、10%、1%、0.1%,看阳性预测值怎么变。

y = rg.random(n) < prev                        # 1% 患病率
score = rg.normal(np.where(y, 1.5, 0.0), 1.0)  # 患者 N(1.5,1)
pos = score > th                               # 阈值判阳
tp = int((pos & y).sum())
fp = int((pos & ~y).sum())
sens = 100.0 * tp / max(tp + fn, 1)            # 灵敏度
spec = 100.0 * tn / max(tn + fp, 1)            # 特异度
ppv = 100.0 * tp / max(tp + fp, 1)             # 阳性预测值

实跑输出:

--------------------------------------------------------------------
诊断阈值:特异度 93% 的检查,在 1% 患病率下阳性只有 7% 是真病
--------------------------------------------------------------------
  人数 × 患病率            200000 人 × 1%
  分数分布                 患者 N(1.5,1),非患者 N(0,1)
  阈值                     分数高于它判为阳性

  阈值   灵敏度   特异度   阳性预测值   误诊数   漏诊数
  ----------------------------------------------------
   0.5    85.5%    69.0%         2.7%    61317      288
   1.0    71.5%    84.1%         4.3%    31559      566
   1.5    50.2%    93.3%         7.0%    13246      989
   2.0    31.5%    97.7%        12.1%     4523     1362
   2.5    16.5%    99.4%        20.7%     1252     1660

  先看阈值 1.5 那一行:**灵敏度 50.2%、特异度 93.3%。**
  **单看这两个数,这是一个相当不错的检查。**
  **但阳性预测值只有 7.0%**——**查出来阳性的人里,九成以上是虚惊一场。**
  具体到人数:**误诊 13246 人,漏诊 989 人。**
  **为什么?因为患病率只有 1%。**
  二十万人里 2000 个病人、198000 个健康人。
  **特异度 93.3% 意味着健康人里有 13246 个被误判——比真病人多好几倍。**

  表二把这件事说得更清楚:固定阈值 1.5,只改患病率。

  患病率   阳性人数   其中真病   阳性预测值
  ----------------------------------------
   50.0%     112998      99685        88.2%
   10.0%      44343      20196        45.5%
    1.0%      28275       1981         7.0%
    0.1%      26984        213         0.8%

  **同一个阈值、同一个模型,患病率从 50% 掉到 0.1%,阳性预测值从 88.2% 掉到 0.8%。**
  **检查本身一点没变,变的只是「在谁身上用」。**
  这条在医疗 AI 里极其重要:**模型报的灵敏度/特异度是在某个数据集上测的。**
  **换到另一个患病率的人群,阳性预测值会完全变样。**
  **所以「AI 诊断准确率 95%」这句话,必须带上「在什么人群里」。**
  实务上的对策也很直接:**先筛高风险人群(提高验前概率),再做检查。**
  这就是分级诊疗和初筛问卷存在的统计学理由。
  要说明:分数分布是我**设的**(两组正态、有重叠),**真实检验指标的分布往往更偏态**。
  **但「患病率决定阳性预测值」这件事是纯概率,不依赖数据。**

这张表的读法是从右往左:先看误诊和漏诊的人数,再看阳性预测值,最后才看灵敏度和特异度。 顺序反了,就会被那两个「看起来漂亮」的百分比骗过去。 阈值 1.5 那一行最值得停一下:灵敏度 50.2% 意味着一半的病人查不出来。 而特异度 93.3% 听起来很好,落到 19.8 万健康人身上就是 13246 个误诊。 「比例」和「人数」在基数悬殊时完全是两个故事。 第二张表是这一节的核心证据:阳转率从 88.2% 掉到 0.8%,检查没变。 这就是「阳性预测值是场景属性」的量化证明。 在 0.1% 的患病率下,这个检查基本等于抛硬币——查出来阳性的人里 99.2% 是健康的。 顺带说一句:患病率越低,阳性预测值越接近「特异度的补数之比」,跟灵敏度几乎无关。 所以在筛查场景里,提升特异度比提升灵敏度更值钱。这跟直觉相反——大家都盯着灵敏度做, 但在低患病率下,1 个点的特异度提升,减少的误诊人数远超 1 个点的灵敏度提升。 要说明:分数分布是我设的(两组正态、有重叠), 真实检验指标的分布往往更偏态,还常有「灰区」需要复检。 但「患病率决定阳性预测值」这件事是纯概率,不依赖数据。

30 秒实验

  1. 把患病率从 1% 改成 5%(比如有家族史的人群),重跑第一张表。 阳性预测值会涨到三成左右——患病率抬五倍,阳性预测值抬四倍多。
  2. 把特异度从 93.3% 人为提到 99%(比如加一道复检),看阈值 1.5 那一行。 阳性预测值会跳一大截——低患病率下,特异度就是命门。
  3. 把两组分布的均值差从 1.5 缩到 0.8(检查变差),看整张表。 所有阈值下阳性预测值都会掉,因为灵敏度和特异度同时变差。

常见误区

  1. 「准确率 95% 的模型可以直接用」。在 1% 患病率下,阳性预测值可能只有 7%,九成以上是虚惊。
  2. 「灵敏度特异度高就是好检查」。这两个数与人群无关,真正决定临床价值的是它们和患病率的组合。
  3. 「阈值调低就能多查出病人」。 灵敏度确实上去了,但阈值 0.5 时误诊 61317 人,阳性预测值掉到 2.7%。
自测三问(点开对答案) 1. 为什么灵敏度、特异度换人群不变,阳性预测值却会变? —— 前两个是条件概率,只依赖模型本身;阳性预测值还取决于人群里正例的占比,即患病率。 2. 1% 患病率下,为什么特异度比灵敏度更值钱? —— 因为健康人基数巨大(19.8 万),特异度每提高 1 个点, 减少的误诊人数远多于灵敏度提高 1 个点带来的真阳性。 3. 「AI 诊断准确率 95%」这句话缺了什么? —— 缺「在什么人群里」。同样 95%,专科门诊和社区筛查的阳性预测值差几十倍。

上一节解决「查出来阳性怎么办」。 再往上游一步: 还没生病的人,怎么判断谁该干预?这就是健康管理要做的事,而它的评价口径和诊疗完全相反。

相关笔记