class_docstr: <a id="bottleneck_residual_block"></a>
  
  ## Bottleneck Residual Block
  
  This implements the bottleneck block described in the paper.
  It has $1   imes 1$, $3     imes 3$, and $1         imes 1$ convolution layers.
  
  ![Bottlenext Block](bottleneck_block.svg)
  
  The first convolution layer maps from `in_channels` to `bottleneck_channels` with a $1      imes 1$
  convolution,
  where the `bottleneck_channels` is lower than `in_channels`.
  
  The second $3       imes 3$ convolution layer maps from `bottleneck_channels` to `bottleneck_channels`.
  This can have a stride length greater than $1$ when we want to compress the
  feature map size.
  
  The third, final $1         imes 1$ convolution layer maps to `out_channels`.
  `out_channels` is higher than `in_channels` if the stride length is greater than $1$;
  otherwise, $out_channels$ is equal to `in_channels`.
  
  `bottleneck_channels` is less than `in_channels` and the $3         imes 3$ convolution is performed
  on this shrunk space (hence the bottleneck). The two $1     imes 1$ convolution decreases and increases
  the number of channels.
class_docstr_tok: ["<","a","id=","''","bottleneck_residual_block","''",">","<","\/a",">","#","#","Bottleneck","Residual","Block","This","implements","the","bottleneck","block","described","in","the","paper",".","It","has","$","1","imes","1","$",",","$","3","imes","3","$",",","and","$","1","imes","1","$","convolution","layers",".","!","[","Bottlenext","Block","]","(","bottleneck_block.svg",")","The","first","convolution","layer","maps","from","`","in_channels","`","to","`","bottleneck_channels","`","with","a","$","1","imes","1","$","convolution",",","where","the","`","bottleneck_channels","`","is","lower","than","`","in_channels","`",".","The","second","$","3","imes","3","$","convolution","layer","maps","from","`","bottleneck_channels","`","to","`","bottleneck_channels","`",".","This","can","have","a","stride","length","greater","than","$","1","$","when","we","want","to","compress","the","feature","map","size",".","The","third",",","final","$","1","imes","1","$","convolution","layer","maps","to","`","out_channels","`",".","`","out_channels","`","is","higher","than","`","in_channels","`","if","the","stride","length","is","greater","than","$","1","$",";","otherwise",",","$","out_channels","$","is","equal","to","`","in_channels","`",".","`","bottleneck_channels","`","is","less","than","`","in_channels","`","and","the","$","3","imes","3","$","convolution","is","performed","on","this","shrunk","space","(","hence","the","bottleneck",")",".","The","two","$","1","imes","1","$","convolution","decreases","and","increases","the","number","of","channels","."]
docstr: * `in_channels` is the number of channels in $x$
  * `bottleneck_channels` is the number of channels for the $3    imes 3$ convlution
  * `out_channels` is the number of output channels
  * `stride` is the stride length in the $3       imes 3$ convolution operation.
docstr_tok: ["*","`","in_channels","`","is","the","number","of","channels","in","$","x","$","*","`","bottleneck_channels","`","is","the","number","of","channels","for","the","$","3","imes","3","$","convlution","*","`","out_channels","`","is","the","number","of","output","channels","*","`","stride","`","is","the","stride","length","in","the","$","3","imes","3","$","convolution","operation","."]
code: def __init__(
      self,
      in_channels: int,
      bottleneck_channels: int,
      out_channels: int,
      stride: int,
  ):
      """
      * `in_channels` is the number of channels in $x$
      * `bottleneck_channels` is the number of channels for the $3 \times 3$ convlution
      * `out_channels` is the number of output channels
      * `stride` is the stride length in the $3 \times 3$ convolution operation.
      """
      super().__init__()
  
      # First $1 \times 1$ convolution layer, this maps to `bottleneck_channels`
      self.conv1 = nn.Conv2d(
          in_channels, bottleneck_channels, kernel_size=1, stride=1
      )
      # Batch normalization after the first convolution
      self.bn1 = nn.BatchNorm2d(bottleneck_channels)
      # First activation function (ReLU)
      self.act1 = nn.ReLU()
  
      # Second $3 \times 3$ convolution layer
      self.conv2 = nn.Conv2d(
          bottleneck_channels,
          bottleneck_channels,
          kernel_size=3,
          stride=stride,
          padding=1,
      )
      # Batch normalization after the second convolution
      self.bn2 = nn.BatchNorm2d(bottleneck_channels)
      # Second activation function (ReLU)
      self.act2 = nn.ReLU()
  
      # Third $1 \times 1$ convolution layer, this maps to `out_channels`.
      self.conv3 = nn.Conv2d(
          bottleneck_channels, out_channels, kernel_size=1, stride=1
      )
      # Batch normalization after the second convolution
      self.bn3 = nn.BatchNorm2d(out_channels)
  
      # Shortcut connection should be a projection if the stride length is not $1$
      # or if the number of channels change
      if stride != 1 or in_channels != out_channels:
          # Projection $W_s x$
          self.shortcut = ShortcutProjection(
              in_channels, out_channels, stride
          )
      else:
          # Identity $x$
          self.shortcut = nn.Identity()
  
      # Second activation function (ReLU) (after adding the shortcut)
      self.act3 = nn.ReLU()
  
file_path: annotated_deep_learning_paper_implementations/labml_nn/resnet/__init__.py
imports: from typing import List, Optional
  import torch
  from torch import nn
  from labml_helpers.module import Module
label_desc: Use image node_id 1 to create a new BottleneckResidualBlock object from inherited base classes: Module with example: obj = BottleneckResidualBlock(in_channels, bottleneck_channels, out_channels, stride)