正在加载...

从噪点到收敛:一周内构建出了一套MC风格的文生图模型的记录

技术杂货铺  ·  2025-12-26

预计阅读时间: 18.3 分钟
3081 字12 图250 字/分
本文内容较新·4天前更新
最后更新: 2025年12月26日

我想,这不会是一篇教程,也不是一篇非常成功的可借鉴的经验

它是一份关于 我自己构建的模型,为什么一开始一定失败 的个人记录

过去的接近一周的时间,我尝试训练一个能够生成 16*16像素的Minecraft 风格 文生图的AI模型

这个目标并不宏大,甚至可以说有种朴素的美:

    输入一句中文描述,生成一个符合Minecraft风格的像素贴图

但让我没有料到的是,这个看起来几乎没有什么复杂度的目标,会迫使我在短短几天内 推翻并重写代码三十多次

在这个过程中,我大量以来了Google Gemini和OpenAI Chatgpt帮我完成代码方面的实现

但真正错误的发现和问题的判断,以及为什么这一步一定不行,几乎全是来自自己与AI的反复讨论推演和否定


一、起点:一个看似“完美”的方案

最开始我的构思其实很简单,一个非常标准的方案:

  • 将样本中的图像量化为一组离散的颜色,创建一个8*8的调色板

  • 将16*16的训练样本展平为一个256长度的序列

  • 使用 Transformer Decoder 做自回归生成

  • 使用 BERT 作为文本和图像的桥梁

  • 使用 CrossEntropy & Label Smoothing

  • 生成阶段引入 CFG Classifier-Free Guidance

从代码角度看,这套方案几乎挑不出任何问题:

  • Loss稳定下降

  • 训练正常进行

  • 日志一切正常

但是实际上训练了100epoch的结果狠狠打了我的脸

  1. 形状完全是混乱的

  2. 颜色也完全随机,没有任何统一

  3. 略微调大CFG试图挽回,发现更乱了

ep125_蓝色的钻石.pngep120_蓝色的钻石.pngep135_蓝色的钻石.png

作为第一次涉足这个领域的人,我的第一反应非常经典

是不是学习率不太对?

是不是模型参数太少了?

是不是训练样本不够准确或者数量太少了?

现在回头看,对于当时的我,这些问题全部问错了方向


二、真正的怀疑:这不是参数的问题

最开始的我,试图让Gemini无脑增加这个训练的参数量,结果反而变得更加糟糕了

  • 训练速度慢如蜗牛

  • 100Epoch需要一整天

  • 结果更加不可控

所以我很快的放弃了这条路

真正的转折点,来自一个简单但是危险的问题:

我的模型,真的在学习形状和颜色吗?

当我开始从这个角度审视当前的训练时,问题和解决的方向才第一次变得清晰


三、在一步步的探索中解决问题

虽然我曾经从未涉足这个领域,但好在我热爱探讨问题,我去向AI询问了大量关于文生图训练的问题,自己摸着石头过河找到了一条路


问题一:Transformer根本不知道什么叫“形状”

在最初的实现中,所有图像都被flatten成了一个 一维的256长度序列

这意味着:

  • 模型只感知一维空间

  • 完全不知道上下左右的关系

在模型的眼里:

  • 第17个像素

  • 第18个像素

  • 第200个像素

在空间中是完全等价的

但文生图的核心却是 空间上的连续性

这是一个非常隐蔽但又非常致命的错误:

我在用一个毫无空间感知力的模型,学习空间上的结构


问题二:颜色的学不会,其实是模型自己的最优解

当时模型生成了大量橙色、黑色、灰色的混合块,完全无视描述中的颜色提示。

后来我才意识到,这并不是生成策略的问题,而是 Loss 设计的问题

在训练数据中:

  • 背景色占据了大的地位

  • 那些鲜艳的红蓝黄紫等非常稀有

而我最初的Loss设计默认了:

所有颜色同等重要

于是模型学到的最优策略是:

使用出现频率最高的颜色,最快地降低 Loss

下图展示了按照颜色出现频次排序的可视化调色板

palette_frequency_check.png

从 Loss 的角度看,这个策略毫无破绽;

从模型自身的角度看,它甚至非常“聪明”。

但从最终生成结果来看,这是一场彻头彻尾的赛博灾难,我训练了一个电子垃圾

这也是我第一次认识到

模型在失败时,往往是在非常认真的完成一个你不想要的目标


问题三:自回归放大了所有的错误

最早我使用了标准的自回归生成:

从左到右,从上到下,一个接一个生成

这个在文本的生成里很自然,但是在文生图的像素里却是灾难性的:

  • 前面几个像素一旦预测错误

  • 后续 200 多个像素都会被持续误导

在这种结构下,CFG 的作用也发生了反转:

  • CFG 小 → 模糊成一团

  • CFG 稍大 → 直接炸成随机色块

CFG 不再是语义增强器,而变成了一个 不稳定性的放大器

result_一把金色的剑.pngep15_红色的苹果.pngep10_一个蓝色的钻石.png


问题四:颜色过少导致信息被严重压缩

一开始我认为,对 Minecraft 风格贴图来说,64 色已经足够

而且颜色少意味着:

  • 训练更快

  • 更容易看到“效果”

但后来我在一次偶然的修改中,将调色板从 8×8 扩展到了 16×16,发现模型对颜色的学习速度明显提升

现在回头看原因非常清楚:

颜色数量过少,导致大量语义差异被压缩到同一个 token 中。

深蓝与浅蓝、冷色与暖色,被迫映射到同一个离散表示中

任何微小偏移都会导致颜色语义发生剧烈跳变。

palette_check.pngpalette_check.png


四、修补:一连串的新尝试

在逐步意识到这些问题后,我做了一系列修改,包括但不限于:

  • 改进调色板生成方式(按色相、明度排序)

  • 将透明色作为独立 token 处理

  • 扩充调色板规模与训练样本

  • 尝试不同的 embedding 维度和 Transformer 深度

  • 预计算文本 embedding,提高训练稳定性

这些改动并非毫无作用:

  • 训练更稳定了

  • Loss 曲线更好看了

  • 偶尔能看到一些模糊的形状

但模型依然陷在一个怪圈里——

它似乎在努力,但努力的方向依旧是错的。


五、承认:这不是代码缺陷,而是Transformer的特性

真正的进展来自一次认知的和考量的转变

我不再试图驯服Transformer这个狂暴的机器,而是承认他确实有一些特性使他无法完成这些任务

于是,我后来的人物开始围绕另一个核心思想:

1.图像是一个二维的场,而不是一个一维的序列

  • 我将token reshape回了2D,建立了一个二维的坐标

  • 引入了卷积神经网络用于建模贴图局部的连续性

  • 开始引入空间一致性(Total Variation) Loss

2.放弃AR,转向MaskGIT

  • 并行预测

  • 多轮润色

  • 先学习结构,再摸索细节

3.明确了新的Loss模式:什么重要、什么不重要

  • 根据颜色的出现频率加权

  • 根据饱和度再次加权

  • 对权重截断,防止权重过高或过低

4.改变文本条件

  • 不再使用Bert用于对其

  • 使用Clip进行全局embedding


六、明显的信号:学习率“好调了”

有一个非常真实,但是很难提前预料到的现象:

当模型开始合理时,学习率突然变得好调了

同样的优化器,同样的scheduler

  • 梯度方向开始稳定

  • 结构逐渐清晰

  • 颜色开始与文本对应

这让我更确信了一件事:

不是我不会调参,而是梯度本身是错的

ep285_3_一把蓝色的钻石剑_像素风格武器.pngep295_0_一个金色的圆形徽章中心镶嵌着紫色的方形宝石.pngep295_1_一把科幻风格的枪_带有紫色的枪身和白色的瞄准镜.png

ep285_3_一把蓝色的钻石剑_像素风格武器

ep290_0_一个金色的圆形徽章中心镶嵌着紫色的方形宝石

ep290_1_一把科幻风格的枪_带有紫色的枪身和白色的瞄准镜


七、回头看这一周:重要的收获

这套模型虽然现在依旧不完美

但对我来说,这一周的最大的收获不是生成了多少张合理的图,而是:

我终于开始明白,并能解释:

为什么第一版的模型一定会失败

很多时候,不是模型“没学会”,也不是数据不够,更不是算力不够,而是因为

我们让它做的事情,超出了它的能力


八、写在最后

我写下这份记录,不是为了证明我做对了什么,而是为了提醒未来可能再次走在这条路上的自己

当模型表现得像无规则的乱码时

也许不是它坏了,而是我没有告诉它如何理解这个世界

这份记录,对我来说很重要

也希望能让以后遇到相同问题的,跟我一样的新兵蛋子,可以有一点借鉴,少走一些弯路


最后,感谢 Google Gemini & OpenAI ChatGPT

让我在几乎没有 Python 基础的情况下,完成了一个真正可用的文生图模型原型

评论
正在加载验证组件