AI 看皮肤病越来越准,可它盯着的可能不是那颗痣-卷积神经网络的应用
上个月陪家人去医院皮肤科,排队时刷到一条新闻:研究说 AI 看皮肤病的水平已经追上皮肤科医生了。今年 3 月发在 JAMA Dermatology 的一篇荟萃分析,汇总了多项研究:AI 识别皮肤癌的敏感性 80.9%、特异性 75.6%;皮肤科医生是 78.6% 和 75.2%;要是医生配上 AI 当助手,能到 91.9% 和 83.7%。单看这组数字,你会觉得这玩意儿马上就能上岗了。
结果 9 月 3 日又刷到一篇报道,The Conversation 上田纳西大学几位研究者写的,当头一盆冷水:这些 AI 工具对浅色皮肤越来越准,对深色皮肤却存在巨大的盲区。更让人后背发凉的是他们挖出的机制:模型学到的可能不是病灶本身,而是把病灶周围那圈皮肤的颜色当成了捷径线索。2024 年有研究者做了个实验,痣一点没动,只把背景皮肤调暗,GPT-4 就把一颗完全良性的痣判成了恶性黑色素瘤。
把两条新闻放在一起,大问题就浮出来了:AI 看皮肤病,为什么这么准,又为什么会跑偏去盯皮肤颜色?这两个现象看起来矛盾,其实它们发生在同一个动作上:看。准,是它从图里看到了有用的东西;偏,是它从图里看到了不该看的东西。所以解释这一对矛盾,只有一条路:把它的"看法"彻底拆开。拆出来的四个问题,是一条推理链:第一,先搞懂它到底怎么"看"图;第二,懂了机制就会发现它的眼睛一次只吃得下几个像素,那"偏"的信号,背景皮肤的颜色,是什么时候混进判断的;第三,可它明明有 80% 的准确率,光抄颜色撑不起这个数,形状信号肯定也学进去了,而看清形状需要大视野和深网络,这份"准"的前提是谁、怎么造出来的;第四,机制到这儿全齐了,模型脑子里既有形状也有颜色,到底谁说了算?让模型自己来解释。
案例数据先交代。我用的是 HAM10000,一个公开的皮肤镜图像数据集,维也纳医科大学和澳大利亚的机构花了 20 年收集,一共 10,015 张图,600×450 分辨率,覆盖 7 类常见皮肤病变,其中色素痣 6,705 张、黑色素瘤 1,113 张、良性角化病 1,099 张,剩下四类加起来约 1,100 张。超过一半的病灶有病理确诊,2018 年发表在 Scientific Data 上,文末有下载方式。请先记住它的一个属性:数据集以浅色皮肤人群为主,论文里写得明明白白。这个属性在第二问和收口处都要用到。
第一问:它怎么"看"图:一个 9 参数的小窗口扫遍全图
我从数据集里取出一张黑色素瘤的皮肤镜图,缩到 96×96 的灰度图,然后手工设计了一个 3×3 的小方格,里面填了 9 个数,让它从图片左上角开始,一行一行地滑动。每滑到一个位置,就把小方格里的 9 个数和它盖住的 9 个像素对应相乘再求和,得到一个输出值。滑完整张图,得到的就是一张新的图,叫特征图。这个过程叫卷积,是卷积网络唯一的基础动作,它所有的本领都是从这一个动作里长出来的。
第二张图里,水平边缘核滑完全图,特征图上处处亮着水平线条;垂直边缘核抓竖线;斑点核响应孤立的小点。算笔账:这个方格从头到尾只有 9 个参数,却扫过全图;不用卷积的话,输出图每个位置配一套独立权重要8万+个。同一个方格反复使用,这就是神经网络篇埋下的"参数共享",本篇把账算细:省了九千多倍。
图里第四个核是我随机填的数,滑出来一片乱纹。人填的核能抓边缘,是因为数按边缘模式填的。可皮肤病千奇百怪,人填不过来,所以,核里的数交给反向传播去学。
基础动作清楚了,第一问的答案里已经埋着第二问的引线:这个动作每次只吃进 9 个像素。在这 9 个像素里,能判断的只有颜色深浅、亮度变化、细小纹理这一类局部信号;痣的轮廓形状、病灶到正常皮肤的过渡,这些跨几百个像素的模式,根本不在单个窗口的信息里。也就是说,背景皮肤的颜色,天然是这种看法最容易读到、最先读到的信号之一。可它一次只看 9 个像素,又怎么会对"整张图的背景"形成判断?背景到底是从哪一步混进去的?
第二问:偏的通道:视野长大时,把痣旁边的皮肤也圈了进来
答案在"叠"。特征图上一个输出值能"看到"的输入区域,叫感受野。单层卷积,视野就是那 3×3。但卷积网络是一层叠一层的:第二层的方格滑在第一层的特征图上,它盖住的 9 个值,每个又各自对应输入图上 3×3 的区域,叠起来算,第二层的一个输出能看到 5×5,三层是 7×7。每叠一层,视野向外扩一圈。
我把这几种情况画在图里,红框就是输出神经元在输入图上的视野。第四种是两层 3×3 之后再接一个 2×2 的最大池化(相邻 4 个值里只留最大的,一步跨 2 个像素),视野变成 6×6,看起来比三层 7×7 还小,但池化把"跳跃步长"翻了一倍,再叠一层卷积,视野直接从 6 跳到 10。
这就是第二问的答案:背景颜色不是从哪一扇"后门"混进来的,它是随着视野长大,被圈进输出神经元的视野范围的。感受野有个反直觉的性质:它只由核多大、步长多少、池化几次、叠几层决定,跟输入图多大无关。视野从 3×3 长到几十×几十的每一层里,圈住的都不只是痣,还有痣周围一大圈皮肤。模型想不看周围皮肤,除非视野只长到痣那么大,而视野的大小是架构定的,不是模型自己挑的。
视野要长大,路上的每一步都是交换,我把账算给你看。先看尺寸账:设输入边长 W,核边长 K,步长 S,边缘补 P 圈零(填充),特征图边长就是:
我在脚本里对六组参数验证了这条公式,全对得上。224 的输入用 7×7 核、步长 2、补 3 圈,输出正好 112,这是 ResNet 的标准开局。
再看边缘账。3×3 的核不补圈滑一遍,输入图中心的像素被盖住 9 次,四个角的像素只被盖住 1 次。同一种病灶特征,长在中间被看 9 遍,长在角上只被看 1 遍,而皮肤镜照片里病灶经常不在正中心。补 1 圈零,角上的被盖次数升到 4,所以实践里几乎都用"补 1 圈"的 3×3。最后是池化的账:94×94 的特征图池化后变 47×47,下一层计算量省 4 倍,附赠视野提速,但代价是位置信息。我找到特征图上响应最强的那个 2×2 窗口,4 个值是 1.19、1.11、0.94、0.75,在窗口里随便换位置,输出都还是 1.19,最大值只认数值、不认位置。
到这儿,"偏"的通道全通了:小窗口先读颜色(第一问),视野长大把周围皮肤圈进来(这一问)。可推理到这儿有个刺眼的事实没解释:如果这套机制只会抄颜色近路,它凭什么叫"越来越准"?80% 的准确率意味着形状、边界这些大尺度信号确实也学进去了。而看清形状要求视野足够大、网络足够深。往深里叠,路好走吗?我替你试过了,不好走。
第三问 准的前提:深网络的两块拦路石,VGG 和 ResNet 各搬走一块
我照"补 1 圈的 3×3 加池化"搭了个 6 个卷积层的网络,任务就是分辨黑色素瘤和色素痣(设定:mel 全取 1,113 张,nv 抽样 1,000 张,共 2,113 张,缩到 32×32 灰度,训练 1,479 张、验证 316 张、测试 318 张)。普通堆叠的版本,验证准确率从第一轮的 0.528 一路跌到第六轮的 0.500,等于闭着眼睛猜。注意这不是过拟合:过拟合是训练集好、测试集差,这里是连训练集都学不动。
原因出在反向传播的链式法则上,梯度要一层层乘回去,网络一深,连乘下来要么消失要么爆炸,浅层的核收不到有效学习信号,视野就停在半路。第一块拦路石是"深得起,但太贵",VGG 搬走了它:用小核堆出大视野。两个 3×3 的核前后叠,感受野是 5×5,跟直接用一个 5×5 的核一样。但参数账不同:同样 8 个输入通道、8 个输出通道,一个 5×5 核要 1,600 个参数,两个 3×3 核只要 1,152 个,还白赚一次非线性激活。视野相同、参数更省、表达更强,VGG 的全部智慧就这一句。我在 HAM10000 上做了对照,三种结构用完全相同的配置跑 6 轮:浅层网络(每级一个 3×3)测试准确率 0.538;VGG 式堆叠(每级两个 3×3)0.679;每级一个 5×5 的省事版只有 0.528。视野相同的两种搭法,堆小核的高出 15 个百分点。
第二块石头是梯度断路,ResNet 的解法朴素到不像话:残差块里,两层卷积的输出再加上输入本身,给梯度开一条直通车道,顺着加法通路直接流回浅层,不被连乘磨损。同一个 6 层任务,换成残差块,验证准确率爬到 0.649,测试集 0.723 对普通版的 0.516。152 层的 ResNet 就是靠这个训起来的。
第三问答完,"准"的来源清楚了:小核堆视野、残差保训练,网络终于又深又稳,形状信号进得来了。现在按推理,模型脑子里同时住着两种信号:有用的形状,和抄近路的背景颜色。到底谁说了算?空对空地争没意思,第四问让模型自己招供。
第四问:让模型自己解释:把"看哪里"画出来,把背景调暗
第一步,把它的注意力画出来:把最后一层卷积的特征图按全连接层的权重加权叠加,得到类激活图,叠在原图上,红色代表模型看得重的地方。结果和第二问的推理严丝合缝:红色高响应集中在图片的外圈和病灶周围的皮肤上,病灶本身反而是一片冷色。
第二步,动背景,看判断跟不跟。我把测试集 318 张图的外圈 4 个像素环整体调暗到 50%,中央区域一个像素不动,痣还是原来那颗痣。就这么一个与病灶毫无关系的改动,模型的预测有 59.7% 发生了翻转,准确率从 0.679 掉到 0.522,接近瞎猜。
回到标题
现在可以回答大问题了,四个问题的答案串起来就是完整的因果链:卷积窗口小,网络浅层只能先读到颜色和亮度(第一问);数据集以浅色皮肤为主,mel 和 nv 的成像亮度与背景皮肤色调存在系统性关联,抄"背景颜色"这条捷径比学病灶形状省力得多;视野长大时,周围皮肤被天然圈进判断(第二问),捷径就藏在那里;网络往深处本来有机会转向形状信号,VGG 和 ResNet 把这条路修通之后,两种信号在模型里并存(第三问);激活图和调暗实验证明,抄近路的信号在很多判断里占了上风(第四问)。所以开头那对矛盾不矛盾了:AI 准,是因为这套机制真能把视野层层堆大、把特征学出来;AI 偏,是因为同一套机制在数据有捷径时会先抄近路。准和偏,是同一个机制的正面和反面。
这里我要诚实交代量级:我的模型只有几层卷积、3 万多参数、32×32 灰度小图,0.679 是演示量级,离可用的筛查工具很远。但捷径学习与感受野的关系是结构性的,不随模型规模消失,小模型反而把它演得更赤裸。那能怎么办?业界的答案也正在这里:光看准确率不够,得把"看哪里"纳入评估,让类激活图成为体检项目;数据层面则要覆盖不同肤色,把捷径堵死。评价一个视觉模型,"它看哪里"比"它多准"更要紧。
三个值得记住的点
- 感受野只由核大小、步长、池化和层数决定,与输入图多大无关。浅层视野只有几个像素,所以网络总是先抓颜色和纹理,深层才看得见形状。判断一个卷积网络"能不能看见你关心的问题",先算它的感受野。
- 两个 3×3 堆叠的感受野等于一个 5×5,参数从 1,600 降到 1,152,还多一次非线性。同样的视野,堆小核永远更划算,VGG 的全部道理就这一句。
- 网络看哪里是可以画出来的。类激活图高亮在外圈、背景一调暗准确率就从 0.679 掉到 0.522,捷径学习当场现形。对视觉模型,"看哪里"比"多准"更要紧。
代码复现
数据源:
- 数据集:HAM10000 皮肤镜图像集,10,015 张、7 类病变
- Kaggle 下载页:
https://www.kaggle.com/datasets/kmader/skin-cancer-mnist-ham10000 - 官方论文(数据集描述,含浅色皮肤人群为主的记载):Tschandl et al., The HAM10000 dataset…, Scientific Data 5, 180161 (2018),DOI:
10.1038/s41597-018-0180-z - 原始归档(ISIC):
https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/DBW86T
代码:
- 仓库主页:
https://github.com/beverlyLee/ai-guanxingji/tree/main/M3 - 复现脚本(7 组实验):
M3/code/model_cnn.py - 下载与数据准备脚本:
M3/data/parallel_download.py(分段下载)、M3/data/extract_from_parts.py(免拼接抽取)
复现三步:
python data/parallel_download.py:从 Kaggle 免 token 直链下载 HAM10000(5.58 GB),16 线程分段下载并校验每个分段的 HTTP 206 与 Content-Range(单线程只有约 260 KB/s,这个数据集直链的部分 CDN 节点还会忽略 Range 请求,脚本里都处理了);python data/extract_from_parts.py:不落盘完整 zip,直接流式读取分段,抽取 mel 全部 1,113 张加 nv 抽样 1,000 张,预处理成 96×96 灰度 npz(约 20 MB),另存 7 类各 3 张展示图;python code/model_cnn.py:先跑反向传播的数值梯度校验(相对误差 1.6e-10),再依次复现本篇全部 8 张图和 stats.json 的每个数字。
最后想问你
回到你自己身上:下次再看到"AI 诊断准确率超过医生"的新闻,你会先问哪句?是"在哪些人身上测的",还是"它到底看的是哪里",或者别的什么?评论区聊聊,我也很想知道有没有人被这类工具的体检报告坑过。觉得有用的话,记得收藏并点赞哦。