本文依據 CMU 10-423/623/723 Generative AI Spring 2026 版。 這是 CMU 10-423 導讀系列第 9 篇,也是影像單元的收尾。前面四篇講了 CNN 與 ViT、GAN、擴散模型 與 變分推論和 VAE,這一篇看作業怎麼把它們全部考一遍,再要你親手寫出一個會畫貓的 DDPM。
用到的官方材料:Coursework 頁上的 hw2.zip(32 MB,內含 27 頁的 hw2.pdf、起始碼、資料與 LaTeX 模板)、HW2 Overleaf 唯讀模板、2 月 13 日的 HW2 recitation 投影片(Google Drive 上的 PowerPoint,公開),以及課綱的作業規則。事實皆於 2026-09-30 打開官方材料核對。本文只寫題目結構與設定,不附任何解答。
基本資料
| 項目 | 內容 |
|---|---|
| 名稱 | Homework 2: Generative Models of Images(Coursework 頁標為「Image Generation」) |
| 範圍 | L5–L8(講次表在 2 月 9 日寫「HW2 out (L5-L8)」) |
| 發下 | 2026-02-09 |
| 截止 | 2026-02-21 晚上 11:59(Slot A,L10 投影片的 Reminders) |
| Slot B | 講次表暫定 3 月 1 日;回饋暫定 2 月 26 日 |
| 繳交 | Gradescope:書面 PDF 一份、程式只交 diffusion.py |
| 總分 | 60 |
配分表(hw2.pdf 第 2 頁):
| 大題 | 分數 |
|---|---|
| LaTeX Template Alignment | 0 |
| Convolutional Neural Networks | 8 |
| Encoder-only Transformers | 4 |
| Generative Adversarial Network (GAN) | 5 |
| Variational Autoencoders | 6 |
| Understanding Diffusion Models | 14 |
| Programming: Diffusion Models | 21 |
| Code Upload | 0 |
| Collaboration Questions | 2 |
書面答案要照模板作答,沒對齊模板會被扣該份作業 2% 的分數,hw2.pdf 給的理由是批改有用到 AI 輔助的評分器。課綱的 Slot A/B 規則也適用:Slot A 只能交純人工作答,拿到回饋後三天內可以在 Slot B 用 AI 輔助修正答錯的題目,每題取兩次較高分。完整規則寫在系列總覽。
書面題在考什麼
五個書面大題幾乎一講一題,順序跟 L5–L8 一致。
CNN(8 分)。 先給一個單通道輸出的卷積定義,要你算出左右上下各需要幾行、幾列「幻覺像素」(hallucinated pixels),只能用 floor 函式表示;再把公式改寫成先補零、只索引合法位置的版本。最後兩小題是 U-Net 的觀念題:skip connection 的作用,以及特徵圖解析度在 encoder 與 decoder 裡怎麼變。這兩題是為程式題鋪路,因為 DDPM 的去噪網路就是 U-Net。
Encoder-only Transformer(4 分)。 畫出 decoder-only Transformer 所定義序列分佈的有向圖模型,再比較 encoder-only 模型接上逐字詞性分類頭之後,能表達哪一類序列分佈、各自適合什麼應用。對應 L5 的 BERT 段落。
GAN(5 分)。 情境是題目裡的角色 Lora the Llama 想替灰階的羊駝照片做 inpainting。題目給一段訓練虛擬碼,要你補上三個空格:模型輸出怎麼只替換被遮住的像素、只看遮罩區域的平方誤差,以及 GAN 形式的目標函數;最後問只用平方誤差訓練有什麼缺點。
VAE(6 分)。 第一小題給一個一維的線性高斯 encoder/decoder,問「把取樣當黑盒子」與「用 reparameterization trick」兩種計算圖下,哪些參數收得到重建項的梯度。第二小題把 VAE 用在影片上,要你寫出一個 batch 的完整損失(MSE 重建項加上乘了 β 的 KL 項)。第三小題是 β-VAE 的單選:β 從 0 慢慢調大時,潛在表示與重建品質怎麼變。
Understanding Diffusion Models(14 分)。 全份作業最重的書面題,分兩段:
- ELBO Surgery(6.1–6.2):6.1 占 5 分,要你從 ELBO 出發,證明它可以拆成重建項、一串 L_t 項與一個跟 θ 無關的常數。題目附了六條提示,還加註「證明很可怕」,期待的答案以方程式為主。6.2 問 L_t 對反向過程的作用,以及它何時最大。
- Image Diffusion(6.3–6.5):給定每一步的前向分佈,先描述怎麼一步步取樣到第 τ 步、時間複雜度是多少;再用 reparameterization trick 推出可以直接從 x₀ 跳到 x_t 的封閉形式;最後說明用這個形式取樣的做法與複雜度。
6.4 推出來的那條式子就是程式題 q_sample 要實作的東西,所以建議先寫完第 6 題再動程式。ELBO 的推導背景在第 8 篇。
程式題:從零實作 DDPM
資料
hw2.pdf 寫的資料集是 Animal Faces-HQ(AFHQ),「15,000 張 36×36 的圖片」,分貓、狗、野生動物三類,作業只用貓圖以降低計算量;handout 也事先說明,因為只用了原資料集的子集,生成品質可能不如預期。我實際解開 zip 數了一次:data/train/ 底下 cat 5,153 張、dog 4,739 張、wild 4,738 張,data/val/ 三類各 500 張,每張確實是 36×36。模型預設的 image_size 是 32。
資料就在 zip 裡,不用另外下載。Kaggle 版 notebook 另外提供一個已上傳的 Kaggle 資料集,2026-09-30 仍可打開。
檔案與要改的地方
| 檔案 | 作用 |
|---|---|
diffusion.py | Diffusion 類別:前向過程、反向過程與排程,要改 |
unet.py | 去噪用的 U-Net,要改(只有 forward) |
trainer.py | 訓練迴圈、取樣、FID、W&B 紀錄 |
utils.py | train_diffusion、visualize_diffusion 兩個入口與 W&B 初始化 |
main.py | 在本機或 AWS 上跑的命令列入口 |
run_in_colab.ipynb/run_in_kaggle.ipynb | 雲端 notebook |
test_diffusion.py、data.pt | 單元測試與測試用的固定張量 |
requirements.txt | 依賴清單 |
hw2.pdf 說只需要改 diffusion.py 和 unet.py,要改的地方都標了 TODO。實際打開檔案:
diffusion.py有 7 個 TODO:__init__裡的預先計算係數,加上q_sample、p_losses、forward、p_sample、p_sample_loop、sample六個函式。recitation 投影片把它們分成訓練(forward、p_losses、q_sample)與取樣(sample、p_sample_loop、p_sample)兩組。- 噪音排程已經寫好:
cosine_schedule實作 Nichol & Dhariwal(2021)的 cosine 排程,s = 0.008。 unet.py的 TODO 集中在Unet.forward:下採樣路徑每一層跑兩個殘差區塊與 attention、存下 skip 特徵,接 bottleneck,再在上採樣路徑把 skip 特徵串接回來。網路的各個元件(ResnetBlock、LinearAttention、SinusoidalPosEmb等)都已經給好。
hw2.pdf 規定的訓練細節:p_losses 用 L1 損失比較真實雜訊與預測雜訊;取樣演算法會先由預測雜訊估出 x̂₀、把它 clamp 到 [−1, 1],再用後驗平均與變異數往前走一步。trainer.py 用 Adam。
有一處要注意:Code Upload 題與測試檔都寫明只上傳 diffusion.py,但 unet.py 也有 TODO,第 8 個測試也在測 U-Net 的 forward。Gradescope 實際怎麼處理 unet.py,校外看不到。
預設超參數
| 參數 | 預設值 |
|---|---|
time_steps(T) | 50 |
batch_size | 32 |
image_size | 32 |
unet_dim | 16 |
unet_dim_mults | [1, 2, 4, 8] |
learning_rate | 1e-3 |
data_class | cat |
dataloader_workers | 16 |
hw2.pdf 說表 4 的這些值在本作業中不需要改;實驗之間要調的只有 train_steps、save_and_sample_every 和 fid。
單元測試
test_diffusion.py 有 8 個測試,全部強制在 CPU 上跑(註解說 GPU 可能產生些微不同的輸出)。T01(檢查上傳檔案)與 T02(noise_like)權重為 0,T03–T08 各 1 分,分別測 p_sample、p_losses、p_sample_loop、sample、q_sample 與 U-Net forward。
我在 2026-09-30 用 macOS、Python 3.12、PyTorch 2.14.0(CPU)跑未修改的起始碼:8 個測試約 5 秒跑完,T01、T02 通過,其餘 4 個 FAIL、2 個 ERROR,這是 TODO 還沒填的預期結果。要注意 diffusion.py 開頭就 import wandb,所以連跑測試都要先裝 wandb。我只裝了 torch、einops、tqdm、wandb,沒有照 requirements.txt 的版本釘選。
五個實驗題
程式題的 21 分全部來自實驗結果,圖都從 W&B 取:
| 題號 | 內容 | 建議參數 | 官方估計(Colab T4) | 分數 |
|---|---|---|---|---|
| 7.1 | 1,000 步的訓練損失曲線;此時應該生成出模糊的貓 | train_steps=1000、save_and_sample_every=100、fid=False | 5–10 分鐘 | 4 |
| 7.2 | 每 100 步算一次 FID,畫 1,000 步內的 FID 曲線 | 同上,fid=True | 15–60 分鐘 | 4 |
| 7.3 | 完整訓練 10,000 步,貼出最後一批生成圖 | train_steps=10000、save_and_sample_every=1000、fid=False | 約 2 小時 | 5 |
| 7.4 | 用 10,000 步的模型畫前向過程:0%、25%、50%、75%、99% | 呼叫 visualize_diffusion | — | 4 |
| 7.5 | 把前向過程最後一步的雜訊餵回去,畫反向過程的同樣五個時間點 | 同上 | — | 4 |
FID 用 clean-fid 的 compute_fid 計算。hw2.pdf 的描述是和「縮放過的訓練圖」比較;trainer.py 的寫法是把 data_path 裡的 train 換成 val 當參考資料夾,只有那個資料夾是空的時候才把訓練集的貓圖縮放後放進去。zip 已經附了非空的 data/val/,照預設路徑跑時實際比對的是哪一組圖,我沒有跑 FID 驗證。
環境與算力:今天還跑得動嗎
hw2.pdf 列了三種跑法:本機或 AWS 用 main.py,雲端用 Colab 或 Kaggle 的 notebook,開 GPU 的詳細步驟則要回去看 HW1 的 handout。兩份 notebook 的用法相同:先裝 requirements.txt,把整份 diffusion.py 貼進指定的 cell,填好 train_steps、save_and_sample_every、fid 三個參數,再呼叫 train_diffusion 與 visualize_diffusion。
算力要抓寬一點:
- recitation 投影片說這是整門課訓練時間最長的一份作業,要跑的實驗在 Colab T4 上合計 2–3 小時,而且除錯時多半會重跑。它的建議是提早開始、先用少量步數測試程式。
- 同一份投影片有一頁寫「Colab Pro is FREE for students」。這是對修課學生說的,校外讀者能不能適用,我沒有查證。
requirements.txt要求 Python 3.9 以上、3.13 以下,並釘選 clean-fid 0.1.35、einops 0.8.1、pillow 11.3.0、tqdm 4.67.1、wandb 0.21.3;torch 與 torchvision 沒有釘版本。utils.py在訓練與視覺化開始前都會呼叫wandb.login(),所以需要一個 Weights & Biases 帳號。紀錄會寫到名為DDPM_AFHQ的 project。
跟 HW1 相比,HW2 一樣不需要下載外部權重,資料也在 zip 裡;多出來的門檻是 W&B 帳號和較長的 GPU 時間。
Recitation 投影片可以補什麼
2 月 13 日的 recitation 投影片共 75 頁,分三段:
- 起始碼導覽:逐一說明每個檔案的用途,把實作拆成四塊:U-Net 的
forward、噪音排程(已完成)、訓練演算法、取樣演算法。後兩塊就是diffusion.py的六個函式。 - 書面題複習:用「會加噪也會去噪」的比喻講擴散模型,強調「去噪不是還原圖片,而是生成圖片」;介紹 FID 怎麼用 Inception-v3 抽特徵、分數越低越接近目標分佈;ELBO 則用「要寫得準確、又要衝點閱率的犯罪現場記者」比喻重建誤差與 KL 項,再用 Jensen 不等式一步步推導,最後講 reparameterization trick。
- 好用的函式:為什麼要用
noise_like而不是直接呼叫torch.randn(投影片的答案是讓每份繳交的隨機性一致)、gather_timestep_coeff怎麼從預先算好的係數表取出需要的時間步,以及torch.cumprod、torch.clamp、torch.full的小測驗。torch.clamp那題特地示範把 numpy 陣列傳進去會丟TypeError。
這份投影片放在 Google Drive 上,是 PowerPoint 檔而不是原生的 Google Slides,2026-09-30 可以直接匯出下載。
這份作業跟考試的關係
講次表把「Programming Test HW1/HW2」排在 2 月 25 日課堂上,範圍是 HW1 與 HW2 的程式部分,閉書。這場測驗校外拿不到,但它說明了課程期待你真的理解自己寫的 q_sample 與 p_sample,而不只是讓測試通過。練習考卷裡對應 HW2 範圍的是 ViT(14 分)、GAN(9 分)、VAE(8 分)與 Diffusion Models(8 分)四題。
怎麼做:今晚下載 hw2.zip,裝好 torch、einops、tqdm、wandb,跑一次 python test_diffusion.py,確認看到 T01、T02 通過、其餘失敗。接著先寫書面題 6.4,把 q(x_t | x₀) 的形式推出來,再打開 diffusion.py 填 __init__ 和 q_sample,讓 T07 變綠。
這一篇可以確認與不能確認的
可以確認:hw2.zip 裡的 PDF、起始碼、資料與測試,recitation 投影片,課綱的 Slot A/B 規則,講次表的日期,以及本機跑單元測試的結果。不能確認:Gradescope 的自動評分與人工批改標準、unet.py 在 Gradescope 上怎麼被評分、講次表暫定的 Slot B 日期(3 月 1 日)當學期是否照表執行、FID 實際使用的參考圖、在 Colab T4 上的實際訓練時間(我沒有在 GPU 上跑),以及官方解答。
延伸閱讀:想從 ODE/SDE 的角度重新理解擴散模型與 flow matching,可以看 MIT 6.S184 導讀;它的 Lab 3 也有另一份從零訓練生成模型的作業可以對照。
系列導覽:上一篇 L8–L9:變分推論、VAE 與擴散模型的 ELBO|下一篇 L10–L11:參數高效微調與 in-context learning|系列總覽
參考資料
- CMU 10-423/623/723 Generative AI(Spring 2026)課程首頁與課綱——Slot A/B 規則
- Coursework 頁
- HW2 handout(hw2.zip)——hw2.pdf、起始碼、AFHQ 子集、單元測試
- HW2 Overleaf 唯讀模板
- HW2 Recitation 投影片(2026-02-13)
- 課程講次表——發下、截止、回饋與程式測驗日期
- Lecture 10 投影片——Reminders 頁的 Slot A 截止時間
- Practice Exam(Spring 2026)
- HW2 Kaggle 資料集(zimingy1/hw2-data)
- Ho, Jain & Abbeel 2020:Denoising Diffusion Probabilistic Models
- Nichol & Dhariwal 2021:Improved Denoising Diffusion Probabilistic Models——cosine 噪音排程
- Choi et al. 2020:StarGAN v2——AFHQ 資料集出處
- GaParmar/clean-fid
- Weights & Biases
Loading...