🧠 第九章 模型评估和优化
📄 一句话概括

亮度偏移扫 9 档、每档 12 个种子:两端 12/12 全崩(平均亮度 0.033 和 0.967), 中间 -0.6 到 +0.6 全部通过;对比度从中间的 0.253 掉到两头的 0.074。

📝 本节和前面几节的关系

上一节把多个模型合成一个。这一节是交付前最后一步——拿它真跑一批,看输出能不能用。 教材用的案例是生成 AI 画作。

一、离线指标管不了「输出能不能用」

训练时盯的是损失和验证集准确率。这些数字只回答一个问题:模型对这批数据拟合得怎么样。 它不回答「出来的东西对不对」。 生成类任务上这个差距特别大。损失降了,图可能结构崩坏、手指多一根; 文本生成里损失降了,可能开始重复同一句话。 所以必须真跑一批、看产出。这一步没法用单个数字概括,只能先定崩坏判据,再批量统计。 顺序上也不能反:先跑了才知道该怎么定判据。凭想象定的阈值,十有八九会误判掉一批好图。

二、AI 画作这个案例为什么典型

输出是图,没有唯一正确答案,不能算「对了几个」。这是生成任务和分类任务最根本的区别。 但崩坏的类型其实很有限:过曝(一片白)、死黑(一片黑)、一片灰(没有对比度)、 结构散掉(该画人画成椅子)。 前三种都能用统计量抓:平均亮度抓过曝和死黑,标准差(对比度)抓一片灰,边缘密度辅助看结构。 第四种抓不到。统计量只能说明「图上有没有明暗变化」,说明不了「明暗变化是不是一个人」。 所以流程必须分两层:统计量粗筛 + 人工复看。

三、实测:扫参数,找可用区间

Demo 扫了 9 档亮度偏移,每档 12 个种子,一共 108 张。 判据是平均亮度跑出 0.05 到 0.95,或者对比度低于 0.02。 -0.7 那档平均亮度只有 0.033,整张图死了黑,细节全被压到 0; +0.7 那档是 0.967,过曝,全被截到 1。两档都是 12/12 全崩。 中间的 -0.6 到 +0.6 七档,每档都是 0/12——一张都没坏。可用区间就是 -0.6 到 +0.6。 对比度那一列呈明显的倒 U 形:0 附近最高 0.253,两头掉到 0.074。 因为截断把明暗差直接砍掉了,这比「亮度太高」更能说明图废了。 所以判据要两条一起看。只看亮度会漏掉「灰蒙蒙一片」那种崩坏——它的亮度完全正常,就是没内容。

四、这套流程能替代什么,不能替代什么

能替代的是「人工快速过一遍」这件事。 人工看 108 张要半小时以上,算统计量不到一秒,而且不会疲劳、不会漏。 替代不了的是判断内容对不对。该画人画成椅子、该有六个手指画成七个,统计量一点反应都没有。 也不能把它当质量分用。统计量正常的图可能毫无美感,也可能完全偏离提示词。 它筛的是「技术上坏掉」,不是「不好看」。 最重要的产出其实不是那些数字,是可用参数区间。有了区间,上线时才敢把这个参数开放给别人调; 没有区间,一调就可能批量出废图。

五、动手 Demo:批量测试:扫 9 档亮度偏移,两端 12/12 全崩

用「两个正弦叠乘加噪声」造 64 × 64 的程序化纹理当生成结果,亮度偏移从 -0.7 扫到 +0.7 共 9 档, 每档跑 12 个种子;对每张算平均亮度、对比度(标准差)、边缘密度,按判据统计崩坏率。

for sh in shifts:
    for sd in range(12):
        im = gen(sd, sh)                       # 生成一张
        mb = float(im.mean())
        ct = float(im.std())                   # 对比度
        bad = mb > 0.95 or mb < 0.05 or ct < 0.02

实跑输出:

--------------------------------------------------------------------
批量测试:按统计指标筛掉崩坏的生成结果
--------------------------------------------------------------------
  生成方式                 64 × 64 程序化纹理(两个正弦叠乘加噪声),不是真模型出图
  扫描参数                 亮度偏移从 -0.7 扫到 +0.7,每个档位跑 12 个种子
  崩坏判据                 平均亮度高于 0.95、低于 0.05,或对比度低于 0.02

  亮度偏移   平均亮度   平均对比度   边缘密度   崩坏张数   崩坏率
  --------------------------------------------------------------
      -0.7      0.033        0.074      0.018    12 / 12     100%
      -0.6      0.062        0.110      0.028     0 / 12       0%
      -0.5      0.103        0.147      0.040     0 / 12       0%
      -0.3      0.233        0.208      0.063     0 / 12       0%
      +0.0      0.500        0.253      0.080     0 / 12       0%
      +0.3      0.767        0.208      0.063     0 / 12       0%
      +0.5      0.897        0.147      0.040     0 / 12       0%
      +0.6      0.938        0.110      0.028     0 / 12       0%
      +0.7      0.967        0.074      0.018    12 / 12     100%

  有崩坏的档位             -0.7、+0.7
  全部通过的档位           -0.6、-0.5、-0.3、+0.0、+0.3、+0.5、+0.6
  可用区间                 -0.6 到 +0.6

  两头的崩坏原因不一样。**-0.7 那一档平均亮度只有 0.033**,整张图死了黑,细节全被压到 0;**+0.7 那一档是 0.967**,过曝,全被截到 1。
  对比度那一列呈明显的倒 U 形:0 附近最高(0.253),两头掉到 0.074。因为截断把明暗差直接砍掉了——这比「亮度太高」更能说明图废了。
  所以真正的判据是两条一起看:亮度跑出 0.05 到 0.95,或者对比度掉到 0.02 以下。只看亮度会漏掉「灰蒙蒙一片」这种崩坏。

  这套做法的价值不在指标本身,在**把「好不好看」拆成了可批量检查的数**。人工看 120 张图要半小时以上,算统计量不到一秒,而且不会疲劳、不会漏。
  但它的边界要讲清:这三个量只能筛掉「技术上坏掉」的图(过曝、死黑、一片灰),筛不掉「技术上正常但内容不对」的图——比如该画人却画了个椅子。
  所以真实流程是两层:先用统计指标粗筛掉明显崩坏的,再人工看剩下的。这样人工量能砍掉一大截。
  这里我用的是程序化纹理,不是真模型出图——沙箱里没有图像生成模型。所以表里具体的亮度数值没有参考意义,有意义的是**这套筛查流程**:定崩坏判据、扫参数、统计崩坏率、找可用区间。

崩坏率只有 0% 和 100% 两种值,中间没有过渡。 因为在两端都是硬截断——所有超过 1 的值全变 1,整张图的信息一次丢光,不存在「崩一半」。 对比度比亮度更早报警:-0.6 那档亮度 0.062 已经贴到 0.05 的边,对比度 0.110 还离 0.02 很远。 说明亮度是这个场景下的主导判据。 反过来在「内容崩坏」上,两个判据都不灵。这也是为什么必须留人工这一层。 这里我用的是程序化纹理,不是真模型出图——沙箱里没有图像生成模型。 所以表里具体的亮度数值没有参考意义,有意义的是这套筛查流程:定崩坏判据、扫参数、统计崩坏率、 找可用区间。

30 秒实验

  1. 把对比度阈值从 0.02 抬到 0.10,看中间几档会不会被误判成崩坏。
  2. 把每档的种子数从 12 降到 3,看崩坏率还稳不稳。
  3. 想一想:为什么「亮度正常但对比度极低」也该算崩坏?

常见误区

  1. 「损失降了就说明模型能用」——损失只反映拟合,不反映产出质量。
  2. 「统计量正常就是好图」——只说明技术上没坏,不说明内容对。
  3. 「试三张没问题就能上线」——样本太少,崩坏率估不准,边界参数尤其危险。
自测三问(点开对答案) 1. 为什么生成任务必须真跑一批,不能只看离线指标? —— 离线指标只说明拟合程度,不说明产出对不对,而生成任务没有唯一正确答案。 2. 平均亮度和对比度分别抓哪种崩坏? —— 亮度抓过曝和死黑,对比度抓「一片灰」这种没有明暗差的情况。 3. 这套批量筛查替代不了什么? —— 判断内容对不对——结构崩坏、偏离提示词这类问题统计量看不出来。

第九章讲完评估和优化。下一章换个角度:模型训好了,怎么管、怎么部署上线。

相关笔记