Lecture 3 · 自回归模型 · Autoregressive Models

Deep Generative Models · Lecture 03。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:Stanford CS236 Deep Generative Models(Fall 2023,Stefano Ermon),Lecture 3 · 2023-10-04 · Autoregressive Models(slides:cs236_lecture3.pdf,36 页);课程笔记 Autoregressive Models。 依据说明:本讲按原课 slides 的顺序展开:动机(二值化 MNIST)→ FVSBN → NADE → 一般离散分布与 RNADE → 自回归模型 vs 自编码器 → MADE → RNN 与字符 RNN → 注意力与生成式 Transformer → PixelRNN、PixelCNN → PixelDefend 与 WaveNet → 小结。slides 上的图(FVSBN、NADE、PixelCNN 的样本,Karpathy 的字符 RNN 例子)用文字和论文出处代替;字符 RNN 那一页的 softmax 数字取自 slide 19。全部实验是本讲义补充的:7 个自回归模型在第 1 讲的像素世界上训练,测试 bits 和真实熵直接比较;PixelCNN 的盲点用梯度精确画出;顺序、异常检测、“容易与难的条件”三个实验检验 slide 36 的总结。 配套代码 Code:code/lecture03/(PyTorch;共享模块 ar_models.py + 5 个演示脚本。第一次运行要训练 7 个模型,代码 03 约 100 秒、代码 04 约 45 秒,其余在 10–50 秒;训练好的模型缓存在 checkpoints/,之后几秒钟) 预计阅读时间:3.5 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲,你应该能够:

  1. 说清楚自回归模型 = 链式法则 + 用带参数的函数表示每个条件分布,以及”这是一个建模假设”是什么意思;解释”自回归”这个名字的来历。
  2. 写出 FVSBN 的条件分布、算出它的参数个数(≈n2/2\approx n^2/2),会手算 p(x)p(x) 和逐个像素采样。
  3. 解释 NADE 的权重共享为什么让参数线性于 nn、让 hih_i 可以递推;说明怎样把 NADE 推广到类别分布(softmax)和连续变量(RNADE,混合高斯)。
  4. 区分自编码器和自回归模型:为什么普通的自编码器不是生成模型;MADE 怎样用掩码把自编码器变成合法的自回归模型,会给定编号推出掩码。
  5. 说出 RNN 的优缺点(参数和长度无关 vs 一个向量概括全部历史、不能并行、梯度消失),注意力的四步,以及生成式 Transformer 为什么需要因果掩码。
  6. 解释 PixelCNN 的掩码卷积(A 型 / B 型)和它的盲点;知道 PixelRNN、WaveNet 的思路。
  7. 总结自回归模型的优点和局限:采样容易但慢、似然精确且可并行、必须选顺序、没有天然的特征;“顺着顺序的条件容易,逆着顺序的条件难”。
  8. 读懂本讲的实验:7 个模型在像素世界上离真实分布有多远,以及几个和预期相反的结果(RNN 胜过 Transformer、MADE 在随机顺序下更好)。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲只给直觉,后面几讲再展开 Previewed only
第 2 讲:链式法则、贝叶斯网络、“用函数代替表格”、逻辑回归最大似然训练的原理(第 4 讲;本讲直接用”最小化 −log⁡pθ(x)-\log p_\theta(x)“训练)
神经网络的基本概念:全连接层、激活函数、反向传播过拟合与正则化(第 4 讲;本讲只指出现象)
PyTorch 不会也没关系,代码里有解释重要性采样与隐变量(第 5 讲;本讲代码 05 只预演一次)
用 Transformer 做大规模语言建模的工程细节(见本仓库 CS336、CS324)

0.3 本讲地图 Map

§1  思路:链式法则 + 函数参数化的条件分布;“自回归”的含义
§2  FVSBN:每个条件一个逻辑回归                                         ← 代码 01
§3  NADE、一般离散分布、RNADE;自回归模型 vs 自编码器;MADE              ← 代码 02
§4  RNN、字符 RNN、注意力、生成式 Transformer                             ← 代码 03
§5  PixelRNN、PixelCNN(掩码卷积与盲点)、WaveNet                         ← 代码 04
§6  应用与总结:排行榜、顺序、异常检测、容易与难的条件                    ← 代码 05
§7  2023 年之后
§8–12  小结、常见误区、练习、延伸阅读、下一讲预告

0.4 记号 Notation

符号含义
x=(x1,…,xn)x = (x_1, \dots, x_n)按某个顺序排好的变量(像素);像素世界里 n=64n = 64
x<ix_{<i}x1,…,xi−1x_1, \dots, x_{i-1}
x^i\hat{x}_i模型给出的 p(xi=1∣x<i)p(x_i = 1 \mid x_{<i})(二值情形)
αi\alpha^iFVSBN 第 ii 个逻辑回归的系数向量
hi∈Rdh_i \in \mathbb{R}^d隐藏表示(NADE 的第 ii 个隐藏向量;RNN 的第 ii 步状态)
W,c,V,bW, c, V, bNADE 的参数(共享的第一层、偏置、输出层)
m(k)m(k)MADE 中单元 kk 的编号(它最多能看前 m(k)m(k) 个输入)
bits/图、bits/dim测试集上的平均 −log⁡2pθ(x)-\log_2 p_\theta(x),以及除以 nn 后的值

0.5 关于数字 · About the numbers

所有实验用同一份数据:像素世界的 10,000 张训练图、2,000 张验证图、5,000 张测试图;真实分布在这 5,000 张测试图上的平均是 25.52 bit/图(0.3987 bits/dim),表里测试集上的”比真实多 bit”都是相对这个数;训练集上的”比真实多 bit”减去的是真实分布在训练集自己上的平均(25.61 bit)。数字来自作者机器(macOS,PyTorch 单线程 CPU)的实际输出,重复运行逐字节相同(计时除外);把 checkpoints/ 删掉重新训练,会得到完全相同的模型(已验证)。有好几个结果和写代码之前的预期相反,讲义按实际结果写,并在 代码 README 的”关于实验结果”里逐条记录。


1. 思路:链式法则 + 函数参数化的条件分布 · The idea

1.1 从第 2 讲接着往下走

原课第 3 页先回顾了第 2 讲的结论:用链式法则写出联合分布,

p(x1,x2,x3,x4)=p(x1) p(x2∣x1) p(x3∣x1,x2) p(x4∣x1,x2,x3),p(x_1, x_2, x_3, x_4) = p(x_1)\,p(x_2 \mid x_1)\,p(x_3 \mid x_1, x_2)\,p(x_4 \mid x_1, x_2, x_3),

这是完全一般的,不需要任何假设(代价是指数大小,没有免费的午餐)。贝叶斯网络假设条件独立、用表表示条件分布;神经模型假设条件分布有特定的函数形式——“足够深的神经网络可以逼近任何函数”。第 2 讲代码 04 已经在一个条件分布上看到了:12 个父节点的表要 4096 格,而一个神经网络用 1538 个参数、1000 个样本就学得最好。本讲把这个想法用到每一个条件分布上。

1.2 动机:二值化 MNIST

原课第 5 页:给定一个手写数字的数据集 D\mathcal{D}(二值化的 MNIST),每张图有 n=28×28=784n = 28 \times 28 = 784 个像素,每个像素非黑即白。目标是学一个分布 p(x)=p(x1,…,x784)p(x) = p(x_1, \dots, x_{784}),使得从中采样的 xx 看起来像一个数字。分两步:

  1. 参数化一个模型族 {pθ(x),θ∈Θ}\{p_\theta(x), \theta \in \Theta\} ——本讲;
  2. 根据训练数据搜索参数 θ\theta ——下一讲。

本讲义的玩具版本是像素世界:n=64n = 64,真实分布已知。

1.3 自回归模型

原课第 6 页:选一个所有随机变量的顺序,例如从左上角(X1X_1)到右下角(X784X_{784})的光栅扫描顺序。不失一般性,用链式法则分解:

p(x1,…,x784)=p(x1) p(x2∣x1) p(x3∣x1,x2)⋯p(xn∣x1,…,xn−1).p(x_1, \dots, x_{784}) = p(x_1)\,p(x_2 \mid x_1)\,p(x_3 \mid x_1, x_2) \cdots p(x_n \mid x_1, \dots, x_{n-1}).

有些条件分布太复杂,没法存成表。于是假设:

p(x1,…,x784)=pCPT(x1;α1) plogit(x2∣x1;α2) plogit(x3∣x1,x2;α3)⋯plogit(xn∣x1,…,xn−1;αn),p(x_1, \dots, x_{784}) = p_{\text{CPT}}(x_1; \alpha^1)\,p_{\text{logit}}(x_2 \mid x_1; \alpha^2)\,p_{\text{logit}}(x_3 \mid x_1, x_2; \alpha^3) \cdots p_{\text{logit}}(x_n \mid x_1, \dots, x_{n-1}; \alpha^n),

更明确地:

pCPT(X1=1;α1)=α1,plogit(X2=1∣x1;α2)=σ(α02+α12x1),plogit(X3=1∣x1,x2;α3)=σ(α03+α13x1+α23x2), …p_{\text{CPT}}(X_1 = 1; \alpha^1) = \alpha^1, \qquad p_{\text{logit}}(X_2 = 1 \mid x_1; \alpha^2) = \sigma(\alpha^2_0 + \alpha^2_1 x_1), \qquad p_{\text{logit}}(X_3 = 1 \mid x_1, x_2; \alpha^3) = \sigma(\alpha^3_0 + \alpha^3_1 x_1 + \alpha^3_2 x_2), \ \dots

原课特别强调:“This is a modeling assumption.” 链式法则本身是恒等式,“每个条件是一个逻辑回归”才是假设。用带参数的函数(这里是逻辑回归)根据前面所有的像素预测下一个像素,这样的模型叫自回归模型(autoregressive model)——“自”是因为预测的对象和用来预测的变量来自同一个序列,“回归”是因为每一步都是一个回归(这个词来自时间序列分析里的 AR 模型:用过去的值回归下一个值)。

为什么这个思路这么好用? 三个原因,本讲会反复验证:

  1. 合法性是免费的:只要每个条件分布是一个合法的分布(例如 σ(⋅)∈(0,1)\sigma(\cdot) \in (0, 1)),乘起来就一定是合法的联合分布——链式法则保证了这一点。不需要算任何归一化常数(第 11 讲的能量模型就要为此付出巨大代价)。
  2. 似然精确可算:log⁡p(x)=∑ilog⁡p(xi∣x<i)\log p(x) = \sum_i \log p(x_i \mid x_{<i}),每一项都能直接算。
  3. 训练就是监督学习:每个条件分布都是”给定前面,预测下一个”的分类问题——这正是语言模型的训练方式。

2. FVSBN:每个条件一个逻辑回归 · Fully Visible Sigmoid Belief Network

2.1 定义

原课第 7 页:条件变量 Xi∣X1,…,Xi−1X_i \mid X_1, \dots, X_{i-1} 是伯努利分布,参数为

x^i=p(Xi=1∣x1,…,xi−1;αi)=σ(α0i+∑j=1i−1αjixj).\hat{x}_i = p(X_i = 1 \mid x_1, \dots, x_{i-1}; \alpha^i) = \sigma\Big(\alpha^i_0 + \sum_{j=1}^{i-1} \alpha^i_j x_j\Big).

这叫 FVSBN(fully visible sigmoid belief network:所有变量都是可见的、用 sigmoid 连接的信念网络)。

  • 怎样算 p(x1,…,x784)p(x_1, \dots, x_{784})? 把所有条件乘起来。slide 7 的例子:p(X1=0,X2=1,X3=1,X4=0)=(1−x^1)×x^2×x^3×(1−x^4)p(X_1 = 0, X_2 = 1, X_3 = 1, X_4 = 0) = (1 - \hat{x}_1) \times \hat{x}_2 \times \hat{x}_3 \times (1 - \hat{x}_4),其中每个 x^i\hat{x}_i 都用已知的前缀算出:x^2=x^2(X1=0)\hat{x}_2 = \hat{x}_2(X_1 = 0),x^3=x^3(X1=0,X2=1)\hat{x}_3 = \hat{x}_3(X_1 = 0, X_2 = 1),x^4=x^4(X1=0,X2=1,X3=1)\hat{x}_4 = \hat{x}_4(X_1 = 0, X_2 = 1, X_3 = 1)。
  • 怎样采样? 先 xˉ1∼p(x1)\bar{x}_1 \sim p(x_1)(np.random.choice([1, 0], p=[x̂₁, 1 − x̂₁])),再 xˉ2∼p(x2∣x1=xˉ1)\bar{x}_2 \sim p(x_2 \mid x_1 = \bar{x}_1),再 xˉ3∼p(x3∣x1=xˉ1,x2=xˉ2)\bar{x}_3 \sim p(x_3 \mid x_1 = \bar{x}_1, x_2 = \bar{x}_2)……
  • 有多少参数? 1+2+3+⋯+n≈n2/21 + 2 + 3 + \dots + n \approx n^2 / 2。

代码 01 第 1、2 部分把这些算了一遍:n=64n = 64 时 2,080 个参数,n=784n = 784 时 307,720 个(完整的表要 1023610^{236})。随机取一组 α\alpha 的 4 像素例子:

像素条件x^i\hat{x}_i这一项
x1x_1(无)0.5221−x^=0.4781 - \hat{x} = 0.478
x2x_2x1=0x_1 = 00.413x^=0.413\hat{x} = 0.413
x3x_3x1=0,x2=1x_1 = 0, x_2 = 10.455x^=0.455\hat{x} = 0.455
x4x_4x1=0,x2=1,x3=1x_1 = 0, x_2 = 1, x_3 = 10.7581−x^=0.2421 - \hat{x} = 0.242

乘起来 p(0,1,1,0)=0.02181p(0, 1, 1, 0) = 0.02181;对全部 16 种取值求和恰好是 1——任何 α\alpha 都如此。注意两个过程的不对称:求概率时,所有 x^i\hat{x}_i 都只依赖已知的 xx,可以一次并行算完;采样时,必须先抽出 x1x_1 才能算 x^2\hat{x}_2,只能一个一个来。这个不对称贯穿所有自回归模型。

2.2 在像素世界上训练 FVSBN

代码 01 第 3 部分(64 个逻辑回归的系数放进一个 64×6464 \times 64 的严格下三角矩阵,一次矩阵乘法算完所有条件):

模型参数个数测试:比真实多 bit训练:比真实多 bit
FVSBN(64 个逻辑回归)2,0804.654.35
贝叶斯网络:独立像素649.9810.02
贝叶斯网络:左 + 左上 + 上 + 右上7715.265.13
贝叶斯网络:左 2 + 上 2 + 两个对角2,5753.643.31

(贝叶斯网络是第 2 讲的 PixelBN,用同样的 1 万张训练图。)

FVSBN 胜过四邻居的表,却输给了六邻居的表。这说明了两种表示各自的代价:表能表示邻居之间的任意组合(“左边两个都黑”和”只有左边一个黑”可以给完全不同的概率),逻辑回归只能让每个像素各自线性地”投票”。FVSBN “看得远”(以前面所有像素为条件),局部的表”看得细”——下一步自然是两者兼得:看得远、又能表示组合。这就是神经网络条件分布要做的事。

2.3 FVSBN 学到了什么

条件概率 x^i\hat{x}_i。 代码 01 第 5 部分把一张测试图上每个像素的 x^i\hat{x}_i 画成深浅图(原课 slide 11 右图对 NADE 做了同样的事):笔画的第一个像素很难预测(x^\hat{x} 很浅——在它之前,模型不知道形状会从哪里开始);笔画开始以后,后面像素的 x^\hat{x} 明显升高(约 0.4–0.6),但并不果断——线性模型没法确定”这是一条横线、还要再画 2 个像素”。

系数。 第 6 部分打印了像素 (4,4) 那个逻辑回归的系数:最大的正系数在紧挨着的左边 (4,3)(+1.4)和上边 (3,4)(+1.2);隔一个的 (4,2)、(2,4) 也是正的(+0.8、+1.0);而正好隔 4 个的 (4,0)、(0,4) 是负的(−0.7、−0.8)。如果 4 个像素之前就开始了一条线,长度为 4 的线到 (4,3) / (3,4) 就已经结束了——模型用线性的方式学到了一点”线长 4 个像素”的规律。没有人告诉它”要看邻居”,它从数据里学到了局部性。

样本里能看到横线、竖线、方框的片段,但常常画不完整或画过头。原课 slide 8 展示的 FVSBN 样本(Caltech 101 剪影)也是这样:大致的形状对了,细节模糊。


3. NADE、MADE 与自编码器 · NADE, MADE and autoencoders

3.1 NADE:单隐藏层,加上权重共享

原课第 9 页:为了改进模型,用单隐藏层的神经网络代替逻辑回归:

hi=σ(Aix<i+ci),x^i=p(xi∣x1,…,xi−1;Ai,ci,αi,bi)=σ(αihi+bi).h_i = \sigma(A_i x_{<i} + c_i), \qquad \hat{x}_i = p(x_i \mid x_1, \dots, x_{i-1}; A_i, c_i, \alpha_i, b_i) = \sigma(\alpha_i h_i + b_i).

如果每个条件都有自己的 Ai,ciA_i, c_i,参数会比 FVSBN 还多。第 10 页的关键一步是权重共享(tie weights):所有条件共用同一个矩阵 WW 和偏置 cc,第 ii 个条件只用 WW 的前 i−1i - 1 列:

hi=σ(W⋅,<i x<i+c),x^i=σ(αihi+bi).h_i = \sigma(W_{\cdot, <i}\, x_{<i} + c), \qquad \hat{x}_i = \sigma(\alpha_i h_i + b_i).

这叫 NADE(Neural Autoregressive Density Estimation,Larochelle & Murray 2011)。如果 hi∈Rdh_i \in \mathbb{R}^d,参数一共有多少?W∈Rd×nW \in \mathbb{R}^{d \times n}、c∈Rdc \in \mathbb{R}^d,再加 nn 个逻辑回归系数向量 αi,bi∈Rd+1\alpha_i, b_i \in \mathbb{R}^{d+1}——线性于 nn。概率在 O(nd)O(nd) 时间内算完。

为什么是 O(nd)O(nd)?因为权重共享让隐藏层的输入可以递推:记 ai=c+W⋅,<ix<ia_i = c + W_{\cdot, <i} x_{<i},则

ai+1=ai+W⋅,i xi,a_{i+1} = a_i + W_{\cdot, i}\, x_i,

从 aia_i 到 ai+1a_{i+1} 只要加一列。代码 02 用累加和(cumsum)一次算出全部 aia_i,和”对每个 ii 单独算 hih_i“的朴素写法比较,logit 的最大差是 9.5×10−79.5 \times 10^{-7}(float32 的舍入误差)。参数个数(d=64d = 64):

像素数 nnFVSBN:n(n+1)/2n(n+1)/2NADE:2dn+d+n2dn + d + n
642,0808,320
784307,720101,200
30724,720,128396,352

n=64n = 64 时 NADE 反而更多,nn 一大就反过来了。

一个关于速度的小插曲:代码 02 第 4 部分对 1000 张图计时,NADE 的累加和写法(O(nd)O(nd))并不比朴素写法(O(n2d)O(n^2 d),64 次小的矩阵乘法)快,甚至更慢(两者都只要几毫秒,具体数字每次运行不同)。原因是累加和写法要建一个 1000×64×641000 \times 64 \times 64 的中间张量,时间花在内存读写上,而矩阵乘法在 CPU 上被高度优化。“运算量少”不等于”跑得快”——在小规模上,常数和内存访问才是主角。

3.2 一般的离散分布与连续变量

非二值的离散变量(原课第 12 页),例如取 0–255 的像素亮度:让 x^i\hat{x}_i 参数化一个类别分布,

hi=σ(W⋅,<ix<i+c),p(xi∣x<i)=Cat(pi1,…,piK),(pi1,…,piK)=softmax(Aihi+bi).h_i = \sigma(W_{\cdot, <i} x_{<i} + c), \qquad p(x_i \mid x_{<i}) = \text{Cat}(p_i^1, \dots, p_i^K), \qquad (p_i^1, \dots, p_i^K) = \text{softmax}(A_i h_i + b_i).

softmax 把 sigmoid 推广到 KK 类:softmax(a)k=exp⁡(ak)/∑jexp⁡(aj)\text{softmax}(a)_k = \exp(a_k) / \sum_j \exp(a_j),把 KK 个任意实数变成 KK 个非负、和为 1 的概率(numpy:np.exp(a) / np.sum(np.exp(a)))。

连续变量(第 13–14 页),例如语音信号:让 x^i\hat{x}_i 参数化一个连续分布,例如 KK 个高斯的均匀混合,

p(xi∣x<i)=∑j=1K1K N(xi;μij,σij),x^i=(μi1,…,μiK,σi1,…,σiK)=f(hi),p(x_i \mid x_{<i}) = \sum_{j=1}^K \frac{1}{K}\,\mathcal{N}(x_i; \mu_i^j, \sigma_i^j), \qquad \hat{x}_i = (\mu_i^1, \dots, \mu_i^K, \sigma_i^1, \dots, \sigma_i^K) = f(h_i),

标准差用 exp⁡(⋅)\exp(\cdot) 保证非负。这叫 RNADE(Uria et al. 2013)。注意这里又出现了第 2 讲 §6 的思想:每个条件分布形式简单(高斯混合),乘起来的联合分布却可以非常复杂。

3.3 自回归模型 vs 自编码器

原课第 15 页:表面上看,FVSBN 和 NADE 很像一个自编码器(autoencoder):

  • 编码器 e(⋅)e(\cdot),例如 e(x)=σ(W2(W1x+b1)+b2)e(x) = \sigma(W^2(W^1 x + b^1) + b^2);
  • 解码器 dd,使得 d(e(x))≈xd(e(x)) \approx x,例如 d(h)=σ(Vh+c)d(h) = \sigma(Vh + c);
  • 损失:二值变量用 ∑x∈D∑i−xilog⁡x^i−(1−xi)log⁡(1−x^i)\sum_{x \in \mathcal{D}} \sum_i -x_i \log \hat{x}_i - (1 - x_i)\log(1 - \hat{x}_i),连续变量用 ∑(xi−x^i)2\sum (x_i - \hat{x}_i)^2。

编码器和解码器被加了限制(例如中间层很窄),不让它学到恒等映射;我们希望 e(x)e(x) 是 xx 的一个有意义的压缩表示(特征学习)。但是:普通的自编码器不是生成模型——它没有定义一个可以从中采样的 xx 上的分布。损失函数看起来像对数似然,可 x^i\hat{x}_i 依赖于整个 xx,包括 xix_i 自己;把这些”概率”乘起来并不构成任何分布。

3.4 MADE:带掩码的自编码器

原课第 16 页:能不能从自编码器得到一个生成模型?需要保证它对应一个合法的贝叶斯网络(DAG 结构),即需要一个链式法则的顺序。如果顺序是 1, 2, 3:x^1\hat{x}_1 不能依赖任何输入(于是生成时不需要任何输入就能开始);x^2\hat{x}_2 只能依赖 x1x_1;……额外的好处:可以用一个神经网络(nn 个输入、nn 个输出)一次前向就产生所有 x^i\hat{x}_i,而 NADE 需要 nn 次——在现代硬件上效率高得多。

MADE(Masked Autoencoder for Distribution Estimation,Germain et al. 2015,第 17 页)的办法是用掩码禁止某些路径:

  1. 给每个输入一个编号:它在顺序里排第几(顺序 x2,x3,x1x_2, x_3, x_1 时,x2x_2 的编号是 1、x3x_3 是 2、x1x_1 是 3)。
  2. 每个隐藏单元随机取一个整数 m∈[1,n−1]m \in [1, n-1](更准确地说,在 [上一层的最小编号, n−1n - 1] 里取):这个单元最多只能依赖前 mm 个输入。
  3. 加掩码维持这个不变量:隐藏层的单元只连接上一层中编号小于等于它的单元;输出层严格小于。

代码 02 第 2 部分打印了 slide 17 的例子(3 个输入、顺序 x2,x3,x1x_2, x_3, x_1、两层各 4 个隐藏单元):

  输入编号:x₁ → 3,x₂ → 1,x₃ → 2
  第 1 隐藏层的编号:[1, 1, 2, 2]      第 2 隐藏层的编号:[1, 1, 1, 2]
  第 1 层掩码(列 = x₁ x₂ x₃):h1 [0,1,0]  h2 [0,1,0]  h3 [0,1,1]  h4 [0,1,1]
  输出层掩码:x̂1(m=3)[1,1,1,1]   x̂2(m=1)[0,0,0,0]   x̂3(m=2)[1,1,1,0]
  ⇒ x̂1 依赖 x₂、x₃;x̂2 不依赖任何输入;x̂3 只依赖 x₂

正是 p(x1,x2,x3)=p(x2) p(x3∣x2) p(x1∣x2,x3)p(x_1, x_2, x_3) = p(x_2)\,p(x_3 \mid x_2)\,p(x_1 \mid x_2, x_3) 要求的结构。为什么这个规则保证自回归性质?沿着任何一条从输入 jj 到输出 ii 的路径,编号”只增不减”(每层要求 ≥\geq),最后一步严格增加(要求 >>);所以只有编号小于 ii 的输入能到达输出 ii。输出层为什么必须严格大于?如果允许等于,编号为 mm 的隐藏单元可能看到第 mm 个输入,而第 mm 个输出又连接它——输出就”看到了自己”。代码对 64 像素的 MADE(2 × 256 隐藏单元)用雅可比矩阵 ∂logiti/∂xj\partial \text{logit}_i / \partial x_j 做了穷举检查:违反自回归性质的 (i,j)(i, j) 对为 0。

3.5 实验:NADE、MADE 和 FVSBN

模型参数个数测试:比真实多 bit训练:比真实多 bit
FVSBN(逻辑回归)2,0804.654.35
NADE(d=64d = 64,权重共享)8,3203.893.46
MADE(2 × 256,掩码)50,4563.712.65

NADE 把每个条件从逻辑回归换成单隐藏层网络,测试误差从 4.65 降到 3.89 bit:隐藏层能表示像素之间的组合。MADE 参数多得多,测试误差 3.71 bit;它的训练误差(2.65)明显低于测试误差——1 万张图对 5 万个参数来说开始不够了(第 4 讲的过拟合)。三者都是同一个链式法则,区别只在”条件分布用什么函数表示”。


4. 越来越长的历史:RNN 与 Transformer · RNNs and Transformers

4.1 RNN:用一个摘要概括历史

原课第 18 页:要建模 p(xt∣x1:t−1;αt)p(x_t \mid x_{1:t-1}; \alpha^t),难点是”历史” x1:t−1x_{1:t-1} 越来越长。想法:保留一个摘要,递推地更新它:

ht+1=tanh⁡(Whhht+Wxhxt+1),ot+1=Whyht+1,h0=b0.h_{t+1} = \tanh(W_{hh} h_t + W_{xh} x_{t+1}), \qquad o_{t+1} = W_{hy} h_{t+1}, \qquad h_0 = b_0.
  1. 隐藏层 hth_t 是到时刻 tt 为止看到的输入的摘要;
  2. 输出层 ot−1o_{t-1} 给出条件分布 p(xt∣x1:t−1)p(x_t \mid x_{1:t-1}) 的参数;
  3. 参数是 b0b_0(初始化)和三个矩阵 Whh,Wxh,WhyW_{hh}, W_{xh}, W_{hy}——参数个数和 nn 无关!

4.2 字符 RNN

原课第 19 页(来自 Karpathy):设 xi∈{h,e,l,o}x_i \in \{h, e, l, o\},用 one-hot 编码。自回归地,

p(x=hello)=p(x1=h) p(x2=e∣x1=h) p(x3=l∣x1=h,x2=e)⋯p(x5=o∣x1=h,x2=e,x3=l,x4=l).p(x = \text{hello}) = p(x_1 = h)\,p(x_2 = e \mid x_1 = h)\,p(x_3 = l \mid x_1 = h, x_2 = e) \cdots p(x_5 = o \mid x_1 = h, x_2 = e, x_3 = l, x_4 = l).

看到 hh 以后输出层 o1=(1.0,2.2,−3.0,4.1)o_1 = (1.0, 2.2, -3.0, 4.1)(对应 h, e, l, o),于是

p(x2=e∣x1=h)=softmax(o1)e=exp⁡(2.2)exp⁡(1.0)+exp⁡(2.2)+exp⁡(−3.0)+exp⁡(4.1)=0.1251.p(x_2 = e \mid x_1 = h) = \text{softmax}(o_1)_e = \frac{\exp(2.2)}{\exp(1.0) + \exp(2.2) + \exp(-3.0) + \exp(4.1)} = 0.1251.

(这个 RNN 最想接的是 o:p(o∣h)=0.8365p(o \mid h) = 0.8365——它还没学好。)代码 03 第 1 部分还把一个随机初始化的小 RNN 处理”hello”的整个过程打印了出来:每一步隐藏向量怎样更新、条件概率是多少、最后相乘得到 p(hello)=4.5×10−4p(\text{hello}) = 4.5 \times 10^{-4}。

slides 第 21–24 页展示了在莎士比亚全集、维基百科、婴儿名字上训练的字符 RNN 的样本:生成是一个字符一个字符进行的,模型必须自己学会合法的单词、语法、标点,甚至维基百科的 Markdown 语法(成对出现的 [[ 和 ]])。

4.3 RNN 的优缺点

原课第 20、25 页:

优点:可以用于任意长度的序列;非常一般——对每个可计算的函数,都存在一个有限大小的 RNN 能计算它。

缺点:

  1. 仍然需要一个顺序;
  2. 似然的计算是串行的(训练很慢);
  3. 生成是串行的(这在自回归模型里无法避免);
  4. 一个隐藏向量要概括全部(不断增长的)历史——例如 h(4)h^{(4)} 要概括 “My friend opened the” 的意思;
  5. 串行计算,无法并行;
  6. 从很多步之前取信息时,梯度会爆炸或消失。

4.4 注意力与生成式 Transformer

原课第 26 页:注意力机制拿一个查询向量和一组键向量比较:

  1. 拿当前的隐藏状态(查询)和过去所有的隐藏状态(键)比较,例如取点积;
  2. 构造一个注意力分布,判断历史的哪些部分相关,例如用 softmax;
  3. 构造历史的摘要,例如加权求和;
  4. 用这个摘要和当前状态预测下一个 token / 词。

第 27 页:当前最先进的模型(GPT)用 Transformer 代替 RNN:用注意力机制自适应地只关注相关的上下文;避免递推计算,只用自注意力,从而可以并行;需要带掩码的自注意力来保持自回归结构(位置 tt 不能看位置 >t> t)。

代码 03 的 TransformerAR 是一个 2 层、64 维、4 个头的 GPT 缩小版:输入序列是 [开始符,x1,…,x63][\text{开始符}, x_1, \dots, x_{63}](整体右移一位),位置 tt 预测 xtx_t;每个位置的输入 = 取值的嵌入 + 可学的位置嵌入。

4.5 实验:RNN、GRU、Transformer

代码 03 第 2 部分(同一份 1 万张训练图;GRU 是带门控的 RNN,门控让它更容易记住几十步之前的信息、缓解梯度消失):

模型参数个数测试:比真实多 bit训练:比真实多 bit训练用时(参考)
FVSBN2,0804.654.35不到 1 秒
NADE8,3203.893.46几秒
MADE50,4563.712.65几秒
RNN(tanh,128 维)17,0250.910.8216 秒
GRU(96 维)28,8970.830.7231 秒
Transformer(2 层,64 维)104,4491.561.2950 秒

这是本讲最出乎我意料的结果:普通的 tanh RNN 和 GRU 是最好的模型,比 MADE 好近 3 bit,也比 Transformer 好。

  • RNN 按光栅顺序一个像素一个像素地更新隐藏状态,像一台有限状态机:可以数”这一行已经画了几个黑像素”、记住”上一行的这个位置有没有笔画”——这正是像素世界的结构。
  • Transformer 在训练集上的误差本身就比 GRU 差得多(1.29 vs 0.72),说明主要问题不是过拟合,而是在这个训练预算下(2500 步、1 万张图)还没学到位:它要从 64 个可学的位置嵌入里自己发现二维网格的结构,而 RNN 的”顺序递推”天生就带着这种归纳偏置。我们没有试更长的训练,结论只针对这里的设置(练习 8)。

“Transformer 一定最好”不是定律。架构是一种先验(第 1 讲 §2.2):在大数据、大模型、长序列上,Transformer 的并行性和灵活的注意力胜出;在小而规则的序列上,RNN 的偏置可能恰好更合适。

误差花在哪里? 第 3 部分按行统计了每个像素的条件 bits(每一行 8 个像素的 −log⁡2p(xi∣x<i)-\log_2 p(x_i \mid x_{<i}) 之和,在测试集上平均):

行FVSBNNADERNNGRUTransformer
第 0 行3.243.253.203.193.19
第 2 行4.084.023.683.673.75
第 4 行4.294.173.613.613.74
第 6 行3.493.332.832.802.98
合计(8 行)30.1729.4126.4326.3527.08

第 0 行各模型几乎一样:没有历史可用时,谁也没有优势。差距主要在第 2–5 行(每行约 0.5 bit):形状的中间部分需要”记住上面画了什么”。最后两行大家都变便宜——形状通常已经画完,下面多半是空白。

注意力在看哪里? 第 4 部分把 Transformer 最后一层(4 个头平均)的注意力画了出来。我原以为预测竖笔画中间的像素时,注意力会集中在正上方;实际上两个例子里,最后一层的注意力都主要落在同一行左边的几个像素和开始符上(例如预测 (2,2) 时,(2,0) 0.27、(1,1) 0.14、(2,1) 0.14,开始符 0.21)。一种可能的解释是它在判断”这一行的笔画从哪里开始、已经画了多长”,而”上方有没有笔画”可能由第一层处理。注意力图只是一个线索,不能当作模型”怎样思考”的完整解释。

样本:GRU 的样本是到目前为止最像真实数据的——完整的方框、十字、长度为 4 的线,还带着和真实数据一样的零星噪声像素。但无论 RNN 还是 Transformer,生成都仍然是 64 步串行的:Transformer 解决的是”训练时的并行”和”远距离依赖”,不是”采样慢”。


5. 图像上的自回归模型:PixelRNN、PixelCNN、WaveNet · Autoregressive models for images and audio

5.1 PixelRNN

原课第 28–29 页(van den Oord et al. 2016):按光栅顺序逐像素建模图像;每个像素的条件分布要给出 3 个颜色,再用链式法则分解成

p(xt∣x1:t−1)=p(xtred∣x1:t−1) p(xtgreen∣x1:t−1,xtred) p(xtblue∣x1:t−1,xtred,xtgreen),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 类的类别分布;条件分布用 RNN 的变体(LSTM + 类似 MADE 的掩码)建模。在下采样的 ImageNet 上效果很好,但非常慢:似然要串行计算。

5.2 PixelCNN 与掩码卷积

原课第 30–32 页:卷积天然适合图像数据,而且在现代硬件上容易并行。PixelCNN(van den Oord et al. 2016)的想法:用卷积架构,根据上下文(一个像素邻域)预测下一个像素。挑战是必须保持自回归结构:掩码卷积保持光栅顺序(彩色图还要额外的掩码处理颜色的顺序)。在 32×32 的 ImageNet 上,PixelCNN 和 PixelRNN 表现相近,但快得多。

代码 04 第 1 部分打印了两种掩码:

  A 型(5×5)        B 型(3×3)
  1 1 1 1 1          1 1 1
  1 1 1 1 1          1 1 0
  1 1 0 0 0          0 0 0
  0 0 0 0 0
  0 0 0 0 0

保留中心上方的所有行,以及中心所在行的左边——正是光栅顺序里”之前”的像素。第一层用 A 型(连中心也不看,否则就看到了要预测的像素);后面的层用 B 型(可以看中心:那个位置存的已经是”只依赖之前像素”的特征)。

5.3 盲点

堆叠的掩码卷积能看到所有之前的像素吗?代码 04 第 3 部分用梯度精确地画出了训练好的 PixelCNN(1 层 5×5 A 型 + 4 层 3×3 B 型)的感受野(██ = 看得到的之前的像素,░░ = 之前但看不到的像素,<> = 目标):

  目标 (6,1):
    ████████████████
    ████████████████
    ██████████████░░
    ████████████░░░░
    ██████████░░░░░░
    ████████░░░░░░░░
    ██<>· · · · · ·

右上方有一块看不到的三角形区域:目标 (6,1) 之前的 49 个像素里有 10 个看不到(目标 (4,4) 只有 1 个看不到)。这就是原始 PixelCNN 著名的盲点(blind spot):B 型掩码只允许信息”向右下”传播,堆叠几层以后,感受野是一个缺了右上角的三角形。在 8×8 的小画布上,5 层卷积的感受野已经覆盖了几乎整张图,看不到的全是盲点里的像素;在大图上,感受野有限还会让远处的像素也看不到。Gated PixelCNN(van den Oord et al. 2016b)用”竖直栈 + 水平栈”两路卷积消除了盲点。

实验结果(代码 04 第 2 部分):PixelCNN 只有 21,057 个参数(卷积核在所有位置共享),测试误差 0.84 bit,训练误差 0.66——和最好的 GRU(0.83)不相上下,好于 Transformer。我原以为盲点会让它明显吃亏;实际上像素世界的形状最大只有 4×4,预测一个像素主要靠附近的像素,局部的卷积恰好是对路的先验(平移等变:同一种笔画在任何位置都用同一个卷积核识别)。训练时一次卷积就算出全部 64 个条件;采样仍然要 64 次前向计算。

5.4 WaveNet

原课第 35 页(van den Oord et al. 2016):非常有效的语音模型。语音每秒有上万个采样点,要看到足够长的历史,普通卷积的感受野增长太慢。扩张卷积(dilated convolution)让卷积核每隔 2d2^d 个位置才接触一次信号:第 dd 层的扩张率是 2d2^d,感受野随层数指数增长。WaveNet 是因果的(只看过去),所以同样是一个自回归模型;它的采样同样是逐个采样点进行的——这直接导致了第 8 讲的 Parallel WaveNet(用流模型把采样并行化)。


6. 应用与总结 · Applications and summary

6.1 PixelDefend:用似然检测对抗样本

原课第 33–34 页:机器学习方法容易受到对抗样本的攻击。能不能检测它们?PixelDefend(Song et al. 2018):在干净的输入上训练一个生成模型 p(x)p(x)(PixelCNN);对一个新输入算 p(x)p(x);对抗样本在 p(x)p(x) 下的概率显著更低。

代码 05 第 3 部分在像素世界上做了同样的事(异常输入和第 1 讲代码 04 相同):

异常输入AUC:真实模型AUC:GRUAUC:FVSBN
噪声更大(ε = 15%)0.9710.9670.947
随机噪声图1.0001.0001.000
世界里没有的形状:对角线0.8960.8630.728
两个形状叠在一起0.8910.8810.776
黑白反色1.0001.0001.000
空白画布 + 噪声0.6160.4800.209

GRU 的 AUC 几乎和真实模型一样:学得越接近真实分布,异常检测就越接近”理论上能做到的程度”;FVSBN 在需要理解结构的异常上差得多。“空白画布”是三者共同的盲区,而且模型越差,越偏爱空白画布(0.616 → 0.480 → 0.209)。即使是完美的模型也抓不住它:问题不只在模型学得好不好,还在”用似然检测异常”这个方法本身(第 1 讲 §4.4、第 15 讲)。

6.2 排行榜

代码 05 第 1 部分:

模型参数个数测试:比真实多 bit/图测试 bits/dim
FVSBN2,0804.650.4714
NADE8,3203.890.4595
MADE50,4563.710.4568
RNN17,0250.910.4130
GRU28,8970.830.4116
Transformer104,4491.560.4231
PixelCNN21,0570.840.4118
(真实分布)00.3987

参照:第 1 讲的伯努利混合(N=10000N = 10000)多 4.62 bit,第 2 讲最好的局部贝叶斯网络(同一份数据)多 3.64 bit。同一个链式法则,条件分布的表示方式决定了一切:从 FVSBN 的 4.65 bit 到 GRU / PixelCNN 的约 0.8 bit。最好的几个模型离真实分布已经不到 1 bit/图(每个像素约 0.013 bit)——自回归模型是精确似然的”优等生”。

6.3 顺序重要吗?

代码 05 第 2 部分:FVSBN 和 MADE 分别用光栅顺序、列优先顺序、随机顺序训练,每种设置训练 3 次(不同的随机排列 / 不同的 MADE 掩码):

模型光栅顺序列优先随机顺序
FVSBN4.65(4.65–4.66)4.65(4.65–4.66)4.65(4.64–4.65)
MADE3.74(3.71–3.79)3.71(3.64–3.75)3.30(3.30–3.31)

理论上,如果每个条件分布都足够灵活(例如一张完整的表),任何顺序都能表示任何分布——链式法则对任何顺序都成立。条件分布被限制在某个函数族里时,顺序可能变得重要。实际结果:FVSBN 对顺序几乎不敏感;MADE 在随机顺序下反而更好(三次都是 3.30),和我预想的”随机顺序最差”相反。一个可能的解释是:光栅顺序的前几十个像素(上面几行)通常是空白,携带的信息很少;随机顺序下,前面几个像素散布在整张图上,很快就告诉模型”形状大概在哪里”,后面的条件因此更容易预测。无论如何,自回归模型必须选一个顺序,怎样选本身就是一个建模决策。

6.4 容易的条件与难的条件

自回归模型精确地给出了 p(x)p(x),那它能回答所有概率问题吗?代码 05 第 4 部分用 GRU(光栅顺序)回答了两个问题,“上半张”是前 32 个像素:

(a) p(下半张∣上半张)p(\text{下半张} \mid \text{上半张}):顺着光栅顺序继续相乘,一次前向计算。模型 12.201 bit,真实分布 11.634 bit,差 0.567 bit——和整体的误差差不多。

(b) p(下半张)p(\text{下半张}):=∑上半张p(上) p(下∣上)= \sum_{\text{上半张}} p(\text{上})\,p(\text{下} \mid \text{上}),要对上半张的 232≈432^{32} \approx 43 亿种取值求和。只能用采样近似:从模型的 p(上)p(\text{上}) 抽 KK 个上半张,取 p^(下)=1K∑kp(下∣上k)\hat{p}(\text{下}) = \frac{1}{K}\sum_k p(\text{下} \mid \text{上}_k):

上半张的样本数 KK估计 − 真实 log⁡2p(下)\log_2 p(\text{下}):平均最大最小
1−3.3834.705−17.801
10−0.6581.711−3.762
100−0.2681.158−1.950
1000−0.1711.171−1.748

(20 张测试图;“真实”是像素世界精确边缘化得到的 log⁡2p(下半张)\log_2 p(\text{下半张})。)

  • 顺着顺序的条件对自回归模型是免费的;逆着顺序的边缘 / 条件就难了,要对所有可能的”前面”求和。
  • 单张图的估计可能偏高也可能偏低,但平均偏低,这不是偶然:p^\hat{p} 本身是 pθ(下)p_\theta(\text{下}) 的无偏估计,可 log⁡\log 是凹函数,E[log⁡p^]≤log⁡E[p^]=log⁡pθ\mathbb{E}[\log \hat{p}] \leq \log \mathbb{E}[\hat{p}] = \log p_\theta(Jensen 不等式)。KK 越大越准,但每张图要采样、计算 KK 次。
  • K=1000K = 1000 时仍有约 −0.17 bit 的偏差:一部分是剩下的采样误差,一部分是模型本身的误差——估计收敛到的是模型的 pθ(下)p_\theta(\text{下}),不是真实的 p(下)p(\text{下})。

“从隐藏的部分采样、再对条件概率取平均”来估计边缘概率——这正是第 5 讲(隐变量模型的边际似然、重要性采样、ELBO)要处理的问题。

6.5 自回归模型小结(原课 slide 36)

  • 容易采样:先 xˉ0∼p(x0)\bar{x}_0 \sim p(x_0),再 xˉ1∼p(x1∣x0=xˉ0)\bar{x}_1 \sim p(x_1 \mid x_0 = \bar{x}_0)……(但要 nn 步串行)
  • 容易算概率:算出 p(x0=xˉ0)p(x_0 = \bar{x}_0)、p(x1=xˉ1∣x0=xˉ0)p(x_1 = \bar{x}_1 \mid x_0 = \bar{x}_0)……乘起来(对数相加);理想情况下这些项可以并行算出,训练才快。
  • 容易推广到连续变量:例如高斯条件分布 p(xt∣x<t)=N(μθ(x<t),Σθ(x<t))p(x_t \mid x_{<t}) = \mathcal{N}(\mu_\theta(x_{<t}), \Sigma_\theta(x_{<t})),或者 logistic 混合。
  • 没有天然的办法得到特征、聚类、做无监督学习:自回归模型没有隐变量 zz,“这张图是什么形状”这种高层描述不在任何地方显式地出现(第 5 讲的隐变量模型要解决它)。
  • 下一讲:学习。

7. 2023 年之后 · Since 2023

  • 自回归模型统治了语言。 GPT-4、Claude、Llama、Qwen……所有主流大语言模型都是 §4.4 的生成式 Transformer:p(x)=∏tpθ(xt∣x<t)p(x) = \prod_t p_\theta(x_t \mid x_{<t}),用最大似然在海量文本上训练。本仓库的 CS336、CS324 从工程和规模的角度详细讲了它们。
  • 自回归图像生成的复兴。 先用一个”图像分词器”(VQ-VAE / VQGAN,第 17 讲的离散隐变量)把图像变成离散 token,再用 Transformer 自回归地生成 token:DALL·E(2021)、Parti(2022),到 VAR(“预测下一个尺度”而不是下一个像素,NeurIPS 2024 最佳论文)。MAR(Li et al. 2024)则保留自回归的结构,但每个 token 是连续的、用一个小扩散模型建模条件分布——又是”条件分布形式灵活、联合分布由链式法则保证”的思路。2025 年起,GPT-4o 等多模态模型把文字和图像放进同一个自回归模型。
  • “采样慢”的对策。 自回归采样必须串行(§2.1),大语言模型的推理成本主要来自这里。投机解码(speculative decoding)用一个小模型先猜几个 token,再用大模型一次并行地验证,在不改变输出分布的前提下加速;KV 缓存避免每一步重复计算之前的位置(本讲代码为了简单没有做,每一步都重算整个序列)。
  • 顺序的问题。 §6.3 发现顺序会影响结果。“任意顺序”的自回归模型(MADE 原论文就提出了用多个随机顺序训练,类似的想法后来发展成 any-order / σ-GPT 等)可以在推理时选择顺序;第 18 讲的掩码扩散语言模型可以看成一种”一次生成多个、顺序不固定”的自回归模型,它正是为了摆脱”必须从左到右”而提出的。

8. 小结 · Summary

  1. 自回归模型 = 链式法则 + 带参数的条件分布。链式法则是恒等式,“条件分布的函数形式”才是假设。它的三大优点:合法性免费(不需要归一化常数)、似然精确可算、训练就是”预测下一个”的监督学习。
  2. FVSBN 每个条件一个逻辑回归,≈n2/2\approx n^2/2 个参数;求概率可以并行,采样只能串行。它”看得远”但只能线性投票,在像素世界上输给”看得细”的六邻居表(4.65 vs 3.64 bit)。
  3. NADE 用共享权重的单隐藏层网络,参数线性于 nn,hih_i 可以递推(O(nd)O(nd));推广到类别分布(softmax)和连续变量(RNADE)。普通的自编码器不是生成模型;MADE 用编号和掩码把自编码器变成合法的自回归模型,一次前向算出所有条件。
  4. RNN 用一个隐藏向量概括历史,参数和长度无关,但串行、梯度消失、“一个向量装下全部历史”;注意力按相关程度加权历史;生成式 Transformer 用带因果掩码的自注意力并行训练。在像素世界上,RNN / GRU(0.91 / 0.83 bit)反而胜过 Transformer(1.56 bit):架构是一种先验。
  5. PixelCNN 用 A / B 型掩码卷积保持光栅顺序,训练快、参数少,但有右上方的盲点;在像素世界上它和 GRU 不相上下(0.84 bit)。PixelRNN 好但慢;WaveNet 用扩张卷积扩大感受野。
  6. 优点与局限:采样容易但 nn 步串行;似然精确且可并行;必须选顺序(MADE 在随机顺序下反而更好);顺着顺序的条件容易,逆着顺序的条件需要对指数多的项求和(采样近似有 Jensen 偏差);没有天然的隐表示。学得好的模型做异常检测接近理论上限,但”简单的输入概率高”仍是似然方法的盲区。

9. 常见误区 · Common pitfalls

  1. “自回归模型对分布做了很强的假设,因为它用了链式法则。” 链式法则是恒等式,不是假设;假设在于条件分布的函数形式(逻辑回归、神经网络……)。如果条件分布足够灵活,自回归模型能表示任何分布。
  2. “自回归模型生成快,因为训练快。” 训练(求似然)可以一次并行算完所有条件;采样必须一个一个来,nn 个变量就要 nn 次串行计算。MADE、PixelCNN、Transformer 都只加速了前者。
  3. “自编码器的重构损失就是对数似然,所以自编码器是生成模型。” 普通自编码器的 x^i\hat{x}_i 依赖整个 xx(包括 xix_i 自己),这些”概率”乘起来不构成任何分布,也没法从中采样。必须加上自回归结构(MADE)才行。
  4. “MADE 的掩码随便设就行,只要输出不连到对应的输入。” 只断开”输入 ii → 输出 ii“的直接连接不够:信息可以经过隐藏单元绕过去。必须用编号规则(隐藏层 ≥\geq、输出层 >>)保证所有路径都满足顺序。
  5. “掩码卷积能看到所有之前的像素。” 堆叠的 B 型掩码卷积有右上方的盲点(代码 04:目标 (6,1) 之前的 49 个像素里 10 个看不到)。
  6. “Transformer 一定比 RNN 好。” 在像素世界的设置里(小数据、规则的短序列、有限的训练预算),GRU 比 Transformer 好 0.7 bit。架构的优劣取决于数据和规模。
  7. “自回归模型能精确算 p(x)p(x),所以任何条件概率都好算。” 只有”顺着顺序”的条件好算;p(下半张)p(\text{下半张}) 要对 2322^{32} 种上半张求和,只能用采样近似,而且 log⁡\log 估计平均偏低。
  8. “似然越高,异常检测就一定越好。” 学得越好的模型确实越接近理论上限,但理论上限本身有盲区(空白画布):完美的模型也抓不住”简单”的异常。

10. 练习 · Exercises

练习 1(参数计数) (a) 推导 FVSBN 的参数个数 n(n+1)/2n(n+1)/2。 (b) 一个灰度图(每个像素 256 个取值)的”类别分布版 FVSBN”:第 ii 个条件是一个 softmax 回归,以前面 i−1i - 1 个像素的数值为输入。参数有多少? (c) NADE(隐藏维数 dd)的参数个数为什么是 2dn+d+n2dn + d + n?对应 ar_models.NADE 里的哪几个张量?

参考答案

(a) 第 ii 个逻辑回归有 i−1i - 1 个系数加 1 个偏置,共 ii 个参数;∑i=1ni=n(n+1)/2\sum_{i=1}^n i = n(n+1)/2。

(b) 第 ii 个 softmax 回归有 256 个输出,每个输出有 i−1i - 1 个系数和 1 个偏置:256⋅i256 \cdot i 个参数(严格地说 softmax 有一个冗余自由度,可以减去一组)。合计 256⋅n(n+1)/2256 \cdot n(n+1)/2。(如果把每个前面的像素也 one-hot 编码成 256 维,系数还要再乘 256。)

(c) W∈Rd×nW \in \mathbb{R}^{d \times n}(共享的第一层,dndn)+ c∈Rdc \in \mathbb{R}^d(dd)+ V∈Rn×dV \in \mathbb{R}^{n \times d}(每个条件的输出权重 αi\alpha_i,ndnd)+ b∈Rnb \in \mathbb{R}^n(每个条件的输出偏置,nn)。对应代码里的 self.W、self.c、self.V、self.b。

练习 2(FVSBN 一定是合法的分布) 证明:对任意参数 α\alpha,FVSBN 的 ∑x∈{0,1}np(x)=1\sum_{x \in \{0,1\}^n} p(x) = 1。提示:从 xnx_n 开始求和。这个证明用到了 σ\sigma 的什么性质?换成任意函数 ff 还成立吗?

参考答案

∑xnp(xn∣x<n)=x^n+(1−x^n)=1\sum_{x_n} p(x_n \mid x_{<n}) = \hat{x}_n + (1 - \hat{x}_n) = 1,所以对 xnx_n 求和后乘积里最后一个因子消失;依次对 xn−1,…,x1x_{n-1}, \dots, x_1 求和,每次都同理。只用到了”每个条件分布本身是一个合法的分布”,即 x^i∈[0,1]\hat{x}_i \in [0, 1]。σ\sigma 保证了这一点;换成任意函数 ff 时,只要输出被限制在 [0,1][0, 1](或者对类别分布用 softmax)就仍然成立——这就是为什么自回归模型”合法性免费”。

练习 3(NADE 的递推) 写出 ai+1a_{i+1} 和 aia_i 的关系,证明 NADE 算出全部 nn 个条件的总运算量是 O(nd)O(nd)。朴素写法(每个 ii 单独算 W⋅,<ix<iW_{\cdot,<i} x_{<i})的运算量是多少?代码 02 的计时为什么和这个分析不一致?

参考答案

ai+1=c+W⋅,<i+1x<i+1=ai+W⋅,ixia_{i+1} = c + W_{\cdot, <i+1} x_{<i+1} = a_i + W_{\cdot, i} x_i,每次 O(d)O(d);再算 hi=σ(ai)h_i = \sigma(a_i) 和 αihi\alpha_i h_i 各 O(d)O(d)。总共 O(nd)O(nd)。朴素写法第 ii 个条件要 O(id)O(id),合计 O(n2d)O(n^2 d)。代码 02 里 n=64n = 64、d=64d = 64 都很小,运算量的差别被常数和内存访问淹没了:累加和写法要建一个 N×d×nN \times d \times n 的中间张量并多次读写,而 64 次小的矩阵乘法调用的是高度优化的 BLAS。复杂度分析描述的是 nn 很大时的趋势。

练习 4(MADE 的掩码) 4 个输入、顺序 x1,x2,x3,x4x_1, x_2, x_3, x_4、一个隐藏层,隐藏单元的编号是 [1,3,2,2,1][1, 3, 2, 2, 1]。(a) 写出输入层到隐藏层的掩码(5 × 4)和隐藏层到输出层的掩码(4 × 5)。(b) 输出 x^4\hat{x}_4 依赖哪些输入?x^3\hat{x}_3 呢?(c) 有没有输入完全没被用到?这会带来什么后果?

参考答案

(a) 输入编号 1,2,3,41, 2, 3, 4。隐藏单元 kk 连接输入 jj 当且仅当 m(k)≥jm(k) \geq j:编号 1 的单元只连 x1x_1;编号 3 的连 x1,x2,x3x_1, x_2, x_3;编号 2 的连 x1,x2x_1, x_2。输出 ii 连接隐藏单元 kk 当且仅当 i>m(k)i > m(k):x^1\hat{x}_1 不连任何单元;x^2\hat{x}_2 连编号 1 的两个单元;x^3\hat{x}_3 连编号 1、2 的四个单元;x^4\hat{x}_4 连全部 5 个。 (b) x^4\hat{x}_4 依赖 x1,x2,x3x_1, x_2, x_3(通过编号 3 的单元能到 x3x_3);x^3\hat{x}_3 依赖 x1,x2x_1, x_2。 (c) x4x_4 不连任何隐藏单元——它是顺序里的最后一个,本来就不需要被任何条件使用,这是正确的。但如果编号都偏小(例如没有一个隐藏单元的编号是 3),x^4\hat{x}_4 就看不到 x3x_3,模型会损失表达能力(虽然仍然合法)。所以 MADE 的编号要在 [min,n−1][\text{min}, n - 1] 里取,并且常常训练时换几组编号。

练习 5(盲点) 只用两层 3×3 的 B 型掩码卷积(第一层换成 3×3 的 A 型)。对目标像素 (r,c)(r, c),第二层输出能看到哪些输入像素?画出来,指出盲点。

参考答案

A 型 3×3 让第一层在 (r′,c′)(r', c') 的特征看到偏移 {(−1,−1),(−1,0),(−1,1),(0,−1)}\{(-1,-1), (-1,0), (-1,1), (0,-1)\} 处的输入;B 型 3×3 让第二层在 (r,c)(r, c) 看到第一层偏移 {(−1,−1),(−1,0),(−1,1),(0,−1),(0,0)}\{(-1,-1), (-1,0), (-1,1), (0,-1), (0,0)\} 处的特征。目标能看到的输入偏移就是两组偏移所有可能的和:第 r−2r - 2 行的 c−2,…,c+2c - 2, \dots, c + 2;第 r−1r - 1 行的 c−2,…,c+1c - 2, \dots, c + 1;第 rr 行的 c−2,c−1c - 2, c - 1。第 r−1r - 1 行的 c+2c + 2 在光栅顺序里排在目标之前、也在 5×5 的范围内,却看不到——这就是盲点的雏形。层数越多,缺失的区域越大,形成右上方的三角形(对照代码 04 的图)。

练习 6(顺序与表达能力) (a) 证明:如果每个条件分布都用完整的表表示,对任意顺序,自回归模型都能精确表示任意分布。 (b) 条件分布限制为逻辑回归(FVSBN)。两个二值变量时,顺序有没有影响?三个呢?提示:把 log⁡p(x1,x2,x3)\log p(x_1, x_2, x_3) 写成对数线性展开 λ0+∑iλixi+∑i<jλijxixj+λ123x1x2x3\lambda_0 + \sum_i \lambda_i x_i + \sum_{i<j} \lambda_{ij} x_i x_j + \lambda_{123} x_1 x_2 x_3。

参考答案

(a) 链式法则对任何顺序都成立:p(x)=∏ip(xπ(i)∣xπ(<i))p(x) = \prod_i p(x_{\pi(i)} \mid x_{\pi(<i)}),而完整的表能表示任何条件分布。

(b) 两个变量时,p(x1)p(x_1) 是任意的伯努利,p(x2∣x1)=σ(α0+α1x1)p(x_2 \mid x_1) = \sigma(\alpha_0 + \alpha_1 x_1) 的两个参数恰好能表示任意的两行 CPT,所以任何顺序都能表示任何分布。三个变量时,前两个因子仍然不受限制;最后一个因子 logit p(x3=1∣x1,x2)=log⁡p(x1,x2,1)−log⁡p(x1,x2,0)=λ3+λ13x1+λ23x2+λ123x1x2\text{logit}\,p(x_3 = 1 \mid x_1, x_2) = \log p(x_1, x_2, 1) - \log p(x_1, x_2, 0) = \lambda_3 + \lambda_{13} x_1 + \lambda_{23} x_2 + \lambda_{123} x_1 x_2,它是逻辑回归的形式当且仅当 λ123=0\lambda_{123} = 0。所以 FVSBN 的分布族 = “三阶交互项为 0 的分布”。三阶交互项 λ123\lambda_{123} 对三个变量是对称的,和谁排在最后无关——三个变量时顺序也没有影响(异或 x3=x1⊕x2x_3 = x_1 \oplus x_2 正是一个 λ123≠0\lambda_{123} \neq 0 的分布,任何顺序都表示不了)。四个变量起,中间的条件分布也受限制,而且限制的是边缘分布的交互项,不同顺序的分布族一般不同。代码 05 里 FVSBN 对顺序几乎不敏感,和这个分析的精神一致:线性条件分布的”缺陷”在很大程度上是对称的。

练习 7(Jensen 偏差) 代码 05 的 p^(下)=1K∑kp(下∣上k)\hat{p}(\text{下}) = \frac{1}{K}\sum_k p(\text{下} \mid \text{上}_k)。(a) 证明 E[p^]=pθ(下)\mathbb{E}[\hat{p}] = p_\theta(\text{下})。(b) 证明 E[log⁡p^]≤log⁡pθ(下)\mathbb{E}[\log \hat{p}] \leq \log p_\theta(\text{下})。(c) K=1K = 1 时,“估计 − 真实”的最小值是 −17.8 bit,这意味着什么?

参考答案

(a) E上∼pθ(上)[pθ(下∣上)]=∑上pθ(上) pθ(下∣上)=pθ(下)\mathbb{E}_{\text{上} \sim p_\theta(\text{上})}[p_\theta(\text{下} \mid \text{上})] = \sum_{\text{上}} p_\theta(\text{上})\,p_\theta(\text{下} \mid \text{上}) = p_\theta(\text{下}),KK 个独立样本的平均也一样。 (b) log⁡\log 是凹函数,由 Jensen 不等式 E[log⁡p^]≤log⁡E[p^]=log⁡pθ(下)\mathbb{E}[\log \hat{p}] \leq \log \mathbb{E}[\hat{p}] = \log p_\theta(\text{下})。 (c) 抽到的那个上半张和观察到的下半张”不配”(例如下半张是一个方框的下半部分,而抽到的上半张是空白),p(下∣上)p(\text{下} \mid \text{上}) 极小。大多数样本的贡献都很小,真正有贡献的上半张很少被抽到——这就是第 5 讲说的”朴素蒙特卡洛几乎所有样本都贡献为零”,改进的办法是从一个更好的分布(接近 p(上∣下)p(\text{上} \mid \text{下}))里抽样:重要性采样。

练习 8(动手:让 Transformer 训练得更久) 把 ar_models.CONFIGS["transformer"] 的 steps 从 2500 改成 10000,删掉 checkpoints/transformer.pt,重新运行代码 03。这会运行约 3–4 分钟(超过本讲义”2 分钟”的约定,所以作者没有运行,留给你)。预测:Transformer 会不会追上 GRU?训练误差和测试误差会怎样变化?

参考答案(预测的思路)

如果 Transformer 落后的原因是”没学到位”(训练误差和测试误差很接近、都偏高),更长的训练应该会继续降低两者,可能缩小和 GRU 的差距;如果主要原因是”从位置嵌入里学二维结构需要更多数据”,那么只加训练步数、不加数据,训练误差会下降,但测试误差下降得少,两者的差距会拉大(开始过拟合)。具体结果以你的运行为准。做完记得把 steps 改回 2500(或者删掉新的缓存),否则代码 04、05 读到的是你训练的版本。

练习 9(思考:自回归模型的”特征”) slide 36 说自回归模型”没有天然的办法得到特征”。可是 GRU 的隐藏向量 h64h_{64} 不正是整张图的一个摘要吗?它和第 5–6 讲 VAE 的隐变量 zz 有什么不同?

参考答案

h64h_{64} 确实是一个确定性的摘要,可以拿来当特征用(很多工作也这样做)。但它和隐变量 zz 有两点本质不同:(1) hh 不是生成过程的一部分——没有”先抽 hh、再生成 xx“的过程,所以不能通过控制 hh 来控制生成(不能”指定形状 = 十字”再采样);(2) hth_t 依赖于顺序,只概括了前 tt 个像素,而且它的含义没有任何约束(不像 zz 有先验 p(z)p(z))。隐变量模型把”高层描述”放进了模型的定义里,于是推断 p(z∣x)p(z \mid x) 有明确的含义——代价是似然不再好算(第 5 讲)。


11. 延伸阅读 · Further reading

原课材料

本讲的模型

  • FVSBN:Frey, Hinton & Dayan, Does the wake-sleep algorithm produce good density estimators?, NeurIPS 1996;Gan et al., Learning Deep Sigmoid Belief Networks with Data Augmentation, AISTATS 2015(slide 8 的样本)
  • NADE:Larochelle & Murray, The Neural Autoregressive Distribution Estimator, AISTATS 2011;RNADE:Uria, Murray & Larochelle, NeurIPS 2013
  • MADE:Germain, Gregor, Murray & Larochelle, MADE: Masked Autoencoder for Distribution Estimation, ICML 2015
  • 字符 RNN:Karpathy, The Unreasonable Effectiveness of Recurrent Neural Networks(博客,2015)
  • Transformer:Vaswani et al., Attention Is All You Need, NeurIPS 2017;GPT-2:Radford et al. 2019
  • PixelRNN / PixelCNN:van den Oord, Kalchbrenner & Kavukcuoglu, Pixel Recurrent Neural Networks, ICML 2016;Gated PixelCNN:van den Oord et al., Conditional Image Generation with PixelCNN Decoders, NeurIPS 2016
  • WaveNet:van den Oord et al., WaveNet: A Generative Model for Raw Audio, 2016
  • PixelDefend:Song, Kim, Nowozin, Ermon & Kushman, ICLR 2018

2023 年之后

  • Tian et al., Visual Autoregressive Modeling: Scalable Image Generation via Next-Scale Prediction(VAR), NeurIPS 2024
  • Li, Tian, Li, Deng & He, Autoregressive Image Generation without Vector Quantization(MAR), NeurIPS 2024
  • Leviathan, Kalman & Matias, Fast Inference from Transformers via Speculative Decoding, ICML 2023

12. 下一讲预告 · Next lecture

本讲解决了”怎样表示 pθ(x)p_\theta(x)“,训练时直接用了”最小化测试集上的 −log⁡pθ(x)-\log p_\theta(x)“。第 4 讲(原课 Lecture 4 Maximum Likelihood Learning)回答第二个问题:怎样学习,以及为什么是这个目标:

  • “最好的”模型是什么意思?学习 = 密度估计;
  • KL 散度:定义、非负性、不对称、“多用了多少比特”的压缩解释;
  • 最小化 DKL(pdata∥pθ)D_{\mathrm{KL}}(p_{\text{data}} \Vert p_\theta) = 最大化期望对数似然——但因为熵未知,我们永远不知道离最优还差多远(像素世界是个例外);
  • 蒙特卡洛估计、经验对数似然、抛硬币的 MLE;
  • 自回归模型的 MLE:梯度下降与随机梯度下降;参数共享;
  • 过拟合与偏差–方差权衡——本讲 MADE 的训练 / 测试差距、第 2 讲不加正则的神经网络,都会在那里得到解释。
← 返回课程目录