FLUX.1架构的理解9——从组件式拼接到端到端统一
文章目录
- 一、前言
- 二、文生图=>图生图=>组件式=>端到端
-
- 问题1:
- 回答1:
- 一、Flux.1 基础架构回顾
- 二、图生图 (img2img) 的本质
- 三、局部重绘 (Inpainting) 的两种技术路线
- 四、ControlNet、IP-Adapter、LoRA 在 Flux.1 中的角色
- 五、端到端专用模型:FLUX-Fill、FLUX-Redux、FLUX-Kontext
- 六、核心问题:为什么要重新训练?组件式生态不好吗?
- 七、生态演进逻辑:从"拼接"到"内化"
- 八、总结:你的困惑解答
- 问题2:
- 回答2:
- 1. 图生图(Img2Img)的原理:为什么不是"特征引导"而是"加噪去噪"?
- 2. 局部重绘路线 A:传统潜空间掩码法
- 3. 局部重绘路线 B:ControlNet-Inpainting
- 4. LoRA 微调主模型权重
- 5. ControlNet:冻结主模型,训练条件编码器
- 6. IP-Adapter:参考图像特征投影到文本 Token 空间
- 7. FLUX.1 Fill:官方 Inpainting/Outpainting 模型
- 8. FLUX.1 Redux:图像变体/风格迁移模型
- 9. FLUX.1 Kontext:上下文感知编辑模型
- 总结对比表
- 问题3:
- 回答3:
- 1. 传统潜空间掩码法 vs 图生图:是特例吗?
- 2. ControlNet-Inpainting 的拼接操作深度解析
- 3. 补充:FLUX Fill 可能如何处理这个问题?
- 总结
一、前言
在之前我主要是在看这篇论文:
Demystifying Flux Architecture
论文地址:https://arxiv.org/pdf/2507.09595
看完这篇论文我现在已经有一些理解了Flux.1的架构,主要是理解了文生图是怎么实现的,但是我还有一些问题,这篇就是回答这些问题,但是回答涉及的一些维度变化,以及具体的原理还需要再看一些论文和代码才能有更清楚地理解,现在只是了解一下。
二、文生图=>图生图=>组件式=>端到端
问题1:
1.我只理解了文生图,我没理解图生图是怎么样的
2.我没理解局部重绘inpainting、Controlnet、IP-Adapter、Lora这些组件加到Flux.1之后是怎么样的
3.社区出现了这个FLUX.1-dev-Controlnet-Inpainting-Alpha:
https://huggingface.co/alimama-creative/FLUX.1-dev-Controlnet-Inpainting-Alpha,这让我迷惑的是为什么会出现FLUX.1和Controlnet和inpainting的组合,这个模型是怎么训练出来的
4.还有一个迷惑就是为什么又会出现FLUX-Fill、FLUX-Redux、FLUX-Kontext,是FLUX试图重新训练一些模型以替代之前的组件式生态吗?它们又是怎么被训练的呢?组件式生态不好吗?为什么要重新训练呢?
回答1:
我来帮你系统梳理 Flux.1 的文生图之外的各种变体和生态演进。这确实是一个容易混淆的领域,因为 Flux 生态同时存在"组件式扩展"和"端到端专用模型"两条路线。
你的问题触及了扩散模型生态演进的两个核心矛盾:“组件式拼接” vs “端到端统一”。让我从架构层面帮你理清这些概念。
一、Flux.1 基础架构回顾
Flux.1 的核心是 Flow Matching (Rectified Flow) 而非传统扩散模型。它的去噪过程学习的是速度场 v θ ( x t , t ) v_\theta(x_t, t) vθ(xt,t),直接指向从噪声到数据点的直线路径 。
基础架构包含:
- 双文本编码器:T5-XXL + CLIP-L
- MMDiT (Multimodal Diffusion Transformer):处理文本和图像 token 的联合注意力
- VAE:将图像压缩到潜空间
二、图生图 (img2img) 的本质
图生图不是一个独立的模型,而是采样过程的干预:
标准文生图:噪声 → 去噪 → 图像
图生图: 图像 → 加噪(到某一步) → 去噪 → 新图像
具体实现:
- 将输入图像通过 VAE 编码为潜空间特征 z o r i g z_{orig} zorig
- 根据
denoising strength(如 0.5)决定加噪步数。若总步数为 28, strength=0.5 则从第 14 步开始(此时 z 14 z_{14} z14 是原图+噪声的混合) - 从 z 14 z_{14} z14 开始,用文本条件继续去噪到 z 0 z_0 z0
- VAE 解码得到新图像
关键洞察:模型权重完全不变,只是初始噪声状态不同。这解释了为什么 Flux.1 原生就支持 img2img——它不需要额外训练 。
三、局部重绘 (Inpainting) 的两种技术路线
路线 A:传统潜空间掩码法(无需训练)
输入:原图 + 二值掩码 mask(1=保留,0=重绘)
流程:
1. 原图编码为 z_orig
2. 对 z_orig 加噪得到 z_t
3. 对掩码区域:用去噪结果 z_t → z_{t-1}
4. 对非掩码区域:直接保留 z_orig 的对应位置(或混合)
5. 重复直到 z_0,VAE 解码
问题:边界融合不自然,语义一致性差,因为模型"看不到"被掩码区域外的上下文。
路线 B:ControlNet-Inpainting(阿里妈妈 Alpha/Beta)
这就是你提到的 FLUX.1-dev-Controlnet-Inpainting-Alpha。它的训练方法是:
架构改造:
- 冻结 Flux.1-dev 主模型(12B 参数不动)
- 新增一个可训练的 ControlNet 分支(通常是小型的 U-Net 或 DiT 编码器)
- 输入不仅包括潜空间噪声,还包括:
- 原图的 VAE 编码(作为条件)
- 掩码图(指示重绘区域)
- 文本提示
训练数据 :
- 1200 万张图像(LAION2B + 内部数据)
- 分辨率 768×768(Alpha)/ 1024×1024(Beta)
- 构造方式:对图像随机掩码,训练模型根据上下文重建被掩码区域
损失函数:
L = E x 0 , x 1 , t , m a s k ∥ v θ ( x t , t , c t e x t , c i m a g e , c m a s k ) − ( x 1 − x 0 ) ∥ 2 \mathcal{L} = \mathbb{E}_{x_0, x_1, t, mask} \| v_\theta(x_t, t, c_{text}, c_{image}, c_{mask}) - (x_1 - x_0) \|^2 L=Ex0,x1,t,mask∥vθ(xt,t,ctext,cimage,cmask)−(x1−x0)∥2
其中 c i m a g e c_{image} cimage 是原图条件, c m a s k c_{mask} cmask 是掩码条件。
为什么需要专门训练?
因为标准 Flux.1 的注意力机制没有被显式训练来"忽略"掩码区域并融合边界。ControlNet 分支专门学习这种条件化生成的能力 。
四、ControlNet、IP-Adapter、LoRA 在 Flux.1 中的角色
| 组件 | 作用层级 | 训练对象 | 核心机制 |
|---|---|---|---|
| LoRA | 权重空间 | 低秩矩阵 ( Δ W = B A \Delta W = BA ΔW=BA) | 微调主模型权重,学习特定风格/概念 |
| ControlNet | 特征空间 | 旁路网络 | 冻结主模型,训练条件编码器注入结构信息(canny/depth/pose) |
| IP-Adapter | 注意力空间 | 图像投影层 | 将参考图像特征投影到文本 token 空间,通过交叉注意力影响生成 |
在 Flux.1 中的特殊性:
- Flux 使用 流匹配 而非传统扩散,但上述组件原理不变
- Flux 的 MMDiT 同时处理文本和图像 token,所以 ControlNet 需要输出与 DiT 兼容的 token 序列
- IP-Adapter 需要将图像特征转换为与 T5/CLIP 文本嵌入对齐的格式
五、端到端专用模型:FLUX-Fill、FLUX-Redux、FLUX-Kontext
这是 Black Forest Labs (BFL) 推出的官方原生模型,与社区组件式方案形成竞争。
FLUX.1 Fill
定位:原生 inpainting/outpainting 模型
架构改变:
- 不是 ControlNet 旁路,而是直接修改主模型架构
- 输入序列同时包含:
- 文本 token(T5 + CLIP)
- 原图 VAE token
- 掩码 token
- 所有 token 通过统一的联合注意力处理,没有"主分支/旁分支"之分
训练方式 :
- 基于 FLUX.1-dev 权重初始化
- 在 inpainting 数据集上端到端微调(不是冻结主模型)
- 损失函数与标准流匹配相同,但输入 x t x_t xt 是掩码后的图像潜空间特征
优势:无需 ControlNet 的额外推理开销,边界融合更自然,因为注意力机制被训练来原生处理这种"部分已知、部分生成"的任务。
FLUX.1 Redux
定位:图像变体/风格迁移(类似 IP-Adapter 的替代品)
架构:
- 输入参考图像通过 VAE 编码
- 图像 token 直接与文本 token 拼接(concatenate)进入 DiT
- 没有单独的"图像投影"网络,而是让 DiT 自己学习如何理解图像条件
训练:
- 使用图像-文本-图像三元组: ( I r e f , T p r o m p t , I t a r g e t ) (I_{ref}, T_{prompt}, I_{target}) (Iref,Tprompt,Itarget)
- 目标是在给定参考图像和文本指令下生成变体
FLUX.1 Kontext
定位:通用图像编辑(“用文本指令编辑图像”)
架构:
- 同时编码输入图像和目标图像(均为 VAE token)
- 文本指令通过 T5 编码
- 三者拼接为统一序列,通过联合注意力处理
训练细节 :
- 阶段 1:从纯文生图检查点出发,联合微调文生图 + 图生图任务
- 阶段 2(Pro 版):使用 LADD (Latent Adversarial Diffusion Distillation) 提升效率
- 阶段 3(Dev 版):通过引导蒸馏压缩到 12B 参数,专门在编辑任务上训练(不训练纯文生图)
关键公式:
L = E x i n p u t , x t a r g e t , t ∥ v θ ( C o n c a t [ z i n p u t , z t a r g e t ] , t , c t e x t ) − ( x t a r g e t − x n o i s e ) ∥ 2 \mathcal{L} = \mathbb{E}_{x_{input}, x_{target}, t} \| v_\theta(Concat[z_{input}, z_{target}], t, c_{text}) - (x_{target} - x_{noise}) \|^2 L=Exinput,xtarget,t∥vθ(Concat[zinput,ztarget],t,ctext)−(xtarget−xnoise)∥2
注意输入是拼接的 token 序列,模型需要学会关注 z i n p u t z_{input} zinput 的未掩码区域,同时生成与 z t a r g e t z_{target} ztarget 对应的区域。
六、核心问题:为什么要重新训练?组件式生态不好吗?
组件式方案的局限性
-
组合爆炸与兼容性问题:
- Flux.1-dev + ControlNet-Inpainting + LoRA + IP-Adapter
- 每个组件独立训练,组合时可能出现特征冲突
- 例如:ControlNet 的梯度与 LoRA 的梯度在注意力层相互干扰
-
信息瓶颈:
- ControlNet 作为"旁路",其输出需要通过零卷积(zero convolution)缓慢注入主模型
- 图像条件信息在注入前被压缩,丢失高频细节
-
推理开销:
- ControlNet 需要额外的前向传播
- 多个组件叠加导致显存占用和延迟增加
-
训练效率低:
- ControlNet 冻结主模型,只能学习"如何影响"生成,不能调整主模型的内部表示
- 对于复杂编辑任务(如"把猫变成狮子,但保持背景光照"),这种间接控制不够精确
端到端方案的优势
-
统一注意力空间:
- 原图、掩码、文本在同一组注意力层中交互
- 模型可以学习"这是背景,要保持";"这是主体,要替换"的细粒度操作
-
原生支持多模态:
- Kontext 的输入定义天然支持"图像 + 文本 → 图像",无需外部拼接逻辑
-
蒸馏优化:
- Dev 版通过蒸馏在保持质量的同时降低计算成本(2秒 vs 7秒)
-
LoRA 生态的延续:
- Kontext 仍然支持 LoRA 微调,但微调的是编辑变换而非静态风格
- 训练数据变为 (原图, 编辑后图, 文本指令) 三元组
七、生态演进逻辑:从"拼接"到"内化"
阶段 1(2023-2024):
SD 1.5/XL → + ControlNet → + LoRA → + IP-Adapter
(组件式,每个解决特定问题)
阶段 2(2024 社区):
Flux.1-dev → + ControlNet-Inpainting(阿里妈妈)
(尝试在 Flux 上复现组件式生态)
阶段 3(2024-2025 官方):
FLUX-Fill / Redux / Kontext
(将高频需求"内化"到基础架构中)
不是替代,而是分层:
- 简单任务(风格迁移、姿态控制):组件式 LoRA/ControlNet 仍足够
- 复杂任务(语义编辑、上下文感知填充):端到端模型更优
- 专业场景(产品摄影、角色一致性):Kontext + 专用 LoRA
八、总结:你的困惑解答
| 你的问题 | 答案 |
|---|---|
| 为什么有 FLUX.1 + Inpainting ControlNet? | 社区尝试用组件式方案扩展 Flux,冻结主模型 + 训练旁路网络 |
| 为什么又出现 FLUX-Fill/Kontext? | BFL 认为 inpainting/editing 是高频需求,值得端到端优化 |
| 组件式不好吗? | 不是不好,是存在组合复杂度和信息瓶颈,端到端在复杂任务上更优 |
| 它们怎么训练的? | Fill/Kontext 基于 Flux 权重初始化,在特定任务数据上端到端微调(不冻结),使用标准流匹配损失 |
| 未来趋势? | 基础模型原生支持多模态编辑,组件式生态退居到轻量级定制(LoRA)和特定控制(ControlNet) |
问题2:
1.图生图只是对参考图加噪然后再去噪的话,岂不是变成预测生成了?为什么会跟我原先想象得不一样,我还以为图生图是先提取参考图特征,然后将这个特征用于引导图像去噪的过程(就像文生图里面的文字用于引导图像去噪一样)
2.局部重绘的路线 A:传统潜空间掩码法,这是什么意思?这个方法的输入输出和中间操作的维度变化是什么,尤其是输入:原图 + 二值掩码 mask,这个输入的二值掩码 mask应该不是输入到transformer的意思吧
3.局部重绘的路线 B:ControlNet-Inpainting的损失函数,这个方法训练和推理的输入输出和中间操作的维度变化是什么
4.LoRA微调主模型权重,这是什么意思?这个方法的输入输出和中间操作的维度变化是什么
5.ControlNet冻结主模型,训练条件编码器注入结构信息(canny/depth/pose),这是什么意思?这个方法训练和推理的输入输出和中间操作的维度变化是什么
6.IP-Adapter将参考图像特征投影到文本 token 空间,通过交叉注意力影响生成,这是什么意思?这个方法训练和推理的输入输出和中间操作的维度变化是什么
7.FLUX.1 Fill训练和推理是如何操作的?输入输出和中间操作的维度变化是什么
8.FLUX.1 Redux训练和推理是如何操作的?输入输出和中间操作的维度变化是什么
9.FLUX.1 Kontext 训练和推理是如何操作的?输入输出和中间操作的维度变化是什么
回答2:
我来详细解答你关于 FLUX.1 及相关扩散模型技术的这些问题。这些问题涉及扩散模型的核心机制,我会从原理、输入输出维度和操作流程三个层面逐一解释。
1. 图生图(Img2Img)的原理:为什么不是"特征引导"而是"加噪去噪"?
你的直觉 vs 实际实现
| 你的想象 | 实际实现 |
|---|---|
| 先提取参考图特征 → 像文本一样通过交叉注意力引导去噪 | 直接对参考图加噪 → 从噪声开始去噪 |
为什么实际是这样?
扩散模型的核心是一个噪声预测网络 ϵ θ ( x t , t , c ) \epsilon_\theta(x_t, t, c) ϵθ(xt,t,c),它学习的是"给定带噪图像 x t x_t xt 和条件 c c c,预测噪声是什么"。
图生图的数学本质:
x t = α ˉ t ⋅ x 0 + 1 − α ˉ t ⋅ ϵ x_t = \sqrt{\bar{\alpha}_t} \cdot x_0 + \sqrt{1-\bar{\alpha}_t} \cdot \epsilon xt=αˉt⋅x0+1−αˉt⋅ϵ
- x 0 x_0 x0:你的参考图(编码到潜空间后)
- t t t:你选择的步数(如 t = 0.7 × T t=0.7 \times T t=0.7×T),步数越大,保留原图信息越少
- 从 x t x_t xt 开始,模型继续去噪到 x 0 ′ x_0' x0′
为什么不是特征提取引导?
- 架构限制:FLUX 的 Transformer 是自回归式去噪,不是 VAE 编码器-解码器结构。它没有"提取高层语义特征再注入"的明确通路。
- 条件机制:文本通过交叉注意力注入,但图像条件没有对应的"图像条件编码器"(除非额外添加如 IP-Adapter)
- 语义保留:加噪去噪本身就保留了低频结构信息(因为 DDPM 的噪声 schedule 对低频影响小),所以 x t x_t xt 已经包含了足够的结构先验
维度变化:
输入: 参考图 RGB [B, 3, H, W]
↓ VAE Encode
潜空间: [B, 16, H/8, W/8] (FLUX 使用 16-channel 潜空间)
↓ 加噪到步数 t (如 0.7*50=35 步)
x_t: [B, 16, H/8, W/8]
↓ Transformer 去噪 (50-35=15 步)
x_0': [B, 16, H/8, W/8]
↓ VAE Decode
输出: [B, 3, H, W]
关键洞察:图生图本质上是一种有偏采样——从靠近目标分布的位置开始,而不是从纯噪声开始。它不是"特征引导",而是"初始化引导"。
2. 局部重绘路线 A:传统潜空间掩码法
核心思想
在潜空间中,只替换 mask 对应区域的噪声,保留非 mask 区域的原图潜空间特征。
详细流程与维度变化
阶段 1: 准备输入
─────────────────────────────────────────
原图 RGB: [B, 3, H, W]
↓ VAE Encoder (8x 下采样)
原图潜码 z_0: [B, 16, h, w] 其中 h=H/8, w=W/8
二值掩码 Mask: [B, 1, H, W] (0=保留, 1=重绘)
↓ 下采样到潜空间分辨率 (最近邻 or 平均池化)
潜空间掩码 M: [B, 1, h, w]
文本条件: [B, 77, 4096] (T5 编码后的文本 embedding)
─────────────────────────────────────────
阶段 2: 扩散过程 (每步 t)
─────────────────────────────────────────
从纯噪声初始化潜空间:
z_t^new = noise [B, 16, h, w]
对于每个去噪步 t (从 T 到 0):
1. 模型预测噪声:
ε_pred = Transformer(z_t, t, text_emb) → [B, 16, h, w]
2. 计算去噪后的潜码:
z_{t-1} = denoise_step(z_t, ε_pred) → [B, 16, h, w]
3. 【关键】掩码混合 (Blending):
z_{t-1} = M ⊙ z_{t-1}^new + (1-M) ⊙ z_{t-1}^orig
───────────────────────────────────────────────
维度: [B,1,h,w] ⊙ [B,16,h,w] + [B,1,h,w] ⊙ [B,16,h,w]
结果: [B, 16, h, w]
解释: mask=1 的区域用新生成的, mask=0 的区域保留原图对应步的潜码
注意: z_{t-1}^orig 是原图加噪到 t-1 步的潜码
z_{t-1}^orig = √(ᾱ_{t-1})·z_0 + √(1-ᾱ_{t-1})·ε
阶段 3: 解码
─────────────────────────────────────────
z_0: [B, 16, h, w]
↓ VAE Decoder
输出: [B, 3, H, W]
关于掩码的澄清
掩码不是输入到 Transformer 的!
- 掩码 不参与 Transformer 的前向传播
- 掩码只在每步去噪后做潜空间混合(Blending)时使用
- Transformer 看到的仍然是完整的潜码(混合后的),它不知道哪里是 mask 区域
为什么这样做有效?
- 非 mask 区域强制保持原图 → 提供结构约束
- mask 区域自由生成 → 但受上下文(mask 边界)影响
- 边界处通过扩散过程的平滑性自然融合
3. 局部重绘路线 B:ControlNet-Inpainting
核心思想
训练一个专门的 ControlNet 分支,将"masked image + mask"作为条件,通过残差连接注入到主模型的 Transformer 中。
训练阶段
输入:
─────────────────────────────────────────
原图 x_0: [B, 3, H, W]
掩码 M: [B, 1, H, W] (二值)
被掩码图像 x_m = M ⊙ noise + (1-M) ⊙ x_0 [B, 3, H, W]
拼接条件图: concat[x_m, M] → [B, 4, H, W]
↓ VAE Encoder
条件潜码 c: [B, 4, h, w] (注意: 4=3+1, 不是16)
加噪潜码 x_t: [B, 16, h, w] (原图加噪)
文本条件: [B, 77, 4096]
─────────────────────────────────────────
ControlNet 编码器:
─────────────────────────────────────────
c [B, 4, h, w]
↓ 4层下采样卷积 (类似 UNet Encoder)
特征序列: [B, C_i, h_i, w_i] for i in 1..N
↓ 每个分辨率投影到 Transformer 维度
残差注入: 在每个 Transformer block 的 attention 后添加
ΔF_i = Linear(Flatten(Conv(c))) → [B, h_i*w_i, D]
推理阶段
输入:
─────────────────────────────────────────
用户图 + mask → 生成 x_m (masked image)
条件潜码 c: [B, 4, h, w]
去噪过程:
for t in [T, ..., 0]:
1. 主模型计算: F = Transformer(x_t, t, text)
2. ControlNet 计算: ΔF = ControlNet(c, t)
3. 残差注入: F' = F + ΔF (在某些层)
4. 继续主模型后续层
5. 预测噪声 ε_pred
6. 去噪步进
─────────────────────────────────────────
输出: [B, 3, H, W]
维度变化总结
| 张量 | 维度 | 说明 |
|---|---|---|
| 输入图像 | [B, 3, H, W] | RGB |
| 输入掩码 | [B, 1, H, W] | 二值 |
| 条件拼接 | [B, 4, H, W] | masked_img + mask |
| 条件潜码 | [B, 4, h, w] | VAE encode (4ch→不经过VAE的mean/std) |
| 加噪潜码 | [B, 16, h, w] | 标准潜空间 |
| ControlNet 输出 | [B, L, D] | 多个尺度的残差特征 |
| 最终输出 | [B, 3, H, W] | 解码后图像 |
关键区别:ControlNet-Inpainting 的 mask 信息确实进入了 Transformer(通过 ControlNet 分支),而传统潜空间掩码法的 mask 只在后处理混合使用。
4. LoRA 微调主模型权重
核心思想
LoRA (Low-Rank Adaptation):冻结原模型权重 W 0 W_0 W0,只训练低秩分解矩阵 A A A 和 B B B。
W = W 0 + Δ W = W 0 + B ⋅ A W = W_0 + \Delta W = W_0 + B \cdot A W=W0+ΔW=W0+B⋅A
其中 B ∈ R d × r B \in \mathbb{R}^{d \times r} B∈Rd×r, A ∈ R r × k A \in \mathbb{R}^{r \times k} A∈Rr×k, r ≪ min ( d , k ) r \ll \min(d, k) r≪min(d,k)
在 FLUX 中的应用位置
FLUX 的 Transformer 主要包含:
- Self-Attention: Q , K , V , O Q, K, V, O Q,K,V,O 投影矩阵
- Cross-Attention: Q , K , V , O Q, K, V, O Q,K,V,O 投影矩阵(与文本交互)
- MLP (FeedForward): 两个线性层
- AdaLN modulation: 时间步和条件 embedding 的缩放/偏移参数
通常 LoRA 只加在 Attention 和 MLP 的线性层上。
维度变化
假设 Transformer 中某一层:
输入 X: [B, L, D] (B=batch, L=seq_len, D=hidden_dim=3072)
Self-Attention 的 Q 投影:
─────────────────────────────────────────
原始权重 W_Q: [D, D] = [3072, 3072] ← 冻结
LoRA 分支:
X ──→ Linear_A ──→ Linear_B ──→ ⊕ ──→ Q
[B,L,D] [D,r] [r,D] [B,L,D] [B,L,D]
A: [3072, r] (通常 r=4,8,16,64)
B: [r, 3072]
ΔW = B·A: [3072, 3072] (但只需存 r*(3072+3072) 参数)
实际计算:
Q = X·W_Q + X·(B·A) = X·W_Q + (X·B)·A
─────────────────────────────────────────
h = X @ B → [B, L, r]
Q_lora = h @ A → [B, L, D]
Q_full = X @ W_Q + Q_lora → [B, L, D]
训练 vs 推理
| 阶段 | 操作 | 参数量 |
|---|---|---|
| 训练 | 冻结 W 0 W_0 W0,只优化 A , B A, B A,B | 仅 ~0.1%-1% 的可训练参数 |
| 推理 | 可合并权重 W = W 0 + B A W = W_0 + BA W=W0+BA,无额外开销 | 与原始模型相同 |
为什么有效?
- 预训练模型已学到丰富先验
- 特定任务(如特定角色、风格)只需微调低维子空间
- 秩 r r r 越小,越接近原模型; r r r 越大,学习能力越强
5. ControlNet:冻结主模型,训练条件编码器
核心思想
完全冻结预训练的 FLUX Transformer,在旁边训练一个可复制的编码器(通常复制主模型前几层),将条件图像(canny/depth/pose)编码成多尺度特征,通过零卷积注入主模型。
架构详解
整体架构:
─────────────────────────────────────────
条件图像 (如 Canny 边缘)
[B, 3, H, W]
↓
┌─────────────────────┐
│ 条件编码器 │
│ (复制主模型前几层) │
│ + 零卷积初始化 │
└─────────────────────┘
↓
多尺度残差特征 [多分辨率]
↓
┌────────────────────┼────────────────────┐
↓ ↓ ↓
Transformer Block 1 Block 2 ... Block N
(注入残差) (注入残差) (注入残差)
↓ ↓ ↓
主模型继续计算... → 预测噪声 → 去噪
─────────────────────────────────────────
维度变化(以 Canny 条件为例)
输入:
─────────────────────────────────────────
条件图 I_c: [B, 3, H, W] (Canny 边缘图,黑底白线)
↓ 轻微下采样卷积 (如 stride=2)
I_c': [B, C, H/2, W/2]
主模型输入:
噪声潜码 x_t: [B, 16, h, w] (h=H/8, w=W/8)
文本条件: [B, 77, 4096]
─────────────────────────────────────────
ControlNet 编码器 (复制主模型 Encoder):
─────────────────────────────────────────
条件图通过类似 VAE 的编码器:
z_c = Encoder(I_c') → [B, 16, h, w]
然后进入复制的 Transformer blocks:
for each block i in ControlNet:
F_i = TransformerBlock_i(z_c, t, text) → [B, L_i, D]
【关键】零卷积注入:
ΔF_i = ZeroConv(F_i) → [B, L_i, D]
初始时 ZeroConv 权重=0,所以 ΔF_i=0,不影响主模型
将 ΔF_i 加到主模型对应 block 的残差连接中
─────────────────────────────────────────
主模型 (冻结):
─────────────────────────────────────────
for each block i in MainModel:
F_i = TransformerBlock_i(F_{i-1}, t, text)
F_i = F_i + ΔF_i ← 注入 ControlNet 残差
─────────────────────────────────────────
训练 vs 推理
| 训练 | 推理 | |
|---|---|---|
| 主模型 | ❌ 冻结 | ❌ 冻结 |
| ControlNet | ✅ 训练 (仅零卷积和条件编码器) | ✅ 前向传播 |
| 输入 | (x_t, I_c, text) → 预测噪声 | 相同 |
| 损失 | MSE(ε_pred, ε_gt) | 无损失,直接生成 |
为什么"零卷积"很重要?
- 初始化时 ControlNet 输出为 0 → 主模型行为与预训练时完全一致
- 训练初期不会破坏预训练知识
- 逐渐学习条件控制,是一种渐进式微调
6. IP-Adapter:参考图像特征投影到文本 Token 空间
核心思想
将参考图像编码成一组"图像 token",这些 token 的维度与文本 token 相同,然后通过交叉注意力注入到 Transformer 中,就像文本条件一样。
架构详解
文本条件路径 (标准):
─────────────────────────────────────────
文本: "a photo of a cat"
↓ T5-XXL Encoder
文本 token: [B, 77, 2048] (T5 输出)
↓ 投影到 query/key/value 空间
文本 emb: [B, 77, D] (D=3072 for FLUX)
─────────────────────────────────────────
图像条件路径 (IP-Adapter 新增):
─────────────────────────────────────────
参考图 I_ref: [B, 3, H, W]
↓ CLIP Image Encoder (通常 ViT-H/14)
图像特征: [B, N, D_clip] (如 N=257, D_clip=1280)
↓ Image Projection (可学习的线性层/MLP)
图像 token: [B, N_img, D] (D=3072, 与文本 token 同维度!)
─────────────────────────────────────────
交叉注意力注入:
─────────────────────────────────────────
在 Transformer 的 Cross-Attention 层:
Q = X @ W_Q [B, L, D] (X 是图像潜码序列)
K = concat[K_text, K_image] [B, 77+N_img, D]
V = concat[V_text, V_image] [B, 77+N_img, D]
Attention(Q, K, V) = softmax(Q·K^T/√d) · V
结果: 图像信息通过 K,V 影响 Q 的注意力分布
─────────────────────────────────────────
维度变化详细表
| 张量 | 维度 | 说明 |
|---|---|---|
| 参考图 | [B, 3, H, W] | 如 224x224 |
| CLIP 图像特征 | [B, 257, 1280] | ViT 的 patch tokens + cls token |
| 图像 token (投影后) | [B, N_img, 3072] | N_img 通常=4或16,可学习 |
| 文本 token | [B, 77, 3072] | 标准 T5 输出投影 |
| 拼接后的 K/V | [B, 77+N_img, 3072] | 文本+图像一起作为条件 |
| 查询 Q | [B, L, 3072] | L = h×w + text_len (FLUX 的混合序列) |
| 注意力输出 | [B, L, 3072] | 与 Q 同维度 |
训练 vs 推理
| 阶段 | 操作 |
|---|---|
| 训练 | 冻结主模型 + CLIP,只训练 Image Projection 层 + 可能微调 cross-attn 的 K,V 投影 |
| 推理 | 提取参考图特征 → 投影 → 与文本 token 拼接 → 正常 cross-attention |
为什么投影到文本 token 空间?
- 兼容性:无需修改主模型架构,复用现有的 cross-attention
- 灵活性:可以像调整 text prompt 权重一样调整图像条件权重
- 组合性:文本和图像条件可以任意组合、插值
7. FLUX.1 Fill:官方 Inpainting/Outpainting 模型
核心创新
FLUX.1 Fill 是 Black Forest Labs 发布的专门用于图像修复和扩展的模型。它不是插件,而是原生支持 mask 条件的完整模型。
训练方式
输入构造 (训练时):
─────────────────────────────────────────
原图 x_0: [B, 3, H, W]
掩码 M: [B, 1, H, W] (1=需要填充的区域)
被掩码图 x_m = M ⊙ gray + (1-M) ⊙ x_0 [B, 3, H, W]
(用灰色填充 mask 区域,或随机噪声)
条件拼接: concat[x_m, M] → [B, 4, H, W]
↓ VAE Encoder
条件潜码 c: [B, 4, h, w]
注意: FLUX Fill 的 VAE 可能修改以接受 4ch 输入
─────────────────────────────────────────
目标:
原图潜码 z_0: [B, 16, h, w] (标准 VAE encode)
训练目标: 预测噪声 (标准扩散损失)
─────────────────────────────────────────
推理流程与维度
输入:
─────────────────────────────────────────
用户图: [B, 3, H, W]
掩码: [B, 1, H, W]
被掩码图 x_m: [B, 3, H, W] (mask 区域填灰/噪声)
条件: concat[x_m, M] → [B, 4, H, W]
↓ VAE (修改版)
条件潜码: [B, 4, h, w] (或可能是 [B, 16, h, w] 如果内部处理)
文本条件: [B, 77, 4096] (可选,描述填充内容)
─────────────────────────────────────────
去噪:
─────────────────────────────────────────
初始化: z_T ~ N(0,1) [B, 16, h, w]
for t in [T, ..., 0]:
# FLUX Fill 的 Transformer 内部会融合条件潜码
ε_pred = Transformer(z_t, t, text, condition=c)
z_{t-1} = denoise_step(z_t, ε_pred)
─────────────────────────────────────────
输出:
─────────────────────────────────────────
z_0: [B, 16, h, w]
↓ VAE Decode
输出图: [B, 3, H, W]
(非 mask 区域应该与原图一致,mask 区域生成新内容)
与传统方法的关键区别
| 传统潜空间掩码 | FLUX.1 Fill | |
|---|---|---|
| mask 信息 | 外部混合,不进入模型 | 内部条件,模型原生理解 |
| 架构 | 标准模型 + 后处理 | 修改的 VAE/Transformer |
| 一致性 | 边界可能不自然 | 端到端训练,边界更自然 |
| 扩展性 | 支持 outpainting | 原生支持 |
8. FLUX.1 Redux:图像变体/风格迁移模型
核心思想
FLUX.1 Redux 是一个图像到图像的转换/变体生成模型。它接收一张参考图,生成保持其内容但可改变风格、视角等的变体。
推测的架构(基于官方描述和类似工作)
Redux 的核心可能是:
- 图像编码器:将参考图编码为紧凑的语义表示
- 与文本条件的融合:将图像表示与文本 prompt 结合
- 引导去噪:从噪声开始,同时受文本和图像条件引导
训练/推理流程:
─────────────────────────────────────────
参考图 I_ref: [B, 3, H, W]
↓ CLIP/SigLIP Image Encoder
图像特征: [B, N, D_img] (如 768-dim)
↓ 投影层
图像条件 token: [B, N_img, D] (D=3072)
文本 prompt: [B, 77, 4096] (可选,如 "same character, oil painting")
─────────────────────────────────────────
去噪过程:
─────────────────────────────────────────
z_t: [B, 16, h, w] (纯噪声初始化)
for t in [T, ..., 0]:
# 图像条件通过 cross-attention 注入
# 类似 IP-Adapter,但可能是更深度的集成
ε_pred = Transformer(z_t, t, text, image_tokens)
z_{t-1} = denoise_step(z_t, ε_pred)
─────────────────────────────────────────
输出: [B, 3, H, W] (保持参考图内容,风格/视角变化)
维度变化
| 张量 | 维度 | 说明 |
|---|---|---|
| 参考图 | [B, 3, H, W] | 输入 |
| 图像特征 | [B, N, D_img] | 视觉编码器输出 |
| 图像条件 token | [B, N_img, 3072] | 投影后,与文本同空间 |
| 文本条件 | [B, 77, 3072] | 可选 |
| 噪声潜码 | [B, 16, h, w] | 标准 |
| 输出 | [B, 3, H, W] | 变体图像 |
与 IP-Adapter 的区别
- Redux 可能是端到端训练的专门模型,而非插件
- 图像条件可能通过更深层的方式融合(如直接修改 attention 机制)
- 可能使用更强的图像编码器(如自监督视觉模型)
9. FLUX.1 Kontext:上下文感知编辑模型
核心思想
FLUX.1 Kontext 是用于上下文感知的图像编辑,支持:
- 多轮对话式编辑
- 保持图像 ID/角色一致性
- 理解图像中的关系和上下文
推测的架构(基于官方描述)
Kontext 的核心创新可能是:
- 多模态上下文编码器:同时编码图像和编辑指令
- 记忆机制:保持跨轮次的角色/对象一致性
- 细粒度控制:精确编辑指定区域而不破坏其他内容
多轮对话流程:
─────────────────────────────────────────
轮次 1:
输入: 原图 I_0 + 指令 "把猫变成橘色"
↓ Kontext 编码器
上下文表示 C_0: [B, L_ctx, D]
生成: I_1 (猫变橘色,其他不变)
轮次 2:
输入: I_1 + 指令 "把背景换成海滩"
↓ Kontext 编码器 (携带 C_0 历史)
上下文表示 C_1: [B, L_ctx, D] (融合历史)
生成: I_2 (猫还是橘色,背景变海滩)
─────────────────────────────────────────
维度变化推测
单轮推理:
─────────────────────────────────────────
输入图 I: [B, 3, H, W]
↓ VAE
潜码 z: [B, 16, h, w]
编辑指令: "把左边的狗换成猫"
↓ T5 + 可能的空间解析模块
文本条件: [B, 77, 4096]
空间条件: [B, h, w] (指示编辑区域的热图/坐标)
可选: 历史上下文 (多轮时)
历史 token: [B, N_hist, D]
Transformer 输入:
- 图像 token: [B, h*w, D] (flattened 潜码)
- 文本 token: [B, 77, D]
- 历史 token: [B, N_hist, D] (多轮时)
- 编辑指令解析出的空间/对象 token
输出: [B, 3, H, W]
─────────────────────────────────────────
关键技术推测
| 特性 | 可能实现 |
|---|---|
| 角色一致性 | 跨轮次的图像 token 缓存/更新机制 |
| 精确编辑 | 空间 mask + 指令的联合条件 |
| 上下文理解 | 对象关系图 (scene graph) 嵌入 |
| 多轮对话 | KV-cache 风格的上下文缓存 |
总结对比表
| 方法 | 核心机制 | 条件如何进入模型 | 是否修改主模型 | 训练成本 |
|---|---|---|---|---|
| Img2Img | 加噪初始化 | 不进入,作为起点 | 否 | 无 |
| 潜空间掩码 | 每步混合 | 不进入,后处理 | 否 | 无 |
| ControlNet-Inpainting | 残差注入 | ControlNet 编码器 → 残差 | 否,新增分支 | 中等 |
| LoRA | 低秩微调 | 修改权重本身 | 是,低秩修改 | 低 |
| ControlNet | 残差注入 | 条件编码器 → 零卷积 | 否,新增分支 | 中等 |
| IP-Adapter | Cross-Attn | 图像→投影→文本空间 | 否,新增投影层 | 低 |
| FLUX Fill | 原生 Inpainting | 修改 VAE/Transformer | 是,端到端 | 高(预训练) |
| FLUX Redux | 图像条件生成 | 深度图像编码 | 是,端到端 | 高(预训练) |
| FLUX Kontext | 上下文编辑 | 多模态上下文编码 | 是,端到端 | 高(预训练) |
问题3:
1.局部重绘路线 A:传统潜空间掩码法是可以看做是图生图的特例吗?为什么?感觉有点像但是操作不同呀,图生图是先对参考图进行加噪然后再去噪,传统潜空间掩码法似乎是在每一步去噪之后再把掩码中的上下文信息贴回去,以实现上下文保持不变,对掩码目标区域去噪
2.局部重绘路线 B:ControlNet-Inpainting 的这一步:被掩码图像 x_m = M ⊙ noise + (1-M) ⊙ x_0之后拼接条件图: concat[x_m, M],这个拼接操作要怎么理解呢?如果只是为了引导transformer加噪的过程中要在掩码目标区域生成的话,为什么不直接用掩码呢?是因为x_m是作为上下文信息输入,掩码M才是用于引导吗?可是它们拼接之后不是上下文信息和引导信息混在一起了吗?难道各自有位置编码做区分?位置编码能区分吗?怎么区分的? 然后经过VAE编码器之后得到条件潜码 c,可是加噪又是在原图加噪,条件潜码 c是如何引导原图生成过程的呢?是跟文字引导一样吗?
回答3:
1. 传统潜空间掩码法 vs 图生图:是特例吗?
答案是:不是简单的特例,而是两种不同范式
| 图生图 (Img2Img) | 潜空间掩码法 (Inpainting A) | |
|---|---|---|
| 起点 | 参考图加噪到 x t x_t xt | 纯噪声 x T x_T xT |
| 参考图作用 | 作为去噪的初始状态 | 作为每步的硬约束 |
| 信息流动 | 只影响第一步,后续自由扩散 | 每步强制贴回原图信息 |
| 语义保留 | 依赖扩散过程的平滑性 | 强制保持非 mask 区域不变 |
| 可控性 | 低(strength 控制整体变化) | 高(mask 精确控制区域) |
为什么不是特例?
图生图的本质:
x_t = √(ᾱ_t)·z_0 + √(1-ᾱ_t)·ε
然后从这个 x_t 开始去噪到 x_0'
→ 参考图 z_0 的信息只存在于初始状态 x_t 中
→ 去噪过程中,模型自由生成,参考图信息逐渐"稀释"
→ 最终 x_0' 可能与 z_0 差异很大(取决于 t 的大小)
潜空间掩码法的本质:
每步: z_{t-1} = M ⊙ z_{t-1}^new + (1-M) ⊙ z_{t-1}^orig
→ 参考图信息每步都被"贴回去"
→ 非 mask 区域永远保持原图(强制约束)
→ mask 区域受上下文影响,但边界被精确控制
关键区别:信息注入的"硬度"
图生图: 软约束 ──→ 参考图信息在噪声中,模型可以选择性忽略
掩码法: 硬约束 ──→ 非 mask 区域被强制锁定,模型无法修改
你可以把图生图理解为"从参考图附近开始随机游走",而掩码法是"在 mask 区域内随机游走,但边界被钉死"。
2. ControlNet-Inpainting 的拼接操作深度解析
你的困惑非常合理!让我拆解这个设计的每个层面。
2.1 为什么要 concat[x_m, M] 而不是只用 M?
直觉: 只用 M 告诉模型"这里要重绘"
问题: 模型不知道"重绘成什么样"或"周围是什么"
实际: concat[x_m, M] 提供两个互补信息
─────────────────────────────────────────
x_m = M ⊙ noise + (1-M) ⊙ x_0
通道 0-2 (x_m): 保留的上下文内容 + mask 区域的噪声占位
通道 3 (M): 明确标记"哪里需要生成新内容"
─────────────────────────────────────────
类比理解:
- x m x_m xm 就像一张被涂改过的草稿,涂改处是乱码,但周围是完整的
- M M M 就像红笔圈出的标记,告诉模型"圈出来的地方是乱码,请重写"
- 两者缺一不可:没有 x m x_m xm,模型不知道上下文;没有 M M M,模型不知道哪里是乱码
2.2 它们混在一起,怎么区分?位置编码能区分吗?
答案是:不需要位置编码区分,因为通道维度本身就是区分!
concat[x_m, M] 的维度:
─────────────────────────────────────────
x_m: [B, 3, H, W] → RGB 图像
M: [B, 1, H, W] → 单通道掩码
拼接后: [B, 4, H, W]
空间结构:
每个空间位置 (i,j) 有 4 个数值:
[R_ij, G_ij, B_ij, M_ij]
↑ 来自 x_m ↑ 来自 M
─────────────────────────────────────────
卷积层天然能区分通道:
# ControlNet 的第一层卷积
conv1 = nn.Conv2d(4, 64, kernel_size=3, padding=1)
# 输入: [B, 4, H, W]
# 卷积核权重形状: [64, 4, 3, 3]
# 这意味着每个输出通道都独立学习 4 个输入通道的组合模式
卷积的视角:
- 卷积核在 ( i , j ) (i,j) (i,j) 位置看到的是一个 4 维向量 [ R , G , B , M ] [R,G,B,M] [R,G,B,M]
- 不同的卷积核可以对不同通道敏感:
- 核 A:主要关注 RGB,提取边缘/纹理
- 核 B:主要关注 M,识别 mask 边界
- 核 C:联合关注 RGB 和 M,理解"mask 内外的关系"
不需要位置编码,因为:
- 通道即语义:模型通过训练学会"第 4 通道是掩码信息"
- 空间对应:每个 ( i , j ) (i,j) (i,j) 的 RGB 和 M 天然对齐
- 卷积归纳偏置:局部感受野自然处理空间关系
2.3 VAE 编码后,条件潜码 c 如何引导原图生成?
这是最关键的问题!让我详细说明:
整体流程的维度对应:
─────────────────────────────────────────
条件路径 (ControlNet):
concat[x_m, M]: [B, 4, H, W]
↓ 特殊 VAE (或浅层卷积编码器)
c: [B, C_c, h, w] (C_c 可能是 4 或 16)
主路径 (扩散):
原图加噪 x_t: [B, 16, h, w]
↓ Transformer 去噪
x_{t-1}: [B, 16, h, w]
─────────────────────────────────────────
c 如何影响 x_t 的去噪?
ControlNet 的工作方式:
─────────────────────────────────────────
1. c [B, C_c, h, w] 进入 ControlNet 编码器
(通常是复制的主模型前几层,但输入通道改为 4)
2. ControlNet 产生多尺度特征:
{F_1, F_2, ..., F_N}
F_i 的形状: [B, L_i, D] 其中 L_i = h_i × w_i
3. 在主模型 Transformer 的每个 block:
标准流程:
F = SelfAttention(F) + F
F = CrossAttention(F, text_emb) + F ← 文本引导在这里
F = FFN(F) + F
ControlNet 注入:
F = F + ZeroConv(ControlNet_Feature) ← 条件引导在这里!
─────────────────────────────────────────
维度: [B, L, D] + [B, L, D] = [B, L, D]
注意: 这是残差连接!ControlNet 输出被加到主特征上
─────────────────────────────────────────
与文本引导的对比:
| 文本引导 | ControlNet 引导 | |
|---|---|---|
| 机制 | Cross-Attention | 残差相加 (Skip Connection) |
| 交互方式 | Q(图像) × K(文本) → 注意力权重 | 直接特征偏移 |
| 维度 | K/V: [B, 77, D] | 残差: [B, L, D] |
| 作用位置 | 特定 Cross-Attn 层 | 多个 Transformer block |
| 信息类型 | 语义/概念 | 空间结构/布局 |
关键理解:
文本引导的数学:
Attention(Q_img, K_text, V_text) = softmax(Q_img·K_text^T/√d)·V_text
→ 这是"查询-检索"机制:图像 token 查询文本语义
→ 每个图像位置独立决定关注哪些文本词
ControlNet 引导的数学:
F_out = F_main + ΔF_control
→ 这是"直接偏移"机制:直接修改特征表示
→ ΔF_control 已经编码了"mask 区域应该生成什么"的空间信息
为什么 c 能引导 mask 区域?
ControlNet 的训练目标:
─────────────────────────────────────────
输入: (x_m, M, text) + x_t (原图加噪)
目标: 预测噪声 ε,使得去噪后得到完整原图 x_0
损失: L = ||ε_pred - ε_gt||²
训练过程中:
- ControlNet 学习从 (x_m, M) 中提取"缺失内容应该是什么"的信息
- 特别是 M=1 的区域,ControlNet 必须从上下文 (M=0 的 x_m) 推断
- 这种推断被编码为 ΔF,注入主模型
推理时:
- 对于 M=1 的区域,ControlNet 产生强信号 ΔF,引导生成新内容
- 对于 M=0 的区域,ControlNet 产生弱信号(或零),保持原图
─────────────────────────────────────────
2.4 一个更直观的类比
想象你在修复一幅古画:
传统潜空间掩码法 (方法 A):
→ 你每画一笔,助手就把非破损区域的原画贴回去
→ 你只能修改破损区域,但不知道"应该画什么"
→ 全靠你自己的技艺
ControlNet-Inpainting (方法 B):
→ 助手先看破损区域周围的完整画面 (x_m)
→ 助手再看到标记 (M) 知道哪里破损
→ 助手基于上下文给出建议:"这里应该是天空,用蓝色"
→ 这个建议 (ΔF) 融入你的绘画过程
→ 你结合建议和技艺完成修复
3. 补充:FLUX Fill 可能如何处理这个问题?
FLUX.1 Fill 作为原生 Inpainting 模型,可能采用了更优雅的设计:
推测的 FLUX Fill 架构:
─────────────────────────────────────────
输入处理:
x_m: [B, 3, H, W] (masked image)
M: [B, 1, H, W] (mask)
可能不拼接,而是分别编码:
z_m = VAE(x_m): [B, 16, h, w]
z_M = Downsample(M): [B, 1, h, w]
然后在潜空间融合:
z_input = concat[z_m, z_M] → [B, 17, h, w]
或者: z_input = z_m ⊙ (1-z_M) + learned_embedding(z_M)
Transformer 修改:
可能在 attention 中增加 mask-aware 机制
如: 让模型明确知道哪些 token 对应 mask 区域
─────────────────────────────────────────
总结
| 问题 | 答案 |
|---|---|
| 掩码法是图生图特例吗? | 不是。图生图是软初始化,掩码法是硬约束 |
| 为什么 concat[x_m, M]? | x m x_m xm 提供上下文, M M M 标记目标区域,两者互补 |
| 混在一起怎么区分? | 通道维度区分,卷积天然学习通道语义,无需位置编码 |
| c 如何引导原图生成? | 通过残差注入到 Transformer 特征,与文本的 cross-attention 机制不同 |
| 和文本引导一样吗? | 不一样。文本是 cross-attention 查询-检索,ControlNet 是直接特征偏移 |
你的思考非常深入,特别是关于"信息如何区分"和"如何引导"的问题,触及了条件扩散模型的核心设计哲学。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)