人脸分割/分析 face-parsing.PyTorch 源码解析 resnet.py

import torch.nn.functional as F
import torch.utils.model_zoo as modelzoo

# from modules.bn import InPlaceABNSync as BatchNorm2d

resnet18_url = 'https://download.pytorch.org/models/resnet18-5c106cde.pth'


def conv3x3(in_planes, out_planes, stride=1):
    """3x3 convolution with padding"""
    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
                     padding=1, bias=False)


class BasicBlock(nn.Module):
    def __init__(self, in_chan, out_chan, stride=1):
        super(BasicBlock, self).__init__()
        self.conv1 = conv3x3(in_chan, out_chan, stride)
        self.bn1 = nn.BatchNorm2d(out_chan)
        self.conv2 = conv3x3(out_chan, out_chan)
        self.bn2 = nn.BatchNorm2d(out_chan)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = None
        if in_chan != out_chan or stride != 1:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_chan, out_chan,
                          kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_chan),
                )

    def forward(self, x):
        residual = self.conv1(x)
        residual = F.relu(self.bn1(residual))
        residual = self.conv2(residual)
        residual = self.bn2(residual)

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

        out = shortcut + residual
        out = self.relu(out)
        return out


def create_layer_basic(in_chan, out_chan, bnum, stride=1):
    layers = [BasicBlock(in_chan, out_chan, stride=stride)]
    for i in range(bnum-1):
        layers.append(BasicBlock(out_chan, out_chan, stride=1))
    return nn.Sequential(*layers)


class Resnet18(nn.Module):
    def __init__(self):
        super(Resnet18, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
                               bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = create_layer_basic(64, 64, bnum=2, stride=1)
        self.layer2 = create_layer_basic(64, 128, bnum=2, stride=2)
        self.layer3 = create_layer_basic(128, 256, bnum=2, stride=2)
        self.layer4 = create_layer_basic(256, 512, bnum=2, stride=2)
        self.init_weight()

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

        x = self.layer1(x)
        feat8 = self.layer2(x) # 1/8
        feat16 = self.layer3(feat8) # 1/16
        feat32 = self.layer4(feat16) # 1/32
        return feat8, feat16, feat32

    def init_weight(self):
        state_dict = modelzoo.load_url(resnet18_url)
        self_state_dict = self.state_dict()
        for k, v in state_dict.items():
            if 'fc' in k: continue
            self_state_dict.update({k: v})
        self.load_state_dict(self_state_dict)

    def get_params(self):
        wd_params, nowd_params = [], []
        for name, module in self.named_modules():
            if isinstance(module, (nn.Linear, nn.Conv2d)):
                wd_params.append(module.weight)
                if not module.bias is None:
                    nowd_params.append(module.bias)
            elif isinstance(module,  nn.BatchNorm2d):
                nowd_params += list(module.parameters())
        return wd_params, nowd_params


if __name__ == "__main__":
    net = Resnet18()
    x = torch.randn(16, 3, 224, 224)
    out = net(x)
    print(out[0].size())
    print(out[1].size())
    print(out[2].size())
    net.get_params()

这段代码主要定义了一个深度学习模型,这个模型基于ResNet18的网络架构。以下是对每一行代码的解释:

1-2: 导入torch.nn.functional模块和torch.utils.model_zoo模块。前者包含了许多神经网络的函数,后者可以加载预训练的模型。

5: 设置一个字符串变量resnet18_url,这个URL链接指向预训练的ResNet18模型。

7-11: 定义一个函数conv3x3,这个函数返回一个2D卷积层,卷积核大小为3x3,步长和填充可以自定义。

13-34: 定义了一个名为BasicBlock的类,这个类继承了PyTorch中nn.Module的基础类。BasicBlock代表ResNet中的基本块,包含两个3x3卷积层。如果输入和输出的通道数不同,或者步长不为1,那么会在shortcut中使用1x1卷积进行调整。

36-41: 定义了一个函数create_layer_basic,用于创建多个BasicBlock

43-61: 定义了一个名为Resnet18的类,这个类同样继承了nn.Module类,表示整个ResNet18的网络结构。

63-73: Resnet18类中定义了一个名为forward的方法,它负责定义网络的前向传播过程

75-82: Resnet18类中定义了一个名为init_weight的方法,它负责从预训练模型中加载权重并更新当前模型的权重。

84-95: Resnet18类中定义了一个名为get_params的方法,这个方法用于获取网络中需要和不需要权重衰减的参数。

97-105: 这部分代码在__name__ == "main"下,意味着只有当这个脚本被直接运行时才会执行。这部分代码实例化了Resnet18类,然后生成一批随机数据,通过Resnet18模型,然后打印出中间层的输出大小。

所以这个脚本的主要目标是定义一个ResNet18模型,然后加载预训练的权重,并在给定输入数据后进行前向传播。

以下是逐行解释的代码:

 
 

pythonCopy code

import torch.nn.functional as F

这一行导入了PyTorch的函数模块,该模块包含了很多神经网络的函数,如激活函数,损失函数等。

 
 

pythonCopy code

import torch.utils.model_zoo as modelzoo

这一行导入了PyTorch的model_zoo模块,该模块提供了一种方便的方式来加载各种预训练的模型。

 
 

pythonCopy code

resnet18_url = 'https://download.pytorch.org/models/resnet18-5c106cde.pth'

设置了预训练的ResNet18模型的下载链接。

 
 

pythonCopy code

def conv3x3(in_planes, out_planes, stride=1): """3x3 convolution with padding""" return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False)

定义了一个名为conv3x3的函数,该函数返回一个3x3的卷积层,带有padding。输入参数包括输入和输出的通道数量以及步长。

 
 

pythonCopy code

class BasicBlock(nn.Module): def __init__(self, in_chan, out_chan, stride=1): super(BasicBlock, self).__init__() self.conv1 = conv3x3(in_chan, out_chan, stride) self.bn1 = nn.BatchNorm2d(out_chan) self.conv2 = conv3x3(out_chan, out_chan) self.bn2 = nn.BatchNorm2d(out_chan) self.relu = nn.ReLU(inplace=True)

定义了一个名为BasicBlock的类,这是ResNet的基础模块。它包括两个卷积层和两个批次归一化层,以及一个ReLU激活函数。

 
 

pythonCopy code

self.downsample = None if in_chan != out_chan or stride != 1: self.downsample = nn.Sequential( nn.Conv2d(in_chan, out_chan, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(out_chan), )

这部分代码定义了当输入通道和输出通道不相等,或步长不为1时,为了使维度匹配,需要对输入进行下采样操作。

 
 

pythonCopy code

def forward(self, x): residual = self.conv1(x) residual = F.relu(self.bn1(residual)) residual = self.conv2(residual) residual = self.bn2(residual)

在forward函数中,先通过两个卷积-批标准化-激活的流程,处理输入x,生成残差(residual)。

 
 

pythonCopy code

shortcut = x if self.downsample is not None: shortcut = self.downsample(x) out = shortcut + residual out = self.relu(out) return out

然后判断是否需要对输入x进行下采样,如果需要则对其进行下采样。接着将下采样后的shortcut与残差相加,然后通过ReLU激活函数,生成输出。

 
 

pythonCopy code

def create_layer_basic(in_chan, out_chan, bnum, stride=1): layers = [BasicBlock(in_chan, out_chan, stride=stride)] for i in range(bnum-1): layers.append(BasicBlock(out_chan, out_chan, stride=1)) return nn.Sequential(*layers)

这个函数用于创建多个BasicBlock,创建的数量由bnum参数指定。第一个BasicBlock的输入通道和输出通道由函数的参数决定,后续的BasicBlock的输入和输出通道相同。

 
 

pythonCopy code

class Resnet18(nn.Module): def __init__(self): super(Resnet18, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) self.bn1 = nn.BatchNorm2d(64) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

定义了Resnet18类,这个类中包含了一个7x7的卷积层,一个批次归一化层,以及一个最大池化层。

 
 

pythonCopy code

self.layer1 = create_layer_basic(64, 64, bnum=2, stride=1) self.layer2 = create_layer_basic(64, 128, bnum=2, stride=2) self.layer3 = create_layer_basic(128, 256, bnum=2, stride=2) self.layer4 = create_layer_basic(256, 512, bnum=2, stride=2) self.init_weight()

定义了四个层,每个层都是由两个BasicBlock组成的。同时,调用了init_weight()函数对网络进行权重初始化。

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

        x = self.layer1(x)
        feat8 = self.layer2(x) # 1/8
        feat16 = self.layer3(feat8) # 1/16
        feat32 = self.layer4(feat16) # 1/32
        return feat8, feat16, feat32

定义了前向传播的函数。首先输入经过一次卷积,一次批归一化,一次激活函数ReLU和一次最大池化。然后依次经过四个层的处理,分别生成feat8, feat16, feat32三个特征,并返回。

 
 
    def get_params(self):
        wd_params, nowd_params = [], []
        for name, module in self.named_modules():
            if isinstance(module, (nn.Linear, nn.Conv2d)):
                wd_params.append(module.weight)
                if not module.bias对不起,这部分代码被切割了。我会继续分析接下来的可能代码。

```python
                if not module.bias is None:
                    nowd_params.append(module.bias)
            elif isinstance(module, nn.BatchNorm2d):
                nowd_params += list(module.parameters())
        return wd_params, nowd_params

定义了权重初始化的函数。首先从model_zoo中加载预训练的ResNet18模型的权重,然后更新当前模型的权重。注意这里忽略了全连接层的权重。

这个函数用于获取模型中的参数,将卷积层和线性层的权重加入到权重衰减参数列表wd_params中,如果这些层有偏置项,那么将偏置项加入到非权重衰减参数列表nowd_params中。对于批量归一化层,将其所有参数加入到非权重衰减参数列表nowd_params中。

 
 

if __name__ == "__main__":
    net = Resnet18()
    fms = net(torch.randn(1, 3, 224, 224))
    for fm in fms:
        print(fm.size())

这段代码是在直接运行这个脚本时会执行的部分。首先创建一个Resnet18的实例,然后创建一批随机的图像数据,通过这个网络,并打印出每一层的输出尺寸

以上就是这段代码的详细解释。这段代码定义了一个ResNet18的模型,并提供了加载预训练权重的方法。同时,还包括了如何使用这个模型进行前向传播的例子。

猜你喜欢

转载自blog.csdn.net/sinat_37574187/article/details/131617005
今日推荐