计算机系统应用教程网站

网站首页 > 技术文章 正文

ResNet (Residual Network) - 残差网络

btikc 2024-09-09 01:42:02 技术文章 15 ℃ 0 评论

ResNet(残差网络)是一种重要的深度学习架构,由微软研究院的Kaiming He等人在2015年提出。ResNet通过引入残差连接(Residual Connections)解决了随着网络深度增加而导致的训练困难问题,使得网络能够成功训练上百甚至上千层的深度。

算法原理

ResNet的核心思想是残差学习(Residual Learning)。在一个深层网络中,如果一个层的输入和输出之间的映射关系是恒等映射(Identity Mapping),那么这个层就不会对网络的性能产生负面影响。基于这个思想,ResNet通过添加跳过一层或多层的直接连接(称为残差连接),允许网络学习输入和输出之间的残差(即差异),而不是直接学习映射关系。

具体来说,ResNet中的每个残差块(Residual Block)包含两个或更多的卷积层,这些卷积层的输出会与输入相加,然后进行非线性激活函数处理。如果输入和输出的维度不一致,通常会使用卷积层或全连接层进行调整。

数学推导解释

假设H(x)是期望的映射关系,F(x)是残差映射(即H(x) - x),那么残差块的目标就是学习F(x)。对于一个简单的残差块,其数学表达式可以写成:

y = F(x) + x

其中,y是残差块的输出,F(x)是经过卷积层的残差映射,x是输入。如果H(x)是一个恒等映射,那么F(x)将接近于0,网络学习的就是输入和输出之间的微小差异,这使得网络更容易学习到有用的特征。

Python代码实现

以下是使用PyTorch实现的ResNet的一个简化版:

import torch
import torch.nn as nn
import torch.nn.functional as F

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, in_planes, planes, stride=1, downsample=None):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.downsample = downsample

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        out = self.relu(out)
        return out

class ResNet(nn.Module):
    def __init__(self, block, layers, num_classes=1000):
        super(ResNet, self).__init__()
        self.in_planes = 64

        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=2)

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512 * block.expansion, num_classes)

    def _make_layer(self, block, planes, blocks, stride=1):
        downsample = None
        if stride != 1 or self.in_planes != planes * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.in_planes, planes * block.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes * block.expansion),
            )

        layers = []
        layers.append(block(self.in_planes, planes, stride, downsample))
        self.in_planes = planes * block.expansion
        for _ in range(1, blocks):
            layers.append(block(self.in_planes, planes))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

# 示例:创建一个ResNet-50网络
resnet50 = ResNet(BasicBlock, [3, 4, 6, 3])
# 假设输入特征图的大小为(1, 3, 224, 224)
input_tensor = torch.randn(1, 3, 224, 224)
# 前向传播
output = resnet50(input_tensor)
print(output.size())  # 应该输出 (1, 1000)

在这个例子中,我们定义了一个简化版的ResNet-50网络,它使用BasicBlock作为残差块,并且包含4个残差层级。我们创建了一个输入张量,并通过了ResNet网络来获取输出。这个简化的ResNet网络可以作为一个构建更深层次ResNet网络的组件。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表