31 / 33 · Autoregressive Models
Autoregressive models: PixelRNN and PixelCNN
Write the density of an image as a product of one-dimensional conditionals, each a softmax over pixel values: the exact likelihood, bits per dimension, parallel training and sequential sampling, recurrent (PixelRNN) and masked convolutional (PixelCNN) networks, the blind spot, Gated PixelCNN and PixelCNN++.
On this page 12
The families of this course deal with the intractable likelihood in different ways: a VAE bounds it, a GAN avoids it, a flow computes it through an invertible map, diffusion bounds it again. Autoregressive models remove the difficulty at its source. They write the density of an image as a product of one-dimensional conditional distributions, each a softmax over a few hundred values and therefore normalized by construction. The likelihood is exact, training is a cross-entropy loss, and the price is paid at sampling time, one pixel after another.
Notation. An image is a vector of values in a fixed order, usually row by row from the top left, and for colour images red, green and blue within each pixel. are the values before position . Values are integers in unless stated otherwise.
The chain rule as a model#
Any joint distribution can be written as
This is the chain rule of probability, not an assumption: it holds for every distribution and every ordering of the coordinates. A model becomes restrictive only through the way the conditionals are parameterized. Maximum likelihood, which the KL note derives from minimizing , becomes a sum of classification losses: at every position, predict the next value from the previous ones, and pay the cross-entropy of the true value.
The whole difficulty is in representing the conditionals compactly. Take binary images of pixels. A table of the joint distribution has free numbers, and so do the tables of all conditionals together, since the conditional of pixel needs one number for each of the histories. The fully visible sigmoid belief network replaces each table by a logistic regression,
with parameters for pixel and in total. For the images of MNIST, , that is 307,720 parameters instead of . Later models share more: NADE (Larochelle and Murray, 2011) computes all conditionals with one hidden layer whose weights are reused across positions, and MADE (Germain et al., 2015) gets them all from one pass of an autoencoder whose weights are masked so that output depends only on inputs before ; the same masks build the autoregressive flows. PixelRNN and PixelCNN go further and reuse one recurrent or convolutional network at every position, so the number of parameters no longer depends on the size of the image.
Sampling follows the product: draw , then given , and so on. Each draw is the inverse-CDF method for a discrete variable: take and return the smallest value whose cumulative probability reaches . An autoregressive model is still noise plus a learned transformation, with uniform numbers as the noise.
Pixels as discrete values#
PixelRNN (van den Oord, Kalchbrenner and Kavukcuoglu, 2016) treats each colour value as a category and predicts it with a 256-way softmax. A discrete distribution needs no assumption about its shape, so it can be multimodal or skewed, and it assigns probabilities rather than densities: and no dequantization is needed, unlike the continuous densities of flows.
Likelihoods of image models are reported in bits per dimension:
averaged over test images. A model that spreads probability uniformly over 256 values gets exactly 8 bits/dim. The number has a direct meaning: with arithmetic coding, a model with bits/dim compresses images losslessly to about bits. On CIFAR-10, , and the best PixelRNN reaches 3.00 bits/dim: bits, about 1.1 KiB per image, or 37.5% of the 24,576 bits of the raw image.
A model that predicts fewer levels solves a coarser problem. With 10 levels of grey a uniform guess costs nats or bits per pixel, and such numbers cannot be compared with bits/dim over 256 values.
Parallel training, sequential sampling#
During training the whole image is known. If the network is built so that its output at position depends only on the inputs , a single forward pass on the real image yields all conditionals at once, and the loss is their sum. Feeding the true previous values rather than the model’s own samples is called teacher forcing, and it makes training as parallel as for any convolutional network.
Sampling cannot be parallel: is computed from , which must be sampled first. A model that predicts one value at a time needs network passes for a sample, 3072 for a CIFAR-10 image and for a grey image of pixels. A model that predicts a whole pixel per pass, such as PixelCNN++ below, needs one pass per pixel, 1024 for CIFAR-10, and draws the three values of the pixel from the predicted parameters; the positions are still sequential. A plain implementation also recomputes the whole image at every pass, although only one output is used. This asymmetry, cheap density and expensive sampling, is the same as for the masked autoregressive flow.
PixelRNN and PixelCNN#
The two networks of van den Oord, Kalchbrenner and Kavukcuoglu (2016) differ in how the output at position collects information from all of while seeing nothing of and later values.
Recurrent layers#
Row LSTM processes the image row by row from top to bottom and computes the state of a whole row at once. The input-to-state part is a convolution along the row (), and the state-to-state part is a one-dimensional convolution over the previous row of states. Each step looks a little to the left and right in the row above, so the context of a pixel is a triangle above it and misses part of the available past.
Diagonal BiLSTM captures the whole past. It skews the input map, shifting each row one position to the right of the previous one, so that the diagonals of the image become columns of an map. The state-to-state convolution is along these columns, and two LSTMs scan in opposite directions; the output of the right-to-left one is shifted down a row before the two are added, so that no future pixel leaks in. The models use up to 12 layers with residual connections between them. The recurrences run sequentially over rows or diagonals even during training, which makes both models slow to train.
Masked convolutions#
PixelCNN replaces the recurrences with convolutions whose kernels are multiplied by a mask. For a kernel with centre ,
so a position sees the rows above it and the pixels to its left. The first layer uses mask A, which excludes the centre, because its input at the centre is the value to be predicted. The later layers use mask B, which includes it: after the first layer, the feature at position has been computed from only, and passing it on is safe. The PixelCNN of the paper has fifteen layers, a first layer with mask A followed by residual blocks with convolutions. Training is fully parallel; sampling is not.
The 5×5 convolution masks of PixelCNN
Colour channels#
With colour, each pixel holds three values ordered R, G, B, and the factorization goes over all (number of pixels) of them:
where is now the whole pixel. The feature maps of every layer are split into three groups, one per colour. Mask A lets the red group see no value of the current pixel, the green group see red, and the blue group see red and green; mask B in addition connects each group to itself.
The simplest alternative models the channels independently, for example with three grey-level models, one per channel. Each channel is then a plausible image on its own, but nothing ties the three together: in a sample, the red, green and blue values of a pixel, and the edges they draw, need not agree. A milder version conditions every channel on all earlier pixels of all channels but drops the dependence among the three values of the current pixel. The chain rule needs that dependence, so this version is an assumption rather than a factorization. A model that keeps the dependence either follows the channel order inside the masks, as above, or, as PixelCNN++ does below, predicts the three values of a pixel jointly with a simple dependence between them.
The blind spot#
Stacking masked convolutions does not give the full past. A mask reaches one row up and one column to each side, or one column to the left in the current row. After layers, the row steps above the target is covered from columns to the left up to only columns to the right, whatever is. The pixels further right in the rows just above stay out of reach at any depth. Within the window of rows and columns, this blind spot holds of the earlier pixels, 6 of 40 for ; its share grows toward a quarter, the figure van den Oord et al. (2016b) give. The Code section below finds the same 6 pixels by differentiating a randomly initialized network.
The blind spot of PixelCNN
Gated PixelCNN#
van den Oord et al. (2016b) close the gap between PixelCNN and PixelRNN with two changes. The first fixes the blind spot with two stacks of convolutions. A vertical stack is not masked within its window and sees all rows above the current one, so its receptive field grows as a rectangle. A horizontal stack sees the current row up to the current pixel. After each layer the vertical stack feeds the horizontal one, never the other way round: information from the horizontal stack would carry pixels to the right of or below the target into the vertical stack.
The second change replaces the ReLUs between layers with multiplicative units, a stand-in for the gates of an LSTM:
where is a convolution, the layer and the element-wise product. A condition enters as an extra term inside both nonlinearities: for a vector , such as a one-hot class label, , and for a spatial map the products become convolutions . The conditional model is .
On CIFAR-10 the Gated PixelCNN reaches 3.03 bits/dim, against 3.14 for PixelCNN and 3.00 for PixelRNN. On ImageNet it is slightly better than PixelRNN, 3.83 against 3.86 bits/dim at and 3.57 against 3.63 at , and it matches the Row LSTM in less than half the training time (60 hours on 32 GPUs). The ImageNet model has 20 layers of 384 units with filters.
PixelCNN++#
A 256-way softmax knows nothing about the order of its classes: 127 and 128 are as unrelated as 0 and 255 until the network learns otherwise, a value never seen in training gets almost no probability, and the output layer has numbers per pixel. PixelCNN++ (Salimans et al., 2017) assumes a continuous intensity with a mixture of logistic distributions and models the observed value as rounded to an integer:
where is the logistic sigmoid, the CDF of the logistic distribution. For the term is replaced by , and for the term by , so the probabilities sum to one over and the edges collect the mass of everything beyond them. With one component, and , the values 126, 127, 128 and 129 get probabilities 0.112, 0.124, 0.121 and 0.105: neighbours are close automatically. With and , the edge value 255 gets 0.438 and 254 gets 0.124, which suits the excess of saturated pixels in real photographs.
The other changes:
- Whole pixels. The network conditions on whole pixels above and to the left and predicts the three channels of a pixel together. The mixture indicator is shared by the channels, and inside a component the means depend linearly on the channels already drawn: and , with produced by the network. The output is numbers per pixel ( weights, a mean and a scale for each channel and component, and three coefficients per component), 50 for , which the paper finds sufficient.
- Downsampling with stride-2 convolutions instead of dilated convolutions to see large regions at several resolutions, with short-cut connections that carry the full-resolution features past the downsampled layers; without them the model fails to train.
- Dropout on the residual path. Without it the training loss falls below 2.0 bits per sub-pixel and the test loss rises above 6.0.
PixelCNN++ reaches 2.92 bits/dim on CIFAR-10. The same model trained on images with added uniform noise, a continuous density instead of the rounding, gives a bound of 3.11.
How the likelihoods compare#
Test negative log-likelihood on CIFAR-10, in bits per dimension (van den Oord et al., 2016a, 2016b; Salimans et al., 2017):
| Model | Bits/dim |
|---|---|
| Uniform distribution | 8.00 |
| Multivariate Gaussian | 4.70 |
| NICE (flow) | 4.48 |
| Real NVP (flow) | 3.49 |
| PixelCNN | 3.14 |
| Row LSTM | 3.07 |
| Gated PixelCNN | 3.03 |
| Diagonal BiLSTM (PixelRNN) | 3.00 |
| PixelCNN++ | 2.92 |
For several years after 2016 autoregressive models held the best likelihoods on these benchmarks. A likelihood measures how well the model compresses the test set, which is not the same as how good its samples look: the evaluation notes measure that separately, and a model can win on one and lose on the other.
Strengths and limits#
- Exact likelihood and simple training. The loss is a sum of cross-entropies with no bound, no adversary and no Jacobian, and it can be monitored on held-out data.
- Slow sampling. One network evaluation per position: for one image with a sub-pixel model, one per pixel ( for colour) when the network predicts whole pixels as PixelCNN++ does, with the channel values then drawn from its output. Either way the cost grows with resolution. Diffusion also needs many passes, but their number is set by the noise schedule, not by the number of pixels.
- No latent code. Nothing summarizes an image in a few numbers, and the ordering is arbitrary: the model must learn global structure from left-to-right, top-to-bottom prediction. PixelVAE (Gulrajani et al., 2017) combines the two ideas: a VAE whose decoder is a PixelCNN. It needs far fewer autoregressive layers than a PixelCNN alone, and its codes are more compressed than those of a plain VAE while still capturing most of the structure of the image.
- Strong priors. Over a small grid of discrete codes, an autoregressive model is an excellent prior. This is its role in VQ-VAE, which fits a PixelCNN over the code indices, and the starting point of the next note.
Notebook#
The PixelCNN notebook of the HSE-LAMBDA Deep Generative Models repository trains a small PixelCNN on movie posters (cell numbers count every cell from 1). It has no saved outputs, so it documents the procedure rather than results.
- Cells 1–3: setup. Posters of pixels are downscaled by 3 to ; batches of 32.
- Cells 4–5, task 1:
bucketizemaps 0–255 to 10 levels by integer division by 26 and clamping at 9. The buckets are not quite equal, as the docstring says: levels 0–8 hold 26 values each and level 9 the 22 values 234–255. The task text gives the shape as batch × channels × width × height; the tensors are batch × channels × height × width. - Cells 6–7, task 2: resize, convert to grey,
ToTensor, multiply by 255 and bucketize. The product returns the exact integers here, so no pixel lands in a wrong bucket. - Cells 9–13:
CustomImageDatasetlists the image files in../data/movie-genre-from-its-poster/posters/, the folder name of the Kaggle Movie Genre from its Poster dataset of IMDB posters. It opens every file once with PIL’sverify()and skips those it cannot read, converts each image to RGB before the transforms, and returns a dummy label 0 with every image so that the default loader works. The last 100 files in sorted order are the validation set. The validation loader drops the last incomplete batch, so 96 of them are used. - Cells 15–19, task 3:
make_maskandMaskedCNN, which multipliesweight.databy the mask in place on every forward pass. This works: the masked weights still receive gradients and the optimizer moves them between steps, but the next forward pass zeroes them again, the same effect as the PixelRNN paper’s zeroing after each update. Multiplying in the forward pass without.data, as in the Code section, keeps their gradients at zero. - Cells 20–21, task 4: a model of one mask-A layer, one mask-B layer and a output to 10 logits per pixel. Two masked layers see little: each prediction reaches at most rows up, 6 of the 89 rows for kernels. The model receives the level indices 0–9 as floats without rescaling.
- Cells 23–24: Adam, cross-entropy over the 10 levels and 50 epochs. After every epoch two validation posters are completed from row 10 and column 10: the region below and to the right is zeroed and refilled pixel by pixel, network passes for the region, while the pixels left of column 10 in the lower rows stay real. The loss is in nats per pixel, with the uniform guess at ; it measures the 10-level problem and is not comparable with bits/dim over 256 values.
- Cell 25: eight completions from row 30 and column 30, network passes for the batch.
- Cell 26: the question about colour images, answered in Colour channels above.
Code#
A PixelCNN for grey images with levels values per pixel, in PyTorch, and a check of its receptive field:
import torch
import torch.nn as nn
import torch.nn.functional as F
def make_mask(include_center, height, width):
mask = torch.zeros(height, width)
mask[: height // 2] = 1 # rows above the centre
mask[height // 2, : width // 2] = 1 # left of the centre in its row
if include_center:
mask[height // 2, width // 2] = 1
return mask
class MaskedConv2d(nn.Conv2d):
def __init__(self, include_center, *args, **kwargs):
super().__init__(*args, **kwargs)
self.register_buffer('mask', make_mask(include_center, *self.kernel_size))
def forward(self, x):
return F.conv2d(x, self.weight * self.mask, self.bias, padding=self.padding)
class PixelCNN(nn.Module):
def __init__(self, levels=10, hidden=64, layers=4, k=3):
super().__init__()
self.levels = levels
blocks = [MaskedConv2d(False, 1, hidden, k, padding=k // 2), nn.ReLU()]
for _ in range(layers - 1):
blocks += [MaskedConv2d(True, hidden, hidden, k, padding=k // 2), nn.ReLU()]
self.net = nn.Sequential(*blocks, nn.Conv2d(hidden, levels, 1))
def forward(self, x): # x: [B, 1, H, W] with integer levels
return self.net(x.float() / (self.levels - 1)) # logits: [B, levels, H, W]
@torch.no_grad()
def sample(self, x, start=(0, 0)): # fill x in raster order from start
x = x.clone()
H, W = x.shape[-2:]
for i in range(start[0], H):
for j in range(start[1] if i == start[0] else 0, W):
probs = self(x)[:, :, i, j].softmax(dim=1)
x[:, 0, i, j] = torch.multinomial(probs, 1).squeeze(1)
return x
# training step: loss = F.cross_entropy(model(x), x.long().squeeze(1)), in nats per pixel
torch.manual_seed(0)
model = PixelCNN()
x = torch.randint(0, 10, (1, 1, 11, 11)).float().requires_grad_(True)
model(x)[0, :, 5, 5].sum().backward() # which inputs reach the output at (5, 5)?
print((x.grad[0, 0, 1:6, 1:10] != 0).int())
The printed window covers the four rows above position , its own row and four columns to each side:
tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 0, 0]], dtype=torch.int32)
No input at or after in raster order has a nonzero gradient, and the zeros in the rows above are the 6 pixels of the blind spot in the figure.
Next#
PixelCNN predicts one pixel from a fixed neighbourhood. Autoregressive transformers replace the convolutions with self-attention, which sees the whole past at every layer, and, for large images, move from pixels to the discrete codes of a VQ autoencoder: a grid of 256 to 1024 tokens describes a image and can be continued from a text prompt.
References#
- A. van den Oord, N. Kalchbrenner, K. Kavukcuoglu. Pixel Recurrent Neural Networks. ICML, 2016. (2016a)
- A. van den Oord, N. Kalchbrenner, O. Vinyals, L. Espeholt, A. Graves, K. Kavukcuoglu. Conditional Image Generation with PixelCNN Decoders. NeurIPS, 2016. (2016b)
- T. Salimans, A. Karpathy, X. Chen, D. P. Kingma. PixelCNN++: Improving the PixelCNN with Discretized Logistic Mixture Likelihood and Other Modifications. ICLR, 2017.
- H. Larochelle, I. Murray. The Neural Autoregressive Distribution Estimator. AISTATS, 2011.
- M. Germain, K. Gregor, I. Murray, H. Larochelle. MADE: Masked Autoencoder for Distribution Estimation. ICML, 2015.
- I. Gulrajani, K. Kumar, F. Ahmed, A. A. Taiga, F. Visin, D. Vazquez, A. Courville. PixelVAE: A Latent Variable Model for Natural Images. ICLR, 2017.