2022.10.23 学习周报


摘要

This week, a paper on Convolutional Neural Networks was interpreted, which proposed that the addition of Inception blocks can use computing resources more efficiently, and can extract more features under the same amount of computing, thereby improving training results. At the same time, I developed a further understanding of the convolutional neural network, tried to answer some questions about convolution and pooling, and finally calculated the partial derivative of the cross-entropy loss function formula.

在这周,一篇关于卷积神经网络的论文被解读了,它提出Inception块的加入能够更高效地利用计算资源,在相同的计算量下能提取到更多的特征,从而提升训练结果。我同时对卷积神经网络展开进一步的理解,试着去回答一些关于卷积和池化的问题,最后去计算了交叉熵损失函数公式的偏导。


一、文献阅读

1、题目

原文链接:Going Deeper with Convolutions

2、摘要

We propose a deep convolutional neural network architecture codenamed Inception that achieves the new state of the art for classification and detection in the ImageNet Large Scale Visual Recognition Challenge 2014(ILSVRC14). The main hallmark of this architecture is theimproved utilization of the computing resources inside the network. By a carefully crafted design, we increased thedepth and width of the network while keeping the computational budget constant. To optimize quality, the architectural decisions were based on the Hebbian principle and the intuition of multi-scale processing. One particular in carnation used in our submission for ILSVRC14 is called GoogLeNet, a 22 layers deep network, the quality of which is assessed in the context of classification and detection.

3、网络结构

3.1 网络示意图

GoogleNet结构示意图:主要包含Inception块和辅助分类器
在这里插入图片描述
Inception块示意图:a图是普通的Inception块;b图是带有1 * 1卷积并可以对输入通道降维的Inception块。
在这里插入图片描述

3.2 Inception块

在这里插入图片描述
从上图不难发现,对同一个输入层,Inception块中有4条并行的线路,从左到右,第1、2、3个是1 * 1的卷积层,第4个是3 * 3的maxpool层,这4条线路的输出有相同的shape和不同的channel,最后会在通道合并层把这些输出的channel维度合并起来。

Inception块的特点:
1)Inception块之间可以堆叠使用
2)添加了1 * 1卷积层,降低了输入层的通道维度,避免参数过量影响训练。
3)多卷积核和池化的并行结构,通过通道合并层进行合并channel。

3.3 网络设计

GoogleNet:整个网络使用了9个Inception块,网络深度为22层,maxpool池化层为5层。

网络中间添加了一些辅助分类器,原因是网络的深度比较深,整个模型的训练时间比较长,并且浅层网络可能对中间层产生的特征具有较强的识别能力。
在这里插入图片描述

4、归纳总结

4.1 deep的两层含义

1)在某种意义上,表示以“初始模块”的形式引入了新的组织层次。
2)在直接意义上,表示网络的深度足够深。

4.2 为什么使用1 * 1卷积层

目的是主要用来降维,通过降低通道数来减少参数量。
效果是同等参数量模型的深度更深,宽度更宽。

4.3 动机和考虑

提高卷积神经网络模型表现最直接的方法是增加网络尺寸:
1)增加层数,增加深度
2)增加网络宽度

网络尺寸的增加会带来问题:
1)更大的参数规模会更容易过拟合
2)模型训练时会占用更多的计算机资源

解决上面问题的方法是引入稀疏型,用稀疏的layer代替全连接层,甚至于卷积内部。

4.4 结构细节

Inception结构的主要思想是考虑如何近似地估计卷积视觉网络的最佳局部稀疏结构,并方便地用Dense组件来实现。

论文中提出了一种逐层构建的方法,分析上一层的相关统计量,并将其聚类为具有高度相关性的unit单元组,然后把这些单元组作为下一层的输入。

具体操作是用一组filter提取上一层的特征信息,并在通道维上串联聚合,形成一整个连结层。目前Inception块中的filter尺寸固定在1 * 1,3 * 3,5 * 5的原因是考虑便利性,因为大尺寸filter和小尺寸filter的混合使用会导致卷积后的尺寸对齐不方便。
在这里插入图片描述

4.5 总结

与较窄和较窄的网络结构相比,用现有dense结构来组合构建出最佳的稀疏结构的优点是在计算量适度增加的情况下,显著提升了网络效果。

二、深度学习

卷积神经网络的层级结构

1、输入层

数据输入层:主要是对原始图形数据进行数据预处理
1)去均值:把输入数据各个维度都中心化为0,其目的是把样本的中心拉回到原点上。
2)归一化:幅度归一化到相同的范围,即减少各维度数据取值范围的差异而带来的干扰。
3)PCA/白化:PCA降维度,白化是对数据各个特征轴上的幅度归一化。

2、卷积层

卷积层的两个关键操作:
1)局部关联,每个神经元看做一个滤波器filter。
2)窗口滑动,filter对局部数据进行计算。

卷积层的作用是通过卷积核的过滤提取出图片中局部的特征。

通过卷积提取到的特征,我们可以得到特征图。比如一个二维的像素图,每个特征是略小于原图的二维图,特征图就是n个特征叠加在一起的一个三维图。 ​

3、激活层

激活层是把卷积层输出结果做非线性映射,目的是在特征提取时,舍弃掉无关联的数据。

ReLU激活函数:ReLU(x)=max{0,x}
在这里插入图片描述
作用:更加有效率的梯度下降以及反向传播,避免了梯度爆炸和梯度消失问题。

4、池化层

池化的作用:压缩数据和参数的量,减少过拟合。
1)特征不变性,压缩无关紧要的信息,合并在卷积中所有局部特征的共有特征。
2)特征降维,删除信息量。
3)一定程度防止过拟合

池化可以被替代吗?
可以,因为池化层会损失大量的信息,用卷积层代替池化效果会更好,但依然要保留池化层,因为池化层没有需要学习的参数,因此完全使用卷积层代替池化层,会使参数量变大,增大计算量。

池化卷积的叠加会带来什么效果?
保留主要的特征的同时减少参数和计算量,防止过拟合,提高模型的泛化能力。

5、全连接层

全连接层是对特征图进行维度上的改变,以此得到每个类别对应的概率。

Flatten:张量扁平化,在全连接层接受输入之前,需要把传递给全连接层的卷积层输出进行扁平化。

全连接层能否使用dropout?
全连接层可以使用dropout,dropout会随机删除神经网络中的部分神经,能够有效缓解过拟合的发生,在一定程度上达到正则化的效果。但卷积层一般使用dropout,因为卷积层参数少,不易过拟合。

最终分类的多少与前面各层有没有对应关系?
在图像分类中,最终将图像分为多少类,一般是由CNN中最后一个全连接层神经元个数确定,也就是说要将图像分为多少类,即最后一个全连接层就为多大。

CNN损失函数

1、交叉熵损失函数表达式

在二分的情况下,模型最后需要预测的结果只有两种情况,对于每个类别我们的预测得到的概率为p和1 - p,二分类的表达式:
在这里插入图片描述
其中:
yi表示样本i的标签,正类为1,负类为0;
pi表示样本i预测为正类的概率。

多分类表达式:
在这里插入图片描述
其中:
M表示类别的数量;
yic表示符号函数,如果样本i真实类别等于c取1,否则取0;
Pic表示样本i属于类别c的预测概率。

2、二分类表达式函数求导

在这里插入图片描述
在这里插入图片描述

3、多分类表达式函数求导

在这里插入图片描述
多分类和二分类的区别在于多分类只有一个类别为1,其他类别都为0。
在这里插入图片描述
在CNN中,损失函数用于计算CNN的输出结果与标签结果的偏差,之后用于反向传播更新梯度。通过不断地训练和优化CNN中的参数,目的是将损失函数最小化,最终学习得到最佳的CNN模型。

完整代码(CNN手写数字识别)

import time

import numpy as np
import torch
from torch import nn
from PIL import Image
import matplotlib.pyplot as plt
import os
import torch.nn.functional as F

from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from torchvision import datasets, transforms, utils

# 定义训练的设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

# 准备数据集
transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize(mean=[0.5], std=[0.5])])

train_data = datasets.MNIST(root="../data", train=True, transform=transform, download=True)

test_data = datasets.MNIST(root="../data", train=False, transform=transform, download=True)

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# print("训练数据集的长度为:{}".format(train_data_size))
# print("测试数据集的长度为:{}".format(test_data_size))

# 利用 DataLoader 来加载数据集
train_dataloader = DataLoader(train_data, batch_size=32, shuffle=True, pin_memory=True)
test_dataloader = DataLoader(test_data, batch_size=32, shuffle=True)
train_dataloader_size = len(train_data)
test_dataloader_size = len(test_data)


# print(train_dataloader_size)
# print(test_dataloader_size)

# # 从二维数组生成一张图片
# img, label = train_data[0]
# img = img.numpy().transpose(1, 2, 0)
# std = [0.5]
# mean = [0.5]
# img = img * std + mean
# # img.resize(20, 20)
# plt.imshow(img)
# plt.show()

# # 从三维数组生成一张黑白图片
# img, label = train_data[0]
# grid = utils.make_grid(img)
# grid = grid.numpy().transpose(1, 2, 0)
# std = [0.5]
# mean = [0.5]
# grid = grid * std + mean
# plt.imshow(grid)
# plt.show()

# # 输出一个batch的图片和标签
# dataiter = iter(train_dataloader)
# imgs, labels = dataiter.next()
# img = utils.make_grid(imgs)
# img = img.numpy().transpose(1, 2, 0)
# std = [0.5]
# mean = [0.5]
# img = img * std + mean
# for i in range(64):
#     print(labels[i], end="")
#     i += 1
#     if i % 8 == 0:
#         print(end='\n')
# plt.imshow(img)
# plt.show()

# 创建网络模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 4 * 4, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):

        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2, 2)
        x = x.view(x.shape[0], -1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return F.log_softmax(x, dim=1)


net = CNN()
net = net.to(device)

# 损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)

# 优化器
learning_rate = 1e-2
optimizer = torch.optim.SGD(net.parameters(), lr=learning_rate, momentum=0.5)

# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("../logs_train")
start_time = time.time()

for i in range(epoch):
    print("--------第 {} 轮训练开始--------".format(i + 1))

    # 训练步骤开始
    net.train()
    for data in train_dataloader:
        imgs, targets = data
        imgs = imgs.to(device)
        targets = targets.to(device)
        ouputs = net(imgs)
        loss = loss_fn(ouputs, targets)

        # 优化器优化模型
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_train_step = total_train_step + 1
        if total_train_step % 100 == 0:
            end_time = time.time()
            print(end_time - start_time)
            print("训练次数:{},Loss:{}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(), total_train_step)

    # 测试步骤开始
    net.eval()
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in test_dataloader:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            ouputs = net(imgs)

            loss = loss_fn(ouputs, targets)
            total_test_loss = total_test_loss + loss
            accuracy = (ouputs.argmax(1) == targets).sum()
            total_accuracy = total_accuracy + accuracy

        print("整体测试集上的Loss:{}".format(total_test_loss))
        print("整体测试集上的正确率:{}".format(total_accuracy / test_data_size))
        writer.add_scalar("test_loss", total_test_loss, total_test_step)
        writer.add_scalar("test_accuracy", total_accuracy / test_data_size, total_test_step)
        total_test_step = total_test_step + 1

        torch.save(net, "net_{}.pth".format(i))
        print("模型已保存")

writer.close()

总结

GoogleNet通过并行的filter将输入特征提取,并在通道维上进行串联合并,构成下一层的输入,再将Inception块层层叠加。为了降低整体参数量,在Inception块中添加了1 * 1的卷积层,有效地降低了特征维度,避免了参数量过大。在本周的学习中,我学习了卷积神经网络的相关内容,比如卷积、池化、Flatten等,让自己对卷积神经网络有了更深的理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值