最近在做表征评估审计,把各个团队报上来的 kNN 准确率挨个复算。复算完发现一个规律:不报错、不崩溃、指标看起来很漂亮的结果里,有一半是错的。
而且这些错都不是低级错误,全是 "看起来很专业、实际上埋了雷" 的那种。我挑四个最常见的写出来,你对着自己的结果看看中了几条。
坑一:切分泄漏 —— 准确率凭空虚高 16 个百分点
这是最常见、也最隐蔽的一种。
我们拿到一份 4817 行的数据集,按行随机切分做 kNN,准确率 75.73%。听起来不错对吧?
但把数据拆细了一看:4817 行里,真正的独立对象只有 621 个。剩下的 4174 行都是数据增强出来的 —— 旋转、翻转、加噪、提亮。每一个真实对象,平均有 7.76 个增强副本。
问题来了:按行随机切分的时候,同一个对象的增强副本,一半在训练集、一半在测试集里。 模型根本不是在认 "新对象",是在认 "我见过这个东西的旋转版本"。
换一种切分方式 —— 按对象切,同一个对象的所有副本全进训练集或全进测试集 —— 准确率直接掉到 59.12%。
差了 16.61 个百分点。 同一个数据集、同一个距离度量、同一个 kNN 算法,只换了切分方式,结果差出这么多。
对外报数的时候,如果你不说清楚是按行切还是按对象切,这个数就是没法复现的。
坑二:空壳数据 —— 号称几千条,有效不到一成
第二个坑更离谱。
团队说 "我们有 5000 条标注好的训练数据",听起来很扎实对吧?
我们打开文件一看:4817 行,逐字节去重之后,只有 643 个唯一数组。剩下的 4000 多行全是重复的。
为什么会这样?因为他们用的是图像转灰度、resize 到 64×64、然后拉平成向量的流程。分辨率一低,本来不同的图压完之后就撞成同一个数组了。659 张源图,最后只剩 643 个唯一表示,碰撞了 16 张图,直接损失 2.43 个百分点的准确率。
号称 5000 条数据,真正独立的样本只有 600 多条。这个 "数据量" 是注水的。
坑三:退化向量 —— 指标失效了还打出 "某类全对"
第三个坑最阴险。
kNN 跑出来,报告说 "甲骨类准确率 100%,全对"。听起来很好对吧?
我们把那一类的向量拉出来一看:全是零向量。
零向量和任何向量的余弦相似度都是 0,在 k 近邻里会被排到最后面。但因为那一类别没有别的样本可比,它自己跟自己比,"最近邻" 就是它自己,自然 "全对"。
这不是模型学得好,是那一整个类的数据都是坏的。指标没报错、没崩溃,安安静静给了你一个 100% 的漂亮数。等你把这个数写进论文、投了标,被人查出来就晚了。
坑四:口径漂移 —— 同一个最优值,三轮出三个数
第四个坑是最容易被忽略的。
同一个实验,第一轮报告准确率 72%,第二轮 76%,第三轮 79%。问 "为什么涨了?",答 "调了一下参数"。
但你问 "具体调了什么?切分方式变了吗?归一化方式变了吗?k 值怎么选的?"—— 答不上来。
每一轮都在悄悄换口径,但没人记录。最后对外报的那个数,不知道是哪一轮的、用什么方法测出来的。别人想复现,复现不出来。
为什么这些坑没人抓?
因为这些问题全都是 "沉默错误"—— 不报错、不崩溃、指标看起来正常。
你写脚本跑 kNN,出来一个 75% 的准确率,你觉得没问题。但这个 75% 是按行切的还是按对象切的?数据里有没有零向量?有没有重复样本?归一化做了没有?
这些东西,不专门去查,永远不会自己暴露。
最后
做表征评估做了这么久,最大的感受就是:一个准确率数字本身没有意义,有意义的是这个数字背后的那四个条件 —— 切分方式、数据去重、归一化口径、是否泄漏。
这四个条件写不清楚,这个数就是飘的。
如果你手里也有要对外报的准确率数字,建议对着上面四个坑自查一遍。查完发现没问题,那这个数才是真的站得住。