Ответ
Residual block (остаточный блок) — это фундаментальный строительный блок архитектуры ResNet, решающий проблему деградации (degradation) в очень глубоких сетях. Проблема не в переобучении, а в том, что добавление слоев сверх определенной глубины ухудшает точность как на обучении, так и на тесте.
Идея: Вместо того чтобы заставлять стек слоев F(x) аппроксимировать желаемое отображение H(x), мы заставляем его аппроксимировать остаточную функцию F(x) = H(x) - x. Тогда исходное отображение становится H(x) = F(x) + x.
Реализация (прямое распространение):
import torch.nn as nn
import torch.nn.functional as F
class BasicResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
# Skip-connection: если размерности входа и выхода не совпадают (например, из-за stride),
# нужен 1x1 сверточный слой для приведения размерности.
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
identity = self.shortcut(x) # Пропускное соединение (может быть тождественным или проекционным)
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += identity # Ключевой момент: сложение, а не конкатенация
out = F.relu(out)
return out
Почему это решает проблему:
- Облегчение обучения: Градиенту проще "протекать" через тождественное соединение. В предельном случае, если оптимальным является тождественное отображение, веса слоев
F(x)могут быть приведены к нулю. - Борьба с исчезающим градиентом: При обратном распространении градиент по тождественной связи равен 1, что обеспечивает прямой путь для градиента от глубоких слоев к浅层.
Эта концепция оказалась настолько успешной, что стала стандартом для большинства современных архитектур сверточных сетей (ResNet, ResNeXt, EfficientNet) и трансформеров (где она применяется вокруг слоя внимания и FFN).