liveBook · Manning

Residual connection

As explained in Learn Generative AI with PyTorch

A neural-network connection that bypasses intermediate layers and passes earlier activations to later layers.

A residual connection passes an input around a set of transformations and adds it to their output: x + f(x). The transformed path can change the input while the direct path carries it forward. This helps address vanishing gradients in deep networks. section 6.3.2

A residual block in an image generator

In a CycleGAN generator, x passes through two convolution-and-instance-normalization stages, with a ReLU between them. The block adds the original x to the result. Nine such blocks sit between the generator’s earlier convolutions and its upsampling blocks. section 6.3.2

Figure 6.4 The architecture of a residual block. The input x is passed through a series of transformations (two sets of Conv2d layer and InstanceNorm2d layer and a ReLU activation). The input x is then added back to the output of these transformations, f(x). The output of the residual block is therefore x + f(x).

Follow the two paths through the block: one transforms x into f(x), while the other carries x directly to the final addition. section 6.3.2

The same addition appears in Transformer decoder blocks. In the GPT-2XL implementation, each block first computes x + attention(layer_norm(x)), then adds a layer-normalized feed-forward result to that updated x. Residual connections therefore surround both sublayers, rather than only the whole decoder block. section 11.2.4

Listing 11.5 Constructing a decoder block
class Block(nn.Module):
    def __init__(self, config):                                 #1
        super().__init__()
        self.ln_1 = nn.LayerNorm(config.n_embd)
        self.attn = CausalSelfAttention(config)
        self.ln_2 = nn.LayerNorm(config.n_embd)
        self.mlp = nn.ModuleDict(dict(
            c_fc   = nn.Linear(config.n_embd, 4 * config.n_embd),
            c_proj = nn.Linear(4 * config.n_embd, config.n_embd),
            act    = GELU(),
            dropout = nn.Dropout(config.resid_pdrop),
        ))
        m = self.mlp
        self.mlpf=lambda x:m.dropout(m.c_proj(m.act(m.c_fc(x)))) 
    def forward(self, x):
        x = x + self.attn(self.ln_1(x))                         #2
        x = x + self.mlpf(self.ln_2(x))                         #3
        return x

The two additions in Block.forward show exactly where the attention and feed-forward paths rejoin their inputs. section 11.2.4

Do not confuse this addition with the skip connections in a denoising U-Net. Those connections carry encoder feature maps past the bottleneck and concatenate them with decoder feature maps to preserve details such as edges and textures. For example, two feature maps shaped (128, 64, 64) combine into a decoder input shaped (256, 64, 64). section 15.1.2

How Learn Generative AI with PyTorch covers it

How deep it goes
Defines x + f(x), traces a convolutional block, and shows two decoder-block additions without deriving gradient behavior.
Examples
Shows nine residual blocks in a CycleGAN generator and two residual additions per GPT-2XL decoder block.
What it assumes
Introduces residual addition from x and f(x) while using convolution, normalization, and neural-network layers as working context.
What you can do after
Build it from scratch in a PyTorch residual block or decoder block by adding each sublayer’s input to its transformed output.
How it connects
Connects residual connections to convolution, instance normalization, layer normalization, self-attention, feed-forward networks, and U-Net skip connections.

Also explained in

Compare how 12 books explain residual connection →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Generative AI with PyTorch. The section links point to the parts of the book it draws on.