Autoregressive Models:一次預測一個變數
第一個真正的深度生成模型家族,也是 ChatGPT 背後的技術。配方只有一句話:選一個順序,用 chain rule 拆開,再用神經網路預測每一個條件分佈。這講從最簡單的 logistic 版本(FVSBN)一路走到 NADE、MADE、RNN,核心問題都是:怎麼讓 n 個分類器共享參數、共享計算。
讀完這頁你應該能夠
- 寫出 autoregressive model 的定義,並說明它如何把生成問題拆成 n 個分類問題。
- 對 FVSBN 計算 \(p(\mathbf x)\)、從它取樣,並算出參數量 \(\approx n^2/2\)。
- 說明 NADE 的權重共享如何讓參數和計算都降到 \(O(nd)\)。
- 把輸出層換成 softmax 或高斯混合,處理多類別和連續變數。
- 解釋為什麼普通 autoencoder 不是生成模型,以及 MADE 如何用遮罩修好它。
- 說明 RNN 用固定參數處理任意長度歷史的方式和它的缺點。
- 區分「訓練時可以平行」和「取樣時必須逐步」這兩件事。
前置知識檢查
- 第 2 講:chain rule、Bayes net、logistic regression、「chain rule + 神經網路」的結論
- sigmoid \(\sigma(z)=1/(1+e^{-z})\),以及 Bernoulli 分佈
- 神經網路的一層 \(\mathbf h = f(A\mathbf x + \mathbf b)\)、矩陣乘法的形狀
- softmax(第 5 節補充)、one-hot 編碼(第 7 節補充)
- 梯度下降的概念(訓練細節是第 4 講,這講只需要知道「參數可以從資料學出來」)
投影片轉成圖片後逐頁看過,文中的「投影片 N 的圖」描述圖片內容。標「出處」的數字都對照過原始論文:Larochelle & Murray (2011) 的 NADE 論文、Uria et al. (2016) 的 NADE 整理論文(arXiv 1605.02226)。
01回顧:為什麼「能算機率」這麼重要
大圖不變:資料來自未知的 \(P_{\text{data}}\),我們選一個模型家族、一個距離,再最佳化。00:36這講的模型家族都會用神經網路定義,\(\theta\) 就是網路的權重。
老師特別強調「能算 \(p_\theta(\mathbf x)\)」的兩個用途:
- 訓練:能算機率,就能做 maximum likelihood,找讓訓練資料機率最大的參數(第 4 講)。02:41
- 異常偵測:例如偵測 adversarial example,它們在乾淨影像訓練出的模型下機率偏低(投影片後半的 PixelDefend)。03:12
第三個用途「非監督式表示學習」對 autoregressive model 來說比較難,之後的 VAE 會自然得多。04:13
投影片 3–4 的圖:(3) 回顧第 2 講的三種寫法:完全一般的 chain rule、用條件機率表加條件獨立的 Bayes net、用 \(p_{\text{Neural}}\) 表示條件分佈的神經模型。(4)「Neural Models for classification」:logistic regression 與一層非線性的 \(p_{\text{Neural}}(Y = 1\mid\mathbf x;\mathbf A,\mathbf b,\boldsymbol\alpha)\),旁邊是一個多層感知器的示意圖(輸入 → 隱藏層 → 單一輸出)。
02Autoregressive 的配方
以二值化 MNIST 為例:每張圖 \(28\times28 = 784\) 個像素,每個是 0 或 1。目標是學 \(p(x_1,\dots,x_{784})\),使取樣出來的圖像數字。11:31
投影片 5–6 的圖:(5) 一排二值化 MNIST 數字 9 3 6 2 0 6 1 4 5 7,以及「兩步驟」:先參數化一個模型家族 \(\{p_\theta(x),\theta\in\Theta\}\)(這講),再用訓練資料找參數(下一講)。(6) 定義與例子:\(p_{\text{CPT}}(X_1 = 1;\alpha^1) = \alpha^1\)、\(p_{\text{logit}}(X_2 = 1\mid x_1;\boldsymbol\alpha^2) = \sigma(\alpha^2_0 + \alpha^2_1x_1)\)、\(p_{\text{logit}}(X_3 = 1\mid x_1,x_2;\boldsymbol\alpha^3) = \sigma(\alpha^3_0 + \alpha^3_1x_1 + \alpha^3_2x_2)\),並強調「Note: This is a modeling assumption」。
- 選順序:例如 raster scan,由左上到右下。
- Chain rule(不失一般性):\(p(x_1,\dots,x_n) = \prod_{i=1}^{n} p(x_i\mid x_{<i})\)
- 用參數化函數表示每個條件分佈(這是建模假設):\(p(x_i\mid x_{<i}) \approx p_{\text{Neural}}(x_i\mid x_{<i};\theta_i)\)
這樣一個困難的生成問題,就拆成一連串我們熟悉的分類問題:給前面的像素,預測下一個像素。14:38
名字的由來:模型在「對自己做迴歸」(regress on itself),用資料點的一部分預測同一個資料點的另一部分。18:51
記號:\(x_{<i}\)、raster scan,以及「順序重要嗎?」
\(x_{<i}\) 是 \((x_1,\dots,x_{i-1})\) 的簡寫,也就是「排在第 \(i\) 個之前的所有變數」。\(x_{1:t}\) 則是 \(x_1\) 到 \(x_t\)。
Raster scan 就是讀書的順序:第一列由左到右,再換下一列。對 8×8 圖,第 \(i\) 個像素(從 0 數)在第 \(\lfloor i/8\rfloor\) 列、第 \(i \bmod 8\) 行。
順序重要嗎?對 chain rule 本身不重要,任何順序都精確成立。05:48但一旦用神經網路近似條件分佈,不同順序的難度就不同。老師的直覺是:如果資料有自然的因果方向(例如時間序列),順著那個方向建模可能比較容易。圖片沒有明顯的方向,raster scan 只是一個方便的選擇。13:36
03FVSBN:每個條件分佈一個 logistic regression
最簡單的選擇:第一個像素用一個參數存(CPT),之後每個像素用 logistic regression 預測。15:43這叫 Fully Visible Sigmoid Belief Network(FVSBN)。「fully visible」是指所有變數都是看得到的,沒有潛在變數。19:25
\(\hat x_i\) 讀作「x hat i」,是模型預測第 \(i\) 個像素為 1 的機率。注意 \(\hat x_i\) 本身是 \(x_{<i}\) 的函數。每個 \(i\) 都有自己的一組係數 \(\boldsymbol\alpha^i\),長度各不相同。16:44
投影片 7 的圖:FVSBN 的示意圖:上排是輸出 \(\hat x_1,\dots,\hat x_4\),下排是輸入 \(x_1,\dots,x_4\),每個 \(\hat x_i\) 只從排在它前面的 \(x_j\) 接線(右下角那個輸出連到所有前面的輸入,左邊的第一個輸出沒有任何連線)。
計算機率:把條件機率乘起來
四個像素、觀測值 \((0,1,1,0)\):20:59
Bernoulli 的機率怎麼寫成一個式子?為什麼取 log?
若 \(x\in\{0,1\}\)、\(p(x{=}1)=\hat x\),則 \(p(x) = \hat x^{\,x}(1-\hat x)^{1-x}\):\(x=1\) 時得 \(\hat x\),\(x=0\) 時得 \(1-\hat x\)。
784 個介於 0 和 1 的數相乘會小到電腦存不下(下溢),所以實務上都算 log 並相加:
把它加上負號就是 binary cross-entropy,分類器最常用的損失函數。第 6 節的 autoencoder 損失會再出現同一個式子。
取樣:照順序一個一個決定
先抽 \(\bar x_1\sim p(x_1)\),再抽 \(\bar x_2\sim p(x_2\mid x_1{=}\bar x_1)\),再抽 \(\bar x_3\sim p(x_3\mid \bar x_1,\bar x_2)\)……每一步都只是丟一枚機率為 \(\hat x_i\) 的硬幣(numpy 的 np.random.choice([1,0], p=[x̂, 1−x̂]))。23:03
這就是第 2 講的 ancestral sampling。好處是不需要 MCMC 之類的複雜方法;壞處是必須逐個變數依序進行,無法平行。
什麼時候條件取樣容易?什麼時候難?(老師沒展開)
容易:已知的部分剛好是順序上的前綴 \(x_{1:k}\)。把它們填進去,從 \(x_{k+1}\) 繼續 ancestral sampling 就好,因為 \(p(x_{k+1:n}\mid x_{1:k}) = \prod_{i>k}p(x_i\mid x_{<i})\) 正好是模型直接給的因子。Raster scan 下「已知上半部、補下半部」就是這種情況。Demo A 有示範。ChatGPT 根據你的 prompt 續寫也是同一件事。
困難:已知的是後面的部分,例如已知下半部要補上半部。這時需要
分母要對上半部所有 \(2^{\text{像素數}}\) 種可能求和,算不動。只能用 MCMC 等近似方法。
參數量與效果
第 \(i\) 個 logistic regression 有 \(i\) 個參數(\(i-1\) 個權重加一個偏差),總共 \(1+2+\cdots+n = n(n+1)/2\approx n^2/2\)。對 MNIST 約 31 萬個,可以接受,但不理想。26:10
效果不好:在 Caltech 101 Silhouettes 上取樣只得到模糊的團塊。logistic regression 太弱,捕捉不了像素間的複雜依賴。27:11
投影片 8 的圖:左邊是訓練資料(Caltech 101 Silhouettes,各種物體的黑色剪影),右邊是 FVSBN 的樣本:只看得出大致的黑色團塊,邊緣破碎。圖出自 Gan et al.,「Learning Deep Sigmoid Belief Networks with Data Augmentation」(2015)。
04NADE:換成神經網路,再把權重綁起來
第一步:每個條件分佈用一層神經網路
NADE(Neural Autoregressive Density Estimation)把 logistic regression 換成單隱藏層網路:28:12
\(A_i\) 的形狀是 \(d\times(i-1)\):\(A_2\) 只有一行(對應 \(x_1\)),\(A_3\) 有兩行,以此類推。如果每個 \(A_i\) 都獨立,參數量仍然是 \(O(n^2 d)\),而且 n 個網路各學各的,浪費了「這些預測問題其實很相關」的結構。31:19
第二步:權重共享(weight tying)
關鍵想法:所有 \(A_i\) 都改成同一個大矩陣 \(W\in\mathbb R^{d\times n}\) 的前 \(i-1\) 行。31:50
直覺:第 1 行 \(\mathbf w_1\) 是「從 \(x_1\) 抽出的特徵」,它在預測之後每一個像素時都被重複使用。所以訓練時,\(\mathbf w_1\) 會從所有預測任務得到學習訊號。37:35
投影片 9–10 的圖:NADE 的示意圖和 FVSBN 一樣是上排輸出、下排輸入,但中間多了一排隱藏單元 \(\mathbf h_i\)。投影片 10 把共享的權重畫成藍點:\(\mathbf h_2,\mathbf h_3,\mathbf h_4\) 的權重矩陣分別是同一個 \(W\) 的前 1、2、3 行(藍色的 \(\mathbf w_1\) 在三個式子裡重複出現),並寫出「If \(\mathbf h_i\in\mathbb R^d\), how many total parameters? Linear in \(n\):weights \(W\in\mathbb R^{d\times n}\), biases \(\mathbf c\in\mathbb R^d\), and \(n\) logistic regression coefficient vectors \(\boldsymbol\alpha_i, b_i\in\mathbb R^{d+1}\). Probability is evaluated in \(O(nd)\).」
| 模型 | 參數 | 算一次 \(p(\mathbf x)\) 的計算量 |
|---|---|---|
| FVSBN | \(\approx n^2/2\) | \(O(n^2)\) |
| NADE(不共享) | \(O(n^2 d)\) | \(O(n^2 d)\) |
| NADE(共享) | \(W\!: d\times n\),\(\mathbf c\!: d\),\(\boldsymbol\alpha_i, b_i\!: n(d+1)\),合計 \(O(nd)\) | \(O(nd)\) |
參數從 n 的平方變成 n 的線性。35:29共享參數也有助於減少 overfitting。33:21
為什麼共享後算 \(p(\mathbf x)\) 只要 \(O(nd)\)?
令 \(\mathbf a_i = W_{\cdot,<i}\,x_{<i} + \mathbf c\),也就是 \(\mathbf h_i\) 在套 sigmoid 之前的值。注意 \(\mathbf a_{i+1}\) 只比 \(\mathbf a_i\) 多加了一行:
每一步只要做一次長度 \(d\) 的向量加法,再算 \(\mathbf h_i=\sigma(\mathbf a_i)\) 和一個長度 \(d\) 的內積。n 步合計 \(O(nd)\)。不共享的話每個 \(\mathbf h_i\) 都要從頭算矩陣乘向量,總共 \(O(n^2d)\)。36:03
Demo A 的 NADE 就是照這個遞迴實作的,訓練用的反向傳播也是 \(O(nd)\)。
投影片 11 的 NADE 結果有兩張 8×10 的 MNIST 網格,出自 Larochelle & Murray (2011):左邊是取樣出的二值圖,右邊是取樣過程中每個像素的 \(\hat x_i\)(灰階)。兩張看起來幾乎一樣,因為左圖就是從右圖的機率逐一抽出來的。39:06Demo A 的「逐像素生成」重現了這兩張圖。
論文裡的數字:FVSBN 和 NADE 在二值化 MNIST 上差多少?
在 28×28 的二值化 MNIST 測試集上,平均負 log-likelihood(越低越好):
| 模型 | nats / 張 |
|---|---|
| Bernoulli 混合(500 個分量) | 137.64 |
| FVSBN(SGD 訓練) | 97.45 |
| NADE(Larochelle & Murray 2011) | 88.86 |
| NADE(固定順序,Uria et al. 2016 重跑) | 88.33 |
| MADE(2 層隱藏層、32 組遮罩) | 86.64 |
| EoNADE(2 層、128 種順序的 ensemble) | 85.10 |
從 logistic 換成一層權重共享的網路,就少了約 9 nats。換算成 bits 是約 12.4 bits / 張,也就是每張圖平均少用 12 個 bit 壓縮。
出處:Larochelle & Murray (2011), "The Neural Autoregressive Distribution Estimator", AISTATS, §6(FVSBN −97.45、NADE −88.86);Uria et al. (2016), arXiv 1605.02226, binarized MNIST 的表(Bernoulli 混合、NADE 88.33、MADE、EoNADE)。
Demo AFVSBN vs. NADE:8×8 手寫數字
同一份資料(1,497 張訓練、300 張測試),兩個模型都在瀏覽器裡用 Adam 訓練。NADE 的隱藏層 \(d = 32\),raster scan 順序。
逐像素生成:看 \(\hat x_i\) 如何一步步決定圖
條件取樣:已知上半部(前綴),補完下半部
觀察重點:
- NADE 的 test NLL 比 FVSBN 低約 0.6 bit(瀏覽器實測 25.9 → 25.3 bits / 張,30 個 epoch)。8×8 的資料量很小,差距有限;在 28×28 的二值 MNIST 上,論文報告的差距大得多(FVSBN 97.45 nats,NADE 88.86 nats,見第 4 節的補充框)。
- 參數量要看清楚比較的對象。NADE 的 \(O(nd)\) 是和不共享權重的 NADE(\(O(n^2d)\))比。和 FVSBN 的 \(n^2/2\) 比,要看 \(d\) 和 \(n\) 的大小:這裡 \(n=64,\ d=32\),NADE 的參數(4,192)反而比 FVSBN(2,080)多。NADE 的優勢在於非線性隱藏層帶來的表達力,而不是參數更少。
- 逐像素生成時,右圖在筆畫「接下來應該延伸到哪」的位置會出現接近 1 的深色預測,空白區則接近 0。
- 補完下半部只是從第 33 個像素開始繼續 ancestral sampling,這對 autoregressive model 很容易。反過來「已知下半部補上半部」就沒有這麼簡單的做法。
05非二元與連續的輸出
架構不變,只要換輸出層,讓網路輸出合適分佈的參數。
K 個類別:softmax
像素值 0–255 是 \(K=256\) 類的 categorical 變數:44:20
指數保證非負,除以總和保證加起來是 1。numpy 寫法是 np.exp(a)/np.sum(np.exp(a))。45:52
softmax 和 sigmoid 的關係、數值穩定性
sigmoid 是 \(K=2\) 的 softmax:$$\mathrm{softmax}(a,0)_1 = \frac{e^a}{e^a+e^0} = \frac{1}{1+e^{-a}} = \sigma(a)$$
平移不變:所有 \(a^j\) 同時加上常數 \(c\),分子分母都乘 \(e^c\),結果不變。實務上會先減掉 \(\max_j a^j\) 再取指數,避免 \(e^{1000}\) 溢位。只用 numpy 那行式子在 logits 很大時會出錯。
名字的由來:它是「軟化的 argmax」。把 logits 乘上很大的數,softmax 會趨近 one-hot(只在最大值處為 1)。Demo C 的 temperature 就是在調這個。
連續變數:RNADE 與高斯混合
語音等連續訊號不適合離散化。做法是讓網路輸出某個連續分佈的參數,例如 \(K\) 個高斯的均勻混合:46:24
網路輸出 \(2K\) 個數。標準差必須是正的,可以讓網路輸出 \(\log\sigma\) 再取 \(\exp\)。48:29
投影片 13 的圖:橫軸 −6 到 6 的一維密度:紅色實線是一個雙峰的高斯混合(左邊一個較寬的峰、右邊一個較高的峰),藍色虛線是各個分量。投影片的例子是「uniform mixture of K Gaussians」。
輸出層小實驗:2K 個數 → 一個條件密度
網路的輸出(左側的原始數字)可以是任意實數。把其中一半當 \(\mu\)、另一半取 \(\exp\) 當 \(\sigma\),就得到一個合法而且可以多峰的密度。換一組前文 \(x_{<i}\),網路就輸出另一組數,對應另一條曲線。
06Autoencoder 與 MADE
FVSBN / NADE 看起來很像 autoencoder
輸入 \(\mathbf x\),輸出 \(\hat{\mathbf x}\),看起來就像在「重建輸入」。49:33Autoencoder 有兩個部分:
- Encoder \(e(\cdot)\):把 \(\mathbf x\) 映到潛在表示,例如 \(e(\mathbf x) = \sigma(W^2(W^1\mathbf x+\mathbf b^1)+\mathbf b^2)\)。
- Decoder \(d(\cdot)\):把表示映回去,希望 \(d(e(\mathbf x))\approx\mathbf x\),例如 \(d(\mathbf h) = \sigma(V\mathbf h+\mathbf c)\)。
損失是重建誤差:51:12
二元版和 FVSBN 的負 log-likelihood 長得一模一樣。52:49差別在於 autoencoder 的 \(\hat x_i\) 可以看到 \(x_i\) 本身,甚至看到所有 \(\mathbf x\)。所以要限制架構(例如瓶頸層很窄),否則學到恆等函數就沒意義了。52:16
投影片 15–16 的圖:左邊是 FVSBN、NADE 的示意圖,右邊是一個 autoencoder(輸入 \(x_1,x_2,x_3\) → 隱藏層 → 輸出 \(\hat x_1,\hat x_2,\hat x_3\),全連接),標題問「On the surface, FVSBN and NADE look similar to an autoencoder」。投影片 16 把這個 autoencoder 標上順序,問怎麼讓它對應到一個合法的 Bayes net,並寫出「Bonus: we can use a single neural network (with \(n\) inputs and outputs) to produce all the \(\hat x_i\) in a single pass. In contrast, NADE requires \(n\) passes.」
普通 autoencoder 不是生成模型
它沒有定義一個可以取樣的 \(p(\mathbf x)\)。54:23課堂上學生提議「丟掉 encoder,只用 decoder」,問題是 decoder 的輸入要從哪來?學生接著說「強迫潛在表示服從高斯,再從高斯取樣餵給 decoder」,老師說那正是 VAE(第 5–6 講)。54:54 55:54
Autoencoder 平常拿來做什麼?
主要用途是學壓縮表示:讓 encoder 的輸出維度很小,為了重建得好,它必須抓住資料最主要的變化因素。老師形容它像「非線性的 PCA」。53:53
PCA(主成分分析)找一個低維線性子空間,使資料投影上去後保留最多變異。Autoencoder 把「線性投影」換成神經網路,所以能找到彎曲的低維結構。
讓 autoencoder 變成 autoregressive
要能取樣,就需要一個順序:56:27
- \(\hat x_1\) 不能依賴任何輸入,所以生成時不需要輸入就能開始;
- \(\hat x_2\) 只能依賴 \(x_1\);
- 依此類推,\(\hat x_i\) 只能依賴 \(x_{<i}\)。
額外好處:一個有 n 個輸入、n 個輸出的網路,一次 forward 就能算出所有 \(\hat x_i\)。NADE 需要依序做 n 步,在 GPU 上效率差很多。61:05
MADE:用遮罩切斷「偷看」的路徑
MADE(Masked Autoencoder for Distribution Estimation,Germain et al. 2015)的做法:61:35
- 選一個順序,例如 \(x_2, x_3, x_1\),對應 $$p(x_1,x_2,x_3) = p(x_2)\,p(x_3\mid x_2)\,p(x_1\mid x_2,x_3)$$輸入依此順序編號:\(x_2\to1,\ x_3\to2,\ x_1\to3\)。
- 每個隱藏單元隨機抽一個整數 \(m\in[1,n-1]\):它只能依賴順序前 \(m\) 個輸入。62:39
- 加上遮罩維持這個性質:隱藏單元只連到前一層編號 ≤ 自己的單元;輸出層編號為 \(d\) 的變數只連到編號 < \(d\) 的隱藏單元(嚴格小於)。63:42
投影片 17 的圖(出自 Germain et al. 2015):左邊是一般的 autoencoder,中間是三個 0/1 遮罩矩陣 \(M^{W^1}, M^{W^2}, M^{V}\)(黑格是 1、白格是 0),右邊是套用遮罩後的 MADE:輸入節點標順序 3、1、2,每個隱藏單元標上它抽到的編號,只有符合規則的連線留下來;輸出分別是 \(p(x_1\mid x_2,x_3)\)、\(p(x_2)\)、\(p(x_3\mid x_2)\)。
遮罩在數學上是什麼?為什麼「≤」和「<」保證了 autoregressive?
遮罩是一個和權重矩陣同形狀的 0/1 矩陣 \(M\),前向傳播改用逐元素乘積 \(W\odot M\)。被設為 0 的連線完全不起作用,梯度也是 0,所以訓練時永遠不會被用到。
歸納證明:輸入單元 \(k'\) 的編號就是它在順序中的位置。如果每個單元都只連到編號 ≤ 自己的單元,那麼編號為 \(m\) 的單元,沿任何路徑往回走,編號只會越來越小或不變,最後只能到達編號 ≤ \(m\) 的輸入。輸出層用嚴格的「<」,所以第 \(d\) 個變數的輸出只看得到編號 \(<d\) 的輸入,也就是 \(x_{<d}\)。這正是 chain rule 需要的。
為什麼隱藏單元的編號只抽 \([1,n-1]\)?編號 \(n\) 的單元只會連到第 \(n+1\) 個以後的輸出,而那不存在,所以它沒有用處。
- 只在訓練時快:取樣時仍然要先生成 \(x_1\)、餵回去、再生成下一個,n 次 forward 無法避免。這是所有 autoregressive model 的共同特徵。66:20
- 遮罩在訓練時就要加:否則預測 \(x_2\) 時直接偷看 \(x_2\) 的真實值。這和語言模型訓練時要遮住未來 token 是同一件事。65:18
- 順序怎麼選:沒有好方法。原論文隨機選,也可以訓練多種順序和遮罩的 ensemble。可能的順序有 \(n!\) 種,而且是離散的,很難最佳化。66:50
- 第一個輸出沒有任何輸入,只能輸出一個常數,也就是資料中 \(x_{\text{first}}\) 的邊際機率。67:51
- 如果隨機抽到的編號很不平均(例如全是 1),某些輸出能用的資訊會很少。模型仍然合法,只是比較弱。所以實務上常用多組遮罩。70:26
Demo BMADE 遮罩視覺化
4 個輸入、兩層各 6 個隱藏單元、4 個輸出。節點裡的數字是 MADE 指定的編號 \(m\)。點任一個輸出節點,看它實際能沿著哪些路徑「看到」哪些輸入。
07RNN:用一個固定大小的摘要處理越來越長的歷史
Autoregressive 的核心困難是:要預測 \(x_t\),條件 \(x_{1:t-1}\) 越來越長。RNN(Recurrent Neural Network)的想法是維護一個摘要向量 \(\mathbf h_t\),每看到一個新輸入就遞迴更新它:71:03
投影片 18 的圖:三排圓點沿時間展開:下排紅色是輸入 \(x_t\),中排綠色是隱藏狀態,上排藍色是輸出 \(o_t\);每個時間步的隱藏狀態都接收前一步的隱藏狀態和這一步的輸入。
- \(\mathbf h_t\) 是「到時間 t 為止看過的所有輸入」的摘要。
- \(\mathbf o_{t-1}\) 給出條件分佈 \(p(x_t\mid x_{1:t-1})\) 的參數(例如過 softmax)。
- 參數只有 \(\mathbf b_0, W_{hh}, W_{xh}, W_{hy}\),與序列長度 n 無關。從 FVSBN 的 \(O(n^2)\)、NADE 的 \(O(n)\),到這裡的 \(O(1)\),是最極端的權重共享。73:35
例子:字元級 RNN 生成 "hello"
字母表 \(\{h,e,l,o\}\),用 one-hot 編碼(h = [1,0,0,0]、e = [0,1,0,0]…)。75:09
訓練的目標是調整三個矩陣,讓 \(\mathbf o_1\) 中對應 e 的那一格變大,依此類推。76:40
投影片 19 的圖(出自 Karpathy 的部落格):四個時間步,輸入層是 h、e、l、l 的 one-hot(黃色),隱藏層是三維的向量(綠色,例如 0.3、−0.1、0.9),輸出層是四個字元的分數(藍色),每一步正確的下一個字元(e、l、l、o)標成綠色粗體、其他標紅色,例如第一步的輸出是 1.0、2.2、−3.0、4.1,正確答案 e 的分數 2.2 還不是最大的。
One-hot 編碼,以及 \(W_{xh}x\) 其實只是「挑一行」
One-hot:K 個類別的變數用長度 K 的向量表示,正確類別的位置是 1,其他是 0。它把類別變成網路能吃的數字,又不會引入「h < e」這種不存在的大小關係。
當 \(x\) 是 one-hot 時,\(W_{xh}x\) 就是取出 \(W_{xh}\) 的第 k 行,所以 \(W_{xh}\) 的每一行可以看成那個字元的embedding(嵌入向量)。這就是現代語言模型 embedding 層的由來。
優缺點
- 可用於任意長度的序列。
- 非常一般:對任何可計算函數,都存在一個有限的 RNN 能算它(理論上)。
- 仍然需要一個順序。
- 算 likelihood 必須依序展開,訓練很慢。
- 生成也是逐步的(autoregressive 無法避免)。
Karpathy 的 char-RNN 例子:3 層、512 個隱藏單元,在莎士比亞全集上逐字元訓練,就能生成有莎士比亞味道的台詞;在 Wikipedia 上訓練,能生成語法正確的 Markdown,連 [[ ]] 的開合都配對正確。而這些全都要靠單一個隱藏向量記住。在嬰兒名字上訓練,則能生成新的名字。79:27
投影片 21–24 的樣本:(21) 莎士比亞風格的「KING LEAR」台詞,用字和韻律都像,但語意不太通。(22) 維基百科風格的段落,講某個阿拉伯國家的首都和「[[Protestant Immineners]]」,連結的 [[ ]] 都配對正確,投影片註明「correct Markdown syntax」。(23) 模型生成的 wiki 原始碼:{{cite journal …}}、「== See also ==」、「== External links ==」和網址,格式完整。(24) 一長串新造的嬰兒名字(Rudi、Levette、Berice、Lussa、Hany……)。
RNN 怎麼訓練?為什麼會有梯度消失/爆炸?
把遞迴沿時間展開,就得到一個很深的網路(每個時間步一層,各層共用權重)。對它做一般的反向傳播,叫 BPTT(backpropagation through time)。
損失對很早的 \(\mathbf h_1\) 的梯度要乘上 t 次 \(\partial\mathbf h_{k+1}/\partial\mathbf h_k = \mathrm{diag}(1-\mathbf h_{k+1}^2)\,W_{hh}\)。如果這些矩陣「放大」,乘很多次就爆炸;如果「縮小」,就趨近 0(消失),模型學不到遠距離的依賴。LSTM、GRU 用門控機制緩解這個問題,attention 則直接讓每個位置看到所有過去位置。
Demo C 的 char-RNN 就是用 BPTT 訓練,並把梯度截斷在 [−5, 5] 防止爆炸(gradient clipping)。
Demo Cchar-RNN:學英文名字
資料是 2,373 個英文名字(90% 訓練、10% 驗證),字母表為 a–z 加上一個結束符號「.」,共 27 類。模型是單層 RNN,48 個隱藏單元,在你的瀏覽器裡用 BPTT 和 Adam 訓練。這就是投影片 24「嬰兒名字」例子的縮小版。
Temperature 是什麼?(課堂沒講,但所有語言模型都在用)
取樣前把 logits 除以 \(T\):\(p_T(k)\propto\exp(o_k/T)\)。
- \(T=1\):原本的模型分佈。
- \(T<1\):分佈變尖,偏向最可能的字元。名字更「安全」、更像常見名字,但多樣性下降,容易重複。
- \(T>1\):分佈變平,更隨機、更有創意,但也更容易拼出怪字。
- \(T\to0\):每步都選最大值(greedy decoding)。
注意:\(T\ne1\) 時,取樣的分佈已經不是模型學到的 \(p_\theta\) 了,它是一個刻意扭曲過的分佈。
瀏覽器實測:訓練 25 個 epoch 後,驗證集約 3.35 bits / 字元,比均勻猜測的 4.75 bits 少了三成。試試看:輸入 "anna"、"michael" 這類常見名字,和 "xqzvt" 這種亂打的字串比較 log₂ p。亂打的字串每個字元的機率都很低,這就是 autoregressive model 能做密度評估(異常偵測)的直接示範。每根柱子是 \(p(x_t\mid x_{<t})\),整個名字的機率就是它們的乘積(最後還要乘上「在這裡結束」的機率)。
08投影片後半:本講時間不夠沒講到的部分
影片在 RNN 的例子處結束,投影片第 25–36 頁沒有在課堂上講。下面整理這些投影片的內容並補上必要的背景。第 4 講的前 30 分鐘補講了這部分,課堂的細節和問答整理在第 4 講筆記的 Part A。
RNN 的問題(投影片 25)
- 單一隱藏向量要總結全部(不斷變長的)歷史。例如 \(\mathbf h^{(4)}\) 要總結「My friend opened the」的意思。
- 依序計算,無法平行。
- 要存取很多步以前的資訊時,梯度會爆炸或消失。
投影片 25 的圖:一個 RNN 沿時間展開讀「My friend opened the」,上方一個長條圖是預測下一個字的分佈(「door」最高)。
Attention(投影片 26)
- 把當前的隱藏狀態(query)和所有過去的隱藏狀態(keys)比較,例如取內積。
- 用 softmax 得到 attention 分佈:歷史中哪些部分和現在相關。
- 用這個分佈對歷史加權平均,得到摘要。
- 用摘要和當前隱藏狀態一起預測下一個 token。
投影片 26 的圖:最後一個隱藏狀態(query,綠色)和所有過去的隱藏狀態(keys,紅色)做內積得到「Attention scores」,經過 softmax 變成「Attention distribution」的長條圖,再加權平均得到「Attention output」,最後和 query 一起預測下一個字。投影片 27 是 Transformer 的架構圖,並附上兩個 demo 連結(GPT-2 large、Llama 2 13B chat)。
Attention 的公式(Transformer 版本)
每個位置 \(t\) 從自己的表示算出三個向量:query \(\mathbf q_t\)、key \(\mathbf k_t\)、value \(\mathbf v_t\)(都是乘上學出來的矩陣)。位置 t 的輸出是
\(\alpha_{ts}\) 表示「位置 t 該花多少注意力在位置 s 上」。和 RNN 不同,每個位置都能直接看到所有其他位置,不必經過一個瓶頸向量。除以 \(\sqrt d\) 是為了讓內積在維度大時不會太大,避免 softmax 飽和。
Causal mask:autoregressive 生成時,位置 t 不能看 \(s>t\),所以把那些 \(\mathbf q_t^\top\mathbf k_s\) 設為 \(-\infty\),softmax 後權重變 0。這和 MADE 的遮罩是同一個概念:讓一次 forward 就能平行算出所有位置的條件分佈,同時不偷看未來。
Generative Transformer / GPT(投影片 27)
目前的 state of the art(GPT 系列)把 RNN 換成 Transformer:用 attention 動態聚焦在相關的上下文;不做遞迴,只用 self-attention,所以能平行化;需要 masked self-attention 來保持 autoregressive 結構。
「訓練可以平行、生成必須逐步」到底是什麼意思?
訓練時,整個序列 \(x_1,\dots,x_n\) 都在訓練資料裡。所有條件分佈 \(p(x_t\mid x_{<t})\) 的「條件」都是已知的真實資料,可以一次全部算出來再相加。這叫 teacher forcing。MADE、PixelCNN、Transformer 都靠遮罩做到一次 forward。RNN 即使條件已知也要依序展開,所以訓練慢。
生成時,\(x_t\) 的條件是模型自己剛抽出來的 \(x_{<t}\)。不先抽出 \(x_{t-1}\) 就無法算 \(p(x_t\mid x_{<t})\),所以一定是 n 步依序進行。這是所有 autoregressive model 的共同成本,也是 ChatGPT 為什麼一個字一個字地「打」出來。
影像:PixelRNN 與 PixelCNN(投影片 28–32)
- PixelRNN(van den Oord et al. 2016):raster scan 逐像素建模。每個像素有三個顏色,同一個像素內也用 chain rule 拆開:
$$p(x_t\mid x_{1:t-1}) = p(x_t^{\text{red}}\mid x_{1:t-1})\,p(x_t^{\text{green}}\mid x_{1:t-1},x_t^{\text{red}})\,p(x_t^{\text{blue}}\mid x_{1:t-1},x_t^{\text{red}},x_t^{\text{green}})$$每個條件都是 256 類的 categorical。條件分佈用 LSTM 變體加上類似 MADE 的遮罩表示。在 downsampled ImageNet 上效果不錯,但算 likelihood 要依序進行,非常慢。
- PixelCNN:用卷積網路,以一個像素的鄰域為上下文預測它。卷積天生適合影像,也容易在 GPU 上平行。為了保持 autoregressive,用 masked convolution:把卷積核中「未來」位置(同列右邊和下方各列)的權重遮成 0,顏色順序也另外遮罩。效果和 PixelRNN 差不多,但快得多。
投影片 28–32 的圖:(28) 一個像素網格,紅色像素 \(x_i\) 的上方與左方是已生成的上下文;下方小圖是同一像素內 R、G、B 三個通道的遮罩(Mask A、Mask B)。(29) PixelRNN 的補完:左欄是遮住下半部的圖(occluded),中間幾欄是模型補完的不同樣本(completions),右欄是原圖(original),例子有海龜、狗、木桶。(30) 一個卷積網路的示意圖。(31) PixelCNN:一個 5×5 的鄰域,「masked convolution」只看上方與左方(綠色 1)而不看右方與下方(紅色 0),旁邊是 masked convolution 疊兩層後的感受野。(32) PixelCNN 在 ImageNet 32×32 上的樣本網格,「Similar performance to PixelRNN, but much faster」。
Masked convolution 長什麼樣?
以 3×3 卷積核為例,中心是目前要預測的像素。在 raster scan 順序下,「過去」是上一列的三格和同列左邊一格:
第一層連中心都要遮掉(type A),之後的層可以保留中心(type B),因為那時中心位置的特徵已經只含過去的資訊。疊很多層後,感受野會往上和往左擴大。這和 MADE 的「≤ 與 <」是同一個道理。
應用:PixelDefend(投影片 33–34)
機器學習模型容易被 adversarial example 騙:在圖上加一點人眼看不出來的雜訊,分類就完全錯。能偵測嗎?Song et al. (2018) 的做法是:在乾淨的資料上訓練 PixelCNN,對新輸入計算 \(p(\mathbf x)\)。adversarial example 在模型下的機率明顯比較低,所以可以偵測出來。這是第 1 講「異常偵測」的實例。
投影片 33–34 的圖:(33) 經典的 adversarial example:一張狗的照片加上一層看不出圖案的雜訊,被分類成「ostrich」(鴕鳥);螳螂加上雜訊也被分成鴕鳥。(34) PixelDefend 論文的直方圖「Densities of Adversarial Examples」:橫軸是 bits per dimension,乾淨的訓練與測試影像分佈在約 1–4.5、峰值約 3 bits/dim;RAND、FGSM 的影像集中在約 5–5.5,BIM 約 4.5,在 PixelCNN 下機率明顯更低;DeepFool、CW 這兩種擾動較小的攻擊只稍微右移(峰值約 3.5),和乾淨影像重疊較多,比較難單靠 likelihood 分開。
出處:Song, Kim, Nowozin, Ermon & Kushman (2018),「PixelDefend」,arXiv 1710.10766。
WaveNet(投影片 35)
非常有效的語音模型。用 dilated convolution(擴張卷積)擴大感受野:第 \(d\) 層的卷積核每隔 \(2^d\) 個位置才取一個點。疊 \(L\) 層,感受野以 \(2^L\) 成長。語音每秒有上萬個樣本點,需要很長的上下文,這很重要。
投影片 35 的圖:WaveNet 的 dilated causal convolution:底下一排是輸入,往上四層的 dilation 分別是 1、2、4、8,每個輸出經由一個二元樹狀的連線看到 16 個輸入。
本講總結(投影片 36)
| 性質 | Autoregressive model |
|---|---|
| 取樣 | ✓ 容易:依序抽 \(\bar x_0\sim p(x_0)\)、\(\bar x_1\sim p(x_1\mid x_0{=}\bar x_0)\)……(但必須逐步) |
| 計算機率 | ✓ 容易:算出每個條件機率再相乘(實務上 log 相加)。理想情況下所有項可平行計算,訓練很快 |
| 連續變數 | ✓ 容易擴充:條件分佈用高斯 \(\mathcal N(\mu_\theta(x_{<t}),\Sigma_\theta(x_{<t}))\) 或 mixture of logistics |
| 特徵 / 分群 / 非監督學習 | ✗ 沒有自然的方法取得特徵 |
| 模型 | 條件分佈怎麼表示 | 參數 | 訓練時算 p(x) |
|---|---|---|---|
| FVSBN | 每個位置一個 logistic regression | O(n²) | 可平行 |
| NADE | 一層網路,權重共享 \(W_{\cdot,<i}\) | O(nd) | O(nd) 遞迴 |
| MADE | 遮罩 autoencoder | 與網路大小有關 | 一次 forward |
| RNN | 遞迴摘要 \(\mathbf h_t\) | O(1)(對 n) | 依序展開,慢 |
| PixelCNN / WaveNet | 遮罩或擴張卷積 | O(1)(對 n) | 一次 forward |
| Transformer | masked self-attention | O(1)(對 n) | 一次 forward |
與其他講的關聯
- 第 2 講:這講把「chain rule + 神經網路條件分佈」完整展開。第 2 講 Demo A 的第四列就是 FVSBN。
- 第 4 講 MLE:這講只定義了模型家族,下一講講怎麼訓練:maximum likelihood 和 KL divergence。你會發現 autoregressive model 的訓練就是對每個條件分佈做分類。
- 第 5–6 講 VAE:課堂問答中「強迫潛在表示服從高斯,再餵給 decoder」就是 VAE。它補上 autoregressive model 缺的「特徵」能力。
- 第 7–8 講 Flow:MAF / IAF 把 autoregressive model 重新詮釋成可逆變換,MADE 是它們的積木。
- 第 18 講:離散資料的 diffusion,是 autoregressive 語言模型的替代方案之一。
常見誤解
自我測驗
1. 寫出 autoregressive model 的分解式。哪一部分是精確的,哪一部分是假設?
2. FVSBN 中,n = 784 時參數有多少?
3. 用 FVSBN 計算 p(x₁=1, x₂=0, x₃=1),寫成 x̂ 的乘積。
4. NADE 隱藏層維度 d = 500、n = 784,共享權重後參數大約多少?
5. 為什麼 NADE 共享權重後算 p(x) 只要 O(nd)?
6. 要用 autoregressive model 生成 0–255 的灰階像素,輸出層要怎麼改?
7. RNADE 用 K 個高斯的混合,網路要輸出幾個數?怎麼保證標準差為正?
8. 為什麼普通 autoencoder 不是生成模型?
9. MADE 中,順序是 x₂, x₃, x₁。某隱藏單元編號 m = 1,它能連到哪些輸入?x₁ 的輸出能連到編號多少的最後一層隱藏單元?
10. MADE 和 NADE 都是 autoregressive。MADE 的優勢在哪?取樣時也有這個優勢嗎?
11. RNN 的參數量和序列長度 n 的關係是什麼?代價是什麼?
12. 已知一張圖的下半部,想用 raster-scan 的 PixelCNN 補上半部,為什麼困難?
延伸閱讀
- CS236 course notes:Autoregressive models,涵蓋 FVSBN、NADE、與 autoencoder 的關係。
- Larochelle & Murray (2011), The Neural Autoregressive Distribution Estimator(本頁參考):NADE 原始論文,FVSBN 與 NADE 的 MNIST 數字出處。
- Uria et al. (2016), Neural Autoregressive Distribution Estimation(本頁參考):NADE 系列的整理版論文,含 DeepNADE、EoNADE、與 MADE 的比較。
- Germain et al. (2015), MADE。
- Karpathy, The Unreasonable Effectiveness of Recurrent Neural Networks,投影片 19–24 的出處,強烈推薦。
- van den Oord et al. (2016), Pixel Recurrent Neural Networks(含 PixelCNN)。
- van den Oord et al. (2016), WaveNet。
- Jay Alammar, The Illustrated Transformer,圖解 attention,補第 8 節的背景。