L4.5 同一台機器要依指令做不同的事,指令從哪裡餵進去?
本篇重用M0.2導航的三十秒:Taylor 展開與「假設直線」·M1.1從鞋子猜身高:Conditional Expectation
起點:同一台機器,不同的指令
到目前為止每一個模型都只做一件事:給 ,回答 。
但實務上更常見的是一台可以被指揮的機器:同一個模型,依照一個額外的指令做不同的事。
- 「把這張圖修成夜景」——條件是一個類別。
- 「照這段文字生成」——條件是一串 token。
- 「現在是流程的第 300 步,請做這一步該做的事」——條件是一個純量。
最後一種特別值得注意,因為它不是使用者給的指令,而是演算法自己的狀態。同一組權重要在很多個不同的階段各做一件不同的事,於是「現在是第幾步」必須被餵進去。
接法看起來很自由:接在輸入、接在中間某一層、或讓網路自己去查。但這幾種接法不是同一件事的幾種寫法——它們對不同形狀的條件有非常不同的成本。
指令要從哪裡餵進去?
接在輸入的一個維度上就好了嗎?
如果那個指令是一個純量,會不會特別吃虧?
課堂提問Q1
把「依指令做不同的事」翻成數學:條件模型和無條件模型差在哪裡?把一個純量條件接成輸入的第二個維度,網路必須自己長出什麼東西——那件事對它來說容易嗎?
先想一想,再展開看整理後的答案
寫下來。 無條件模型學的是一個函數 ;條件模型學的是一族函數,用 索引:
用 從鞋子猜身高:Conditional Expectation 的語言:多了一個條件,桶子被切得更細了。理想上這讓每個桶子裡的散佈更小(預測更準);代價是同一組參數要同時服務所有的 。
接成輸入維度之後,網路要自己長出什麼。 若 對 的依賴是高頻的——例如流程的第 3 步和第 4 步該做的事差很多——那麼把 當成一個普通的輸入座標,就是要求網路在那個座標上表現出高頻的變化。
而 什麼都能擬合,那為什麼還需要別的架構? 的頻率實驗量過這件事:同一個 的網路,目標從 1 個週期換到 32 個週期,訓練 MSE 從 變成 。MLP 對高頻很不友善,而這個不友善不分輸入座標—— 這一維吃虧, 這一維一樣吃虧。
兩條出路,方向不同。
- 把頻率直接給它:不要接 ,接 這一組不同頻率的特徵。網路要高頻時,它已經在輸入裡了,只需要線性組合。
- 不要讓 走同一條路:讓它去改變每一層的行為(尺度與偏移),而不是當成一個和 平起平坐的座標。
本篇會把第一條量出來(嵌入比加寬便宜也有效),並說清楚第二條在什麼形狀的條件上才划算。
三種注入方式,三種「改變什麼」
把接法按照「它改變了網路的哪一部分」排開:
- concat(接在輸入):。最簡單,而且在條件和輸入是同一種東西時很自然(例如都是影像的像素)。缺點是條件的影響必須一路經過所有層才擴散開,而且它和 共用同一條路。
- FiLM(每一層的尺度與偏移):由 算出一組 ,在第 層做 。條件不再是一個座標,而是直接調整每一層的行為。這對「全域性、影響整張圖」的條件特別合適——類別、時間步、風格。
- cross-attention(查表):條件是一串長度可變的東西(一段文字、一組 token),讓每個位置自己去問「我該看這串裡的哪幾個」。它處理的是 concat 與 FiLM 都處理不了的形狀:條件的長度不固定,而且不同位置需要看不同的部分。
三者不互斥,實務上常常同時出現(時間用 FiLM、文字用 cross-attention、低階的輔助輸入用 concat)。挑的依據是條件的形狀:
| 條件長什麼樣 | 適合的注入方式 |
|---|---|
| 一個純量(時間、強度) | 嵌入 → FiLM(或 concat 嵌入) |
| 一個類別(有限選項) | 查一個 embedding 表 → FiLM |
| 一張同尺寸的圖(遮罩、邊緣圖) | concat 在通道上 |
| 一串長度可變的 token | cross-attention |
為什麼純量條件要先做嵌入
正弦嵌入把一個純量 換成一組不同頻率的三角函數:
它在做的事可以用一句話說清楚:把「網路要自己長出高頻」換成「高頻已經在輸入裡,只需要把它們線性組合起來」。
為什麼這會有效,可以從 導航的三十秒:Taylor 展開與「假設直線」 的角度看。一個網路對輸入的局部行為,是由它在那一點的一階(與高階)變化決定的;要在 上做出 個週期,第一層的權重必須大到 ,而那會同時把激發函數推進飽和區(同一張地圖從不同地方出發:損失曲面與初始化 的 gain 掃描量過:太大的尺度讓訓練變差且不穩)。嵌入把這個張力拆開——頻率由固定的、不需要學的 提供,網路只負責挑哪幾個、各要多少。
這個幾何級數也不是隨便挑的:它讓不同尺度的變化都被覆蓋到,而且只要 個維度就涵蓋 倍的頻率範圍。
補充這和 Transformer 的位置編碼是同一個東西
Transformer 的位置編碼用的是同一條式子( 配上幾何級數的頻率),解決的也是同一個問題:位置是一個純量,而網路需要對它有多尺度的敏感度。差別只在那裡的純量是「第幾個 token」,這裡是「第幾步」或「條件的強度」。
同一個技巧在座標型網路(用網路表示一張圖或一個 3D 場景)上叫 Fourier features(參考文獻 2),那篇論文把「為什麼沒有嵌入就學不到高頻」用 neural tangent kernel 講清楚了:沒有嵌入時,網路的等效核對高頻成分的響應以指數衰減,於是那些成分在任何合理的訓練時間內都學不到。
三個名字、三個領域、同一件事——值得把它記成一個通用的手法:當一個純量需要高頻的影響力時,先把它展開成多個頻率。
回到情境:三種接法在同一個任務上
任務刻意設計成「對條件的依賴是高頻的」:
對 的依賴是 1 個週期(很容易),對 的依賴是 8 個週期(照 什麼都能擬合,那為什麼還需要別的架構? 的頻率表,這是要花點力氣的區域)。三個設定跑同樣的 6000 步、同樣的 Adam :
concat 純量 t 輸入維度 2 h= 64 訓練 MSE 0.0018
concat 正弦嵌入 t 輸入維度 13 h= 64 訓練 MSE 0.0004
concat 純量 t(寬兩倍) 輸入維度 2 h=128 訓練 MSE 0.0010
嵌入把誤差降到四分之一(),而它只多了 11 個輸入維度。 對照組更有說服力:把網路加寬兩倍(參數變成約四倍)只從 改善到 ——四倍的參數買到的,還不如 11 個不需要學的輸入維度。
嵌入不是在增加容量,是在把網路不擅長的那件事移出去。
這也解釋了為什麼幾乎每一個需要「時間步」的模型都有一個正弦嵌入層,而且那一層通常沒有參數:它不是模型的一部分,是輸入的一種座標選擇——和 霧中下山,一步該走多大:梯度下降的一頁 裡「標準化改的是碗的形狀」是同一類的動作。
回到情境:「沒有條件」也必須被訓練過
條件模型有一個很容易被忽略的需求:有時候你要它不要依照條件做事。
- 想知道模型在「什麼都不指定」時的預設行為。
- 想比較「有條件」與「無條件」的輸出差在哪裡(很多引導式的取樣做法都需要這兩個)。
- 使用者就是沒有給條件。
直覺的做法是留一個 null token:一個代表「沒有條件」的特殊輸入(例如把 one-hot 全部設成零)。問題是——如果訓練時條件永遠都在,那個 null token 從來沒有出現在訓練資料裡。
把它量出來。任務是 ,,條件用 one-hot 接進輸入;「沒有條件」就是把 one-hot 設成全零。真正的無條件答案是兩類的平均 。訓練時用機率 把條件隨機丟掉(換成全零):
def feats(x, c, mask): # mask=0 表示這一筆的條件被丟掉
oh = np.zeros((len(x), 2)); oh[np.arange(len(x)), c] = 1.0
return np.c_[x, oh*mask[:,None]]
mask = (rng.random(N) > p).astype(float) # p 是條件 dropout 的機率
條件 dropout p=0.0 用 null token 預測「兩類平均」的 MSE 中位數 1.3908
條件 dropout p=0.1 用 null token 預測「兩類平均」的 MSE 中位數 0.0660
條件 dropout p=0.3 用 null token 預測「兩類平均」的 MSE 中位數 0.0225
(對照:直接輸出 0 的 MSE = 0.6575)
那一列比「什麼都不算、直接輸出零」還差兩倍多( 對 )。這不是「精度差一點」——是模型在一個從未見過的輸入上任意外推,而外推的方向沒有任何理由是對的(這正是 台北的房仲去台中:風險與經驗風險 那個 的小型版本:訓練分佈之外沒有定義)。
只要 的條件 dropout,誤差就掉到 ——二十一倍。 再降到 ,但代價是三成的訓練樣本沒有用到條件資訊,條件本身的品質會開始下降。實務上 左右是常見的折衷。
「沒有條件」不是一個預設狀態,它是一個要被訓練的輸入。
刻意違反:把 null token 換成一個「沒用過的」向量。 如果不是用全零,而是隨便挑一個訓練時沒出現過的向量(例如 one-hot 的第三格),結果和 那一列一樣糟——問題不在「用哪個向量代表沒有條件」,而在「那個向量有沒有被訓練過」。 這也是為什麼實務上一律用「訓練時真的餵過的那個」當 null,而不是任何看起來比較中性的東西。
這一切依賴什麼。 一,上面的數字是在一個純量/類別條件、小網路上量的;條件是長序列時 dropout 的做法不同(丟整串、還是丟其中幾個 token,會影響模型學到的東西)。二, 是一個要調的超參數,而它同時影響「無條件有多準」與「有條件有多準」——兩者用同一組權重。三,這裡量的是「無條件輸出對不對」,不是「有條件輸出有沒有變差」;完整的評估要兩個都看。
三種接法、一個頻率旋鈕、一個 dropout 旋鈕
互動 demo:把條件頻率 k 拉大,「純量」那條訓練曲線整條抬高而「嵌入」那條幾乎不動。
先消化一下
參考文獻
- Perez, E. et al. FiLM: Visual Reasoning with a General Conditioning Layer. AAAI 2018.(把「條件改變每一層的尺度與偏移」抽象成一個通用層,並比較它與 concat 的差別。)
- Tancik, M. et al. Fourier Features Let Networks Learn High Frequency Functions in Low Dimensional Domains. NeurIPS 2020.(用 NTK 說明沒有嵌入時高頻為什麼學不到——本篇 Remark 的來源。)
- Ho, J., Salimans, T. Classifier-Free Diffusion Guidance. 2022.(條件 dropout 與 null token 的標準做法;本篇最後一段實驗的實務對應。)