這條路和現在主流圖片生成模型的核心思想是相通的:訓練時把圖片逐步加噪,讓神經網路學會預測噪聲;生成時從純噪聲開始,一步一步把噪聲去掉。 Hugging Face 現在的官方訓練範例也是用 UNet + DDPM scheduler 做這件事。
一、我們先做一個「真的能跑」的最小模型
我建議第一版不要做:
❌ Stable Diffusion
❌ Transformer
❌ CLIP
❌ Text Encoder
❌ VAE
❌ LoRA
❌ GPU 多卡
❌ 512×512
而是做:
32×32 RGB 圖片 → DDPM → 生成 32×32 圖片
例如我們準備:
dataset/ 001.jpg 002.jpg 003.jpg ...
全部是「貓」:
🐱 🐱 🐱 🐱 🐱
訓練完成後:
隨機噪聲 ↓ ↓ 去噪 ↓ ↓ ↓ 一隻新的貓
注意,它不是把某一張圖片「記住後複製」。
它學的是:
「我的訓練資料中的圖片,大概符合什麼樣的分布?」
這正是 unconditional image generation 的基本概念。
二、整個模型其實只有這幾個東西
可以把它理解成:
訓練 │ ▼ 真實圖片 x₀ │ │ 加噪 ▼ x₁ → x₂ → ... → xₜ │ ▼ 神經網路 UNet(xₜ,t) │ ▼ 預測噪聲 ε │ ▼ MSE Loss │ ▼ 更新模型
生成的時候反過來:
純噪聲 xₜ │ ▼ UNet 預測噪聲 │ ▼ 去掉一點噪聲 │ ▼ xₜ₋₁ │ ▼ UNet │ ▼ xₜ₋₂ │ ▼ ... │ ▼ x₀ │ ▼ 圖片
這就是 Diffusion 最重要的原理。
三、第一個版本甚至可以不用 Hugging Face
我建議你第一遍純 PyTorch 寫。
這樣你才能真正看到:
noise = torch.randn_like(images)
以及:
noisy_images = ...
和:
predicted_noise = model(noisy_images, t)
究竟在幹什麼。
四、安裝環境
Windows + NVIDIA GPU 的話:
pip install torch torchvision pillow tqdm
確認:
import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU")
如果:
True NVIDIA GeForce RTX ...
就可以直接用 GPU。
五、第一個模型:極簡 UNet
先建立:
mini_diffusion/ │ ├── dataset/ │ ├── 001.jpg │ ├── 002.jpg │ └── ... │ ├── model.py ├── train.py └── generate.py
model.py:
import torch import torch.nn as nn class SimpleUNet(nn.Module): def __init__(self): super().__init__() self.down1 = nn.Sequential( nn.Conv2d(3, 64, 3, padding=1), nn.ReLU(), nn.Conv2d(64, 64, 3, padding=1), nn.ReLU(), ) self.down2 = nn.Sequential( nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.Conv2d(128, 128, 3, padding=1), nn.ReLU(), ) self.up1 = nn.Sequential( nn.Conv2d(128, 64, 3, padding=1), nn.ReLU(), nn.Conv2d(64, 64, 3, padding=1), nn.ReLU(), ) self.out = nn.Conv2d(64, 3, 1) self.pool = nn.MaxPool2d(2) def forward(self, x): x1 = self.down1(x) x2 = self.pool(x1) x2 = self.down2(x2) x2 = nn.functional.interpolate( x2, size=x1.shape[-2:], mode="nearest" ) x = self.up1(x2) x = x + x1 return self.out(x)
這還不是嚴格意義上的完整 UNet,故意簡化。
我們的目標不是先做最強模型,而是理解原理。
六、最重要的部分:加噪聲
這是整個 Diffusion 最核心的地方。
假設:
原圖: 🐱
我們逐步加噪:
x0 🐱 ↓ 🐱 + 一點噪聲 ↓ ░🐱░ ↓ ░░▓░ ↓ ▒░▒░ ↓ ▓▒▓▒ ↓ 純噪聲
數學上:
xₜ = √ᾱₜ x₀ + √(1-ᾱₜ) ε
其中:
x₀ 原始圖片 ε 隨機噪聲 t 第幾步 ᾱₜ 控制噪聲比例 xₜ 加噪後的圖片
這一步甚至不需要神經網路。
七、寫成 Python
例如:
import torch def add_noise(x, t, alpha_bar): noise = torch.randn_like(x) a = alpha_bar[t] a = a.view(-1, 1, 1, 1) noisy_x = ( torch.sqrt(a) * x + torch.sqrt(1 - a) * noise ) return noisy_x, noise
這裡非常重要:
noise = torch.randn_like(x)
假設:
x = 一張貓
我們隨機生成:
noise = 隨機雜訊
然後:
noisy_x = ...
得到:
貓 + 雜訊
八、真正的訓練邏輯
這是你最值得研究的地方。
for images in dataloader: images = images.to(device) # 隨機選一個時間 t = torch.randint( 0, T, (images.shape[0],), device=device ) # 加噪 noisy_images, noise = add_noise( images, t, alpha_bar ) # 模型預測噪聲 predicted_noise = model( noisy_images ) # 比較真正的噪聲和模型預測的噪聲 loss = torch.mean( (predicted_noise - noise) ** 2 ) optimizer.zero_grad() loss.backward() optimizer.step()
你可以把這段理解成:
我給你一張被污染的圖片,你猜我剛才加進去的是什麼噪聲。
九、模型根本不知道「貓」是什麼
例如:
原圖: 🐱
我們隨機加噪:
🐱 ↓ ░🐱░ ↓ ░▒▓░ ↓ ▓▓▒▓
模型收到:
▓▓▒▓
它不知道:
這是貓
它只需要學:
這張圖片裡面 哪些東西比較像 noise?
因此:
model(noisy_image)
輸出的不是:
cat
而是一張:
predicted_noise
這一點非常重要。
十、Loss 就簡單到令人意外
最基本版本:
loss = ((predicted_noise - noise) ** 2).mean()
也就是:
真實噪聲 ↓ ε 模型預測 ↓ ε' Loss = (ε - ε')²
模型不斷調整參數:
ε' 越來越接近 ε
最後:
loss 1.2 0.8 0.5 0.3 0.18 0.12 0.08 ...
十一、然後最神奇的事情發生了
訓練的時候我們是:
圖片 ↓ 加噪 ↓ 模型學習預測噪聲
但是生成的時候:
純噪聲 ↓ 模型 ↓ 去掉噪聲 ↓ 模型 ↓ 去掉噪聲 ↓ 模型 ↓ ... ↓ 圖片
所以:
訓練
圖片 → 噪聲
生成
噪聲 → 圖片
這就是 Diffusion 最核心的「正向過程 + 反向過程」。
十二、生成程式
最簡化可以理解成:
@torch.no_grad() def generate(model, shape, T): x = torch.randn(shape).to(device) for t in reversed(range(T)): predicted_noise = model(x) x = x - predicted_noise * 0.1 return x
當然,這只是教學版概念,不是正確 DDPM sampling 方程。
真正的 DDPM 要按照 β、α、ᾱ 和 posterior variance 來計算:
xₜ → xₜ₋₁
這個部分我們第二步再完整實現。
十三、為什麼我建議你從 32×32 開始?
因為圖片大小對計算量影響非常大。
例如:
32 × 32 = 1,024 pixels 64 × 64 = 4,096 pixels 256 × 256 = 65,536 pixels 512 × 512 = 262,144 pixels
512×512 是 32×32 的:
256 倍 pixels
而且真正的 Stable Diffusion 也不是直接在 512×512 RGB pixel 上做全部運算,而是先經過 VAE 壓縮到 latent space。
所以我們第一階段:
32×32 RGB
非常合理。
十四、然後逐步升級
我建議你按照這個順序學。
Level 1:最小 Diffusion
PyTorch ↓ 32×32 ↓ 簡單 UNet ↓ DDPM ↓ 生成圖片
目的:
理解 Diffusion。
Level 2:真正的 UNet
加入:
Downsample Residual Block Upsample Skip Connection Time Embedding
變成:
┌───────────────┐ │ │ 圖片 → Down → Down → Bottleneck → Up → Up → 圖片 │ ↑ └──── Skip Connection ────┘
這時候你會真正理解 Stable Diffusion 的 UNet。
Level 3:加入時間 t
這個非常重要。
因為:
t = 10
和:
t = 900
代表圖片處於完全不同的噪聲程度。
所以模型必須知道:
現在是第幾步?
因此:
model(x, t)
而不是:
model(x)
會加入:
Sinusoidal Time Embedding
這就是 Transformer 裡也非常常見的一類 positional encoding 思想。
十五、Level 4:加入文字
到這一步,你就開始接近 Stable Diffusion 了:
Text │ ▼ Text Encoder │ ▼ "一隻在海邊的貓" │ ▼ UNet ▲ │ noisy image
也就是:
Image + Text ↓ UNet ↓ Noise prediction
這就是:
Text-to-Image
十六、Level 5:VAE
接下來再理解:
圖片 ↓ VAE Encoder ↓ Latent ↓ Diffusion ↓ Latent ↓ VAE Decoder ↓ 圖片
例如:
512×512×3
不直接進 Diffusion。
而是:
512×512×3 ↓ VAE ↓ 64×64×4 latent ↓ Diffusion
這就是 Stable Diffusion 為什麼可以把計算量大幅降低。
十七、Level 6:你才開始碰 LoRA
到了這裡你就會明白 LoRA 到底在幹什麼。
不是:
重新訓練一個 Stable Diffusion
而是:
原始模型 + 很少量可訓練參數 ↓ LoRA
所以:
Stable Diffusion │ ├── 原本參數:凍結 │ └── LoRA:訓練
這也是為什麼 LoRA 可以用相對低的硬體成本做風格、人物、產品等微調。
十八、最終你會看到完整架構
等你把前面全部串起來:
Prompt │ ▼ Text Encoder │ ▼ Text Embedding │ ▼ Random Noise → UNet ← Time Embedding │ ▼ Denoising │ ▼ Latent │ ▼ VAE Decoder │ ▼ 512×512 Image
這時候你再去看:
Stable Diffusion
SDXL
FLUX
Imagen
各種 DiT
LoRA
ControlNet
就不會再覺得它們是「一個神秘的 AI 黑盒子」。
十九、你甚至可以用自己的攝影素材來做實驗
這對你其實非常合適。
例如你有大量旅遊攝影:
photos/ ├── hongkong/ ├── shenzhen/ ├── guangzhou/ ├── macau/ ├── taiwan/ └── ...
第一個實驗甚至可以:
只拿 1,000 張香港照片
訓練:
Hong Kong Diffusion
生成:
random noise ↓ ... ↓ 一張「像你的香港攝影資料分布」的新圖片
但這時候還沒有:
「香港夜景」 「香港維港」 「香港街頭」
這些文字控制。
那是下一階段:
Image + Caption
二十、我建議我們真的動手做
如果你願意,我可以直接帶你做一個完整可執行的「迷你 Stable Diffusion 教學專案」,不是只講原理。
我會分成大約 6 個版本:
V1 最小 DDPM 32×32 ↓ V2 真正 UNet ↓ V3 Time Embedding ↓ V4 Text Conditioning ↓ V5 VAE + Latent Diffusion ↓ V6 LoRA 微調
每一版都給你:
完整 Python 代碼 ↓ 訓練資料準備 ↓ 訓練命令 ↓ 生成命令 ↓ 每一段代碼解釋 ↓ 觀察 loss / 中間圖片 ↓ 下一版到底增加了什麼
而且第一版我可以直接寫成一個 Windows + NVIDIA GPU 可以執行的完整專案,大概只有 train.py + model.py + generate.py 三個文件,讓你今天就能跑起第一個「自己訓練出來的圖片生成模型」。
這比直接拿 Hugging Face 的 train_unconditional.py 跑更適合你理解原理;官方腳本本身已經把 UNet、scheduler、dataset、checkpoint 等大量細節封裝起來了。
最后
以上就是热心网友最近收集整理的关于学习PyTorch 写一个极简 DDPM(Diffusion Model),理解图片生成模型到底怎么训练和生成的全部内容,更多相关学习PyTorch内容请搜索靠谱客的其他文章。
发表评论 取消回复