PixelCNN介绍:VQ-VAE的前一步探索
一、自回归图像建模:PixelCNN 的基本思想
图像本质上是一个高维数据,比如一张 32×3232 \times 3232×32 的彩色图像有 32×32×3=307232 \times 32 \times 3 = 307232×32×3=3072 个像素值。直接对这么高维的空间建模非常困难。PixelCNN 采用 自回归(autoregressive)建模 的方式,将联合概率分解为一系列条件概率的乘积:
p(x)=∏i=1H×Wp(xi∣x1,x2,…,xi−1) p(\mathbf{x}) = \prod_{i=1}^{H \times W} p(x_i \mid x_1, x_2, \dots, x_{i-1}) p(x)=i=1∏H×Wp(xi∣x1,x2,…,xi−1)
其中:
- x\mathbf{x}x 是整张图像展平后的像素向量
- xix_ixi 是第 iii 个像素(按从左到右、从上到下的顺序)
- 每个像素的出现概率依赖于它之前的所有像素
这个分解方式类似于语言模型中预测下一个词,只不过这里是预测下一个像素。
例如,在生成第 $ i $ 个像素时,模型只能看到它的“左上区域”——也就是所有已经生成的像素(上方所有行 + 当前行左侧像素),不能看到右边或下边的“未来像素”。
二、掩码卷积(Masked Convolution):防止信息泄露
为了实现上述约束,PixelCNN 使用了 掩码卷积(masked convolution)。普通的卷积操作是中心对称的,会同时看到周围所有方向的信息,这在自回归生成中是不允许的。
1. 掩码设计
假设使用 $3 \times3 $ 的卷积核,我们定义一个二值掩码矩阵 MMM,使得卷积核只能“看到”当前位置之前的像素:
M=[111110000] M = \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 0 \\ 0 & 0 & 0 \\ \end{bmatrix} M=110110100
这个掩码表示:
- 第一行全部可见(上方像素)
- 第二行只有左边和中间可见(当前行左侧像素)
- 第三行全不可见(下方像素,属于未来)
然后,在卷积层初始化时,将卷积核权重与这个掩码相乘:
Wmasked=W⊙M W_{\text{masked}} = W \odot M Wmasked=W⊙M
这样,卷积操作就天然屏蔽了未来的像素信息。
2. 多层堆叠与感受野扩展
单层掩码卷积的感受野有限(如 3×33 \times 33×3),但通过多层堆叠,后续层可以逐步扩大感受野。经过 LLL 层后,最终层可以覆盖整个左上区域,实现全局依赖建模。
三、像素级概率建模:输出分布而非具体值
PixelCNN 不直接输出像素值,而是为每个像素输出一个离散的概率分布。以灰度图为例,每个像素取值范围是 {0,1,…,255}\{0, 1, \dots, 255\}{0,1,…,255},模型输出一个 256 维的 softmax 分布:
p(xi=k∣x<i)=exp(fk(x<i))∑j=0255exp(fj(x<i)) p(x_i = k \mid \mathbf{x}_{<i}) = \frac{\exp(f_k(\mathbf{x}_{<i}))}{\sum_{j=0}^{255} \exp(f_j(\mathbf{x}_{<i}))} p(xi=k∣x<i)=∑j=0255exp(fj(x<i))exp(fk(x<i))
其中 fk(⋅)f_k(\cdot)fk(⋅) 是神经网络(PixelCNN)的第 kkk 个输出通道,表示第 iii 个像素取值为 kkk 的未归一化得分(logit)。
对于彩色图像(RGB),通常采用 逻辑混合分布(Mixture of Logistic Distributions) 或逐通道建模。一种常见做法是将三个通道分开预测:
p(x)=∏ip(ri,gi,bi∣x<i)=∏ip(ri∣x<i)⋅p(gi∣ri,x<i)⋅p(bi∣ri,gi,x<i) p(\mathbf{x}) = \prod_{i} p(r_i, g_i, b_i \mid \mathbf{x}_{<i}) = \prod_{i} p(r_i \mid \mathbf{x}_{<i}) \cdot p(g_i \mid r_i, \mathbf{x}_{<i}) \cdot p(b_i \mid r_i, g_i, \mathbf{x}_{<i}) p(x)=i∏p(ri,gi,bi∣x<i)=i∏p(ri∣x<i)⋅p(gi∣ri,x<i)⋅p(bi∣ri,gi,x<i)
即先预测红色通道,再基于红色预测绿色,再基于前两者预测蓝色,进一步增强依赖性。
四、训练目标:最大化对数似然
PixelCNN 的训练目标是最小化负对数似然(Negative Log-Likelihood),等价于最大化数据出现的概率:
L=−Ex∼data[logp(x)]=−E[∑i=1Nlogp(xi∣x<i)] \mathcal{L} = -\mathbb{E}_{\mathbf{x} \sim \text{data}} \left[ \log p(\mathbf{x}) \right] = -\mathbb{E} \left[ \sum_{i=1}^{N} \log p(x_i \mid \mathbf{x}_{<i}) \right] L=−Ex∼data[logp(x)]=−E[i=1∑Nlogp(xi∣x<i)]
在训练时,输入整张图像,网络对每个位置输出一个概率分布,然后计算交叉熵损失。由于所有操作都是可导的(除了最后的采样),可以通过反向传播优化模型参数。
五、生成过程:逐像素采样
训练完成后,就可以进行图像生成。过程如下:
- 初始化一张全空图像(例如全零)
- 从左上角开始,逐个位置进行预测:
- 将已生成的部分输入 PixelCNN
- 得到当前位置的像素分布 p(xi∣x<i)p(x_i \mid \mathbf{x}_{<i})p(xi∣x<i)
- 从中采样一个像素值(例如按概率随机抽取,或取最可能的值)
- 填入图像
- 重复直到所有像素生成完毕
这个过程是顺序的、不可并行的,所以生成速度较慢,但能保证每一步都符合整体分布。
六、与 VQ-VAE 结合:采样离散编码序列
在 VQ-VAE 的框架中,PixelCNN 并不直接生成原始图像,而是作用于 离散的隐编码图。
回忆一下 VQ-VAE 的流程:
- 编码器将图像 xxx 映射为连续向量 ze(x)z_e(x)ze(x)
- 通过最近邻查找,得到离散索引 k=argminj∥ze(x)−ej∥k = \arg\min_j \|z_e(x) - e_j\|k=argminj∥ze(x)−ej∥,其中 eje_jej 是 codebook 向量
- 所有索引组成一个“小图像” zq\mathbf{z}_qzq,大小远小于原图(如 32×3232 \times 3232×32 而非 256×256256 \times 256256×256)
此时,我们可以把 zq\mathbf{z}_qzq 看作一个“低分辨率语义图”,每个位置的值是一个离散的 codebook 索引(比如从 0 到 511)。
于是,PixelCNN 的任务变成了:
学习这个离散编码图的分布 p(zq)=∏ip(zq,i∣zq,<i)p(\mathbf{z}_q) = \prod_i p(z_{q,i} \mid \mathbf{z}_{q,<i})p(zq)=∏ip(zq,i∣zq,<i)
训练完成后,我们就可以:
- 用 PixelCNN 采样出一个全新的 zq\mathbf{z}_qzq
- 将每个索引查表得到对应的嵌入向量 eke_kek
- 输入解码器,生成完整图像
这就实现了“从无到有”的图像生成。
七、数学总结:完整流程公式化
设原始图像为 xxx,VQ-VAE 的编码过程为:
ze=Encoder(x),ki=argminj∥ze(i)−ej∥2,zq(i)=eki z_e = \text{Encoder}(x), \quad k_i = \arg\min_j \|z_e^{(i)} - e_j\|^2, \quad z_q^{(i)} = e_{k_i} ze=Encoder(x),ki=argjmin∥ze(i)−ej∥2,zq(i)=eki
PixelCNN 建模隐编码图 k={k1,k2,…,kN}\mathbf{k} = \{k_1, k_2, \dots, k_N\}k={k1,k2,…,kN} 的分布:
p(k)=∏i=1Np(ki∣k<i;θ) p(\mathbf{k}) = \prod_{i=1}^N p(k_i \mid \mathbf{k}_{<i}; \theta) p(k)=i=1∏Np(ki∣k<i;θ)
其中 θ\thetaθ 是 PixelCNN 的参数。
训练目标:
Lpixelcnn=−E[∑i=1Nlogp(ki∣k<i)] \mathcal{L}_{\text{pixelcnn}} = -\mathbb{E} \left[ \sum_{i=1}^N \log p(k_i \mid \mathbf{k}_{<i}) \right] Lpixelcnn=−E[i=1∑Nlogp(ki∣k<i)]
生成时,逐个采样:
ki∼p(ki∣k<i) k_i \sim p(k_i \mid \mathbf{k}_{<i}) ki∼p(ki∣k<i)
最后通过解码器生成图像:
x^=Decoder(ek1,ek2,…,ekN) \hat{x} = \text{Decoder}(e_{k_1}, e_{k_2}, \dots, e_{k_N}) x^=Decoder(ek1,ek2,…,ekN)
八、改进与变体
原始 PixelCNN 有一些局限,后续出现了多个改进版本:
- PixelRNN:使用 RNN 结构建模长距离依赖,效果更好但更慢
- PixelCNN++:引入了更灵活的输出分布(混合逻辑分布)、上下文敏感的掩码、残差连接等,提升生成质量
- Conditional PixelCNN:加入类别标签或其他条件,实现可控生成
- Stacked PixelCNN:多尺度生成,先生成低频结构,再生成细节
总结
PixelCNN 的核心是:
- 将图像生成转化为逐像素预测问题
- 使用掩码卷积确保不泄露未来信息
- 输出离散概率分布,通过最大化似然进行训练
- 生成时逐像素采样,构建完整图像
- 与 VQ-VAE 结合时,用于在离散隐空间中采样合理的编码序列,从而实现高质量图像生成
虽然生成速度慢,但它为后来的自回归生成模型(如 Image Transformer、DALL-E、VQ-GAN)奠定了基础。特别是它与 VQ-VAE 的结合,展示了“先压缩,再生成”这一强大范式,直接影响了 Stable Diffusion 等现代生成模型的设计思路。