ResNet

  • Post author:
  • Post category:其他


重要意义:引入shortcut connection,让网络信息有效传播,梯度反传顺畅,使得数千层卷积神经网络都可以收敛

残差结构:

在这里插入图片描述

Identity与F(x)结合形式探讨:

.A-全零填充:维度增加的部分采用零来填充

B-网络层映射:当维度发生变化时,通过网络层映射(例如:1+1卷积)特征图至相同维度

C-所有Shortcut均通过网络层映射(例如:1*1卷积)

有点:1有利于梯度传播 2.提供了恒等映射的可能

里面模块的堆叠方式

Basic:两个3X3卷积堆叠

Bottleneck:利用1X1卷积减少计算量

在这里插入图片描述

在这里插入图片描述

. 恒等映射形式的shortcut connection是从网络退化问题中思考而来

网络退化:指的是模型复杂,难以训练,而不是模型复杂,准确度下降

代码构建部分:

通过建立makelayer建立模块堆叠


class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_planes = 16

        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)#numblock这个指的是堆叠数量
        self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)
        self.linear = nn.Linear(64, num_classes)

        self.apply(_weights_init)

    def _make_layer(self, block, planes, num_blocks, stride):
        strides = [stride] + [1]*(num_blocks-1)
        layers = []
        for stride in strides:#进行堆叠
            layers.append(block(self.in_planes, planes, stride))
            self.in_planes = planes * block.expansion#输入通道数和上一层输出通道数要对应

        return nn.Sequential(*layers)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = F.avg_pool2d(out, out.size()[3])
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

构建Basicblock导入堆叠

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, in_planes, planes, stride=1, option='A'):
        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.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)

        self.shortcut = nn.Sequential()
        if stride != 1 or in_planes != planes:#判断是不是维度变化(因为步长加长,特征图变小,需要处理维度问题)
            if option == 'A':
                """
                For CIFAR10 ResNet paper uses option A.
                """
                self.shortcut = LambdaLayer(lambda x:
                                            F.pad(x[:, :, ::2, ::2], (0, 0, 0, 0, planes//4, planes//4), "constant", 0))#如果维度增加那部分用0填充
            elif option == 'B':
                self.shortcut = nn.Sequential(
                     nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False),#用1x1卷积生维度
                     nn.BatchNorm2d(self.expansion * planes)
                )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)#加多一条Identity的线,就是那个恒等映射那里
        out = F.relu(out)
        return out

PS:

建立混淆矩阵直观看到训练和测试效果

混淆矩阵(Confusion Matrix)常用来观察分类结果,其是一个N*N的方阵,N表示类别数。

混淆矩阵的行表示真实类别,列表示预测类别。例如,猫狗的二分类问题,有猫的图像10张,狗的图像30张,模型对这40张图片进行预测,得到的混淆矩阵为

类别 阿猫 阿狗

阿猫 7 3

阿狗 10 20

从第一行中可知道,10张猫的图像中,7张预测为猫,3张预测为狗,猫的召回率(Recall)为7/10 = 70%,

从第二行中可知道,30张狗的图像中,8张预测为猫,22张预测为狗,狗的召回率为20/30 = 66.7%,

从第一列中可知道,预测为猫的17张图像中,有7张是真正的猫,猫的精确度(Precision)为7 / 17 = 41.17%

从第二列中可知道,预测为狗的23张图像中,有20张是真正的狗,狗的精确度(Precision)为20 / 23 = 86.96%

模型的准确率(Accuracy)为 (7+20) / 40 = 67.5%

可以发现通过混淆矩阵可以清晰的看出网络模型的分类情况,若再结合上颜色可视化,可方便的看出模型的分类偏好。

在这里插入图片描述

代码部分:

        conf_mat = np.zeros((10, 10))
        loss_sigma = []

        for i, data in enumerate(data_loader):

            inputs, labels = data
            inputs, labels = inputs.to(device), labels.to(device)

            outputs = model(inputs)

            optimizer.zero_grad()
            loss = loss_f(outputs, labels)
            loss.backward()
            optimizer.step()

            # 统计预测信息
            _, predicted = torch.max(outputs.data, 1)
# 统计混淆矩阵,这个不错,值得看
            for j in range(len(labels)):
                cate_i = labels[j].cpu().numpy()
                pre_i = predicted[j].cpu().numpy()
                conf_mat[cate_i, pre_i] += 1.#这里就是构建

            # 统计loss
            loss_sigma.append(loss.item())
            acc_avg = conf_mat.trace() / conf_mat.sum()

导入进行可视化

def show_confMat(confusion_mat, classes, set_name, out_dir, verbose=False):
    """
    混淆矩阵绘制
    :param confusion_mat:
    :param classes: 类别名
    :param set_name: trian/valid
    :param out_dir:
    :return:
    """
    cls_num = len(classes)
    # 归一化
    confusion_mat_N = confusion_mat.copy()
    for i in range(len(classes)):
        confusion_mat_N[i, :] = confusion_mat[i, :] / confusion_mat[i, :].sum()

    # 获取颜色
    cmap = plt.cm.get_cmap('Greys')  # 更多颜色: http://matplotlib.org/examples/color/colormaps_reference.html
    plt.imshow(confusion_mat_N, cmap=cmap)
    plt.colorbar()

    # 设置文字
    xlocations = np.array(range(len(classes)))
    plt.xticks(xlocations, list(classes), rotation=60)
    plt.yticks(xlocations, list(classes))
    plt.xlabel('Predict label')
    plt.ylabel('True label')
    plt.title('Confusion_Matrix_' + set_name)

    # 打印数字
    for i in range(confusion_mat_N.shape[0]):
        for j in range(confusion_mat_N.shape[1]):
            plt.text(x=j, y=i, s=int(confusion_mat[i, j]), va='center', ha='center', color='red', fontsize=10)
    # 显示

    plt.savefig(os.path.join(out_dir, 'Confusion_Matrix' + set_name + '.png'))
    # plt.show()
    plt.close()

    if verbose:
        for i in range(cls_num):
            print('class:{:<10}, total num:{:<6}, correct num:{:<5}  Recall: {:.2%} Precision: {:.2%}'.format(
                classes[i], np.sum(confusion_mat[i, :]), confusion_mat[i, i],
                confusion_mat[i, i] / (.1 + np.sum(confusion_mat[i, :])),
                confusion_mat[i, i] / (.1 + np.sum(confusion_mat[:, i]))))



版权声明:本文为m0_50903443原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。