前段时间干过一件让我血压升高的事:某天随手写了句 prompt,批量出了几张图,其中一张构图、光影都挺对味,我顺手就用了。过两天想在它基础上再改改,把 prompt 一字不差贴回去,连模型都没换,点生成------出来的图跟原来那张八竿子打不着。反复点了十几次,一张都对不上。
如果你也写代码,这种感觉应该很熟悉:一段逻辑昨天跑得好好的,今天同样的输入却给了不一样的结果,而你发誓自己什么都没动。放在普通程序里这是要通宵查的 bug;但在文生图里,"同样的输入给不同的输出"恰恰是它默认的工作方式。搞清楚这件事,再谈"怎么把图固定下来",就顺理成章了。
为什么每次都不一样:从一团噪声开始
先说结论:主流文生图基本都不是"照着 prompt 一笔一笔画",而是从一团随机噪声出发,一步步把它"去噪"成一张图。
打个不那么严谨但够用的比方。想象你面前是一台没信号的老电视,满屏雪花点。现在给你一句话"一只坐在窗台上的橘猫",让你盯着这堆雪花,一遍遍地擦,每擦一轮就让画面朝"橘猫"这个方向清晰一点点。擦上几十轮,雪花里就慢慢"浮现"出一只猫。
关键在于:每次开始时那屏雪花点长得都不一样。同样一句"橘猫",从这团雪花里擦出来的猫,和从那团雪花里擦出来的猫,姿势、花色、窗户样式、光线全都会不同。prompt 决定的是"往哪个方向擦",而那团初始雪花决定了"从哪儿起步"。起点不同,终点自然分叉。
所以本质上,你写的 prompt 从来不是唯一的输入。"prompt + 一团随机噪声"才是完整的输入,只不过那团噪声通常在背后悄悄生成,你看不见而已。它每次都换一份,图当然每次都变。
seed:把那团噪声钉死
那团随机噪声是怎么来的?靠一个随机数生成器。而随机数生成器有个人人都用过的性质:给它同一个种子(seed),它就吐出同一串"随机"数。
python
import random
random.seed(42)
print(random.random()) # 0.6394267984578837
random.seed(42)
print(random.random()) # 又是 0.6394267984578837
文生图里的 seed 就是这个道理。固定 seed,等于固定了那份初始噪声。起点一样,擦图的方向(prompt)一样,擦的方式(其他参数)也一样,你就能把同一张图原样复现出来。
这就是可复现的第一把、也是最重要的一把钥匙。很多工具里 seed 默认显示成 -1 或者干脆藏起来,意思是"每次随机给你换一份"。想复现,第一步就是把它从随机改成一个固定数字。顺带一提,批量出图常见的"一次出四张各不相同",内部往往就是 seed、seed+1、seed+2、seed+3 这么排下去------所以你想微调其中某一张,先把它对应的那个具体 seed 抠出来,别再用批量的随机模式。
我平时用图叮这类在线工具随手试想法时,界面上经常压根不给你露 seed,一点就是一张新的------这类消费级工具默认帮你把随机性收敛掉了,图省事;但也意味着你想要"跟上次一模一样"这件事,反而变难了。
光有 seed 还不够:一致性是被一串参数共同锁死的
有人固定了 seed,发现图还是变了,于是怀疑 seed 没用。多半是别的变量偷偷动了。seed 只锁住了"起点",擦图的整个过程还有一串旋钮,任何一个变了,终点都可能偏。
- 采样步数(steps):擦几轮。步数变了,去噪的轨迹就变了,图跟着变。而且不是越多越好,到一定程度后收益递减,再加只是更慢。
- 引导强度(CFG 这一类):控制"多大力气贴着 prompt 走"。调高,更服从你的描述,但容易出现颜色过饱和、细节发糊、画面发死;调低,模型更自由发挥,画面松弛,但也更容易跑题。这里我不给具体数值,因为好坏跟你的模型、prompt、题材都相关,规律记住"高=听话但紧绷,低=自由但发散"就够。
- 采样器(sampler) :相当于用哪套擦法。不同采样器收敛路径不一样,同样 seed 同样步数换个采样器,图也会不同。有个坑值得单独拎出来 :有些采样器本身在每一步还会额外掺入随机(名字里常带
a或 "ancestral"),这种情况下就算你 seed 固定死了,出图也未必稳。想要严格可复现,优先选那些不额外加噪的确定性采样器。 - 分辨率 / 尺寸:改宽高不是简单地把同一张图拉大拉小,而是从一团不同形状的噪声重新擦,构图可能整个换掉。
- prompt 本身:加一个词、删一个词、调一下词序、改一下某个词的权重,方向就偏了。它是输入的一部分,不是注释。
- 模型 / 版本:同样的 seed 和参数,换一个模型、换个模型版本,甚至换一个配套组件,出来就是另一张。
还有一条特别戳工程师的:跨机器的不可复现。哪怕你把上面所有参数一字不差搬到另一台机器,换了显卡、换了推理库版本、换了计算精度,结果也可能有肉眼可见或不可见的细微偏差。这跟浮点运算在不同硬件上结果不完全一致是一个道理。所以严格的可复现,除了记参数,还得把运行环境一起钉住。真要较真时,把模型版本、库版本、精度这些也记进你的"配方"里。
换个角度看,一次出图的完整配方大概长这样:
yaml
prompt: "a orange cat sitting on a windowsill, soft light"
seed: 12345
steps: 30
cfg: 7
sampler: "确定性采样器"
size: [768, 1024]
model: "某模型 vX.Y"
# 想跨机器复现,再补上:精度、推理库版本......
把这一整份存下来,而不是只存一张图 ,你才真正拥有了"随时重来"的能力。这和我们在代码里锁 lockfile、记 commit hash 是一个思路:图是构建产物,这份配方才是源码。
像查 bug 一样调参:控制变量法
搞清楚了"一致性由一串变量共同决定",接下来那个最实用的技巧就自然浮现了------控制变量。
想弄明白某个参数到底起什么作用,别一次改一堆。固定 seed,然后一次只动一个参数 ,把结果排一排对比。这跟你 git bisect 定位是哪个 commit 引入的 bug,或者注释掉一半代码二分排查,是同一套方法论:只有把其它变量摁住,你观察到的差异才能归因到你动的那一个上。
具体怎么做:
- 先锁 seed。这一步不做,后面全是噪声,啥也比不出来。
- 选一个想研究的参数,比如 CFG。其它全部不动,只让 CFG 从低到高排几档,一次出一组。
- 把这组图并排看,你立刻能看出这个参数在你这个题材上的脾气------哪档太紧、哪档跑题、哪档刚好。
- 定下这一档,再去调下一个参数,重复。
不少工具直接内置了"参数网格"这种功能:横轴放一个参数、纵轴放另一个,自动出一整张对照矩阵。本质就是帮你把控制变量法批量化了。用熟之后,你对每个旋钮的手感会比看十篇教程都扎实------因为那是你自己在固定变量下亲眼比出来的,不是听来的。
顺便说,前面那张"怎么都复现不回来"的图,后来我就是这么找回来的:先确认 seed,再一个参数一个参数地对,最后发现是我不知不觉换了采样器。要不是控制变量,我可能还在瞎点。
什么时候该固定,什么时候该放开
聊到这你可能觉得"那我以后全程固定 seed 不就完了"。不对。可复现是手段,不是目的,别本末倒置。
该固定的场景:
- 复现------你想在一张满意的图上继续改,得先能回到它。
- 做 A/B 对比------比较两个 prompt 写法、两个参数、两个模型谁更好,必须把 seed 摁死,否则你比的是运气,不是变量。
- 排查"翻车图"------某张图手崩了、脸糊了,固定 seed 才能反复复现那个问题,才谈得上定位和修。
该放开的场景:
- 探索阶段,你还不知道要什么,就想多看几种可能,这时候随机 seed 才是帮手,固定反而把自己框死。
- 批量出候选,一口气出一堆让人挑,要的就是多样性。
- 追求惊喜,有时候最好的那张恰恰来自你没预料到的那份噪声。
我的习惯是两段式:前期放开随机疯狂出,广撒网找感觉;一旦撞见一张对味的,立刻把它的 seed 和整份参数抠下来存好,后面进入"锁死 seed、精修参数"的模式。探索靠随机,收敛靠固定,各司其职。
最后
文生图看着像玄学,其实"每次都不一样"背后没什么魔法:它从一团随机噪声起步,一步步去噪成图,而那团噪声默认每次都换。seed 让你把噪声钉死,一串采样参数决定去噪的走法,任何一个变量动了图就跟着动。
把它当成一组可控的旋钮,而不是一个吐图的黑盒:想复现就固定 seed、连参数带环境一起存成"配方";想弄懂某个旋钮就固定其它、一次只拧一个;该探索的时候再把随机放回去。说到底,和我们平时写代码、查问题用的是同一套手艺------先让结果可复现,再谈优化。