当前位置: 首页 > news >正文

深度学习——现代卷积神经网络(七)

深度卷积神经网络

学习表征

  1. 观察图像特征的提取⽅法。
  2. 在合理地复杂性前提下,特征应该由多个共同学习的神经⽹络层组成,每个层都有可学习的参数。

当年缺少数据和硬件支持

AlexNet

  1. AlexNet⽐相对较⼩的LeNet5要深得多。 AlexNet由⼋层组成:五个卷积层、两个全连接隐藏层和⼀个全连接输出层。
  2. AlexNet使⽤ReLU⽽不是sigmoid作为其激活函数。

模型设计

在AlexNet的第⼀层,卷积窗⼝的形状是11×11。由于ImageNet中⼤多数图像的宽和⾼⽐MNIST图像的多10倍以上,因此,需要⼀个更⼤的卷积窗⼝来捕获⽬标。第⼆层中的卷积窗⼝形状被缩减为5× 5,然后是3× 3。此外,在第⼀层、第⼆层和第五层卷积层之后,加⼊窗⼝形状为3× 3、步幅为2的最⼤汇聚层。⽽且, AlexNet的卷积通道数⽬是LeNet的10倍。

激活函数

AlexNet将sigmoid激活函数改为更简单的ReLU激活函数。

  1. ReLU激活函数的计算更简单
  2. 当使⽤不同的参数初始化⽅法时, ReLU激活函 数使训练模型更加容易。

容量控制

AlexNet通过暂退法(4.6节)控制全连接层的模型复杂度,⽽LeNet只使⽤了权重衰减。为了进⼀步扩充数据, AlexNet在训练时增加了⼤量的图像增强数据,如翻转、裁切和变⾊。这使得模型更健壮,更⼤的样本量有效地减少了过拟合。

使用块的网络(VGG)

经典的卷积神经网络基本组成部分:

  1. 带填充以保持分辨率的卷积层;
  2. ⾮线性激活函数,如ReLU;
  3. 汇聚层,如最⼤汇聚层。

而⼀个VGG块与之类似,由⼀系列卷积层组成,后⾯再加上⽤于空间下采样的最⼤汇聚层。

VGG块

import torch
import torch.nn as nn 
import d2l.torch as d2l
# 卷积层的数量num_convs、输⼊通道的数量in_channels 和输出通道的数量out_channels.
def Vgg_block(num_convs, in_channels, out_channels):layers = []for _ in range(num_convs):layers.append(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))layers.append(nn.ReLU())in_channels = out_channelslayers.append(nn.MaxPool2d(kernel_size=2, stride=2))return nn.Sequential(*layers)

VGG网络

# 超参数变量conv_arch。该变量指定了每个VGG块⾥卷积层个数和输出通道数。
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
in_channels = 1
def VGG(in_channels, conv_arch):conv_blks = []for (num_convs, out_channels) in conv_arch:conv_blks.append(Vgg_block(num_convs, in_channels, out_channels))in_channels = out_channelsnet = nn.Sequential(*conv_blks, nn.Flatten(),nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5),nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5),nn.Linear(4096, 10))return netnet = VGG(in_channels, conv_arch)
net

网络中的网络(NiN)

LeNet、 AlexNet和VGG都有⼀个共同的设计模式:通过⼀系列的卷积层与汇聚层来提取空间结构特征;然后通过全连接层对特征的表征进⾏处理。 AlexNet和VGG对LeNet的改进主要在于如何扩⼤和加深这两个模块。** **
卷积层的输⼊和输出由四维张量组成,张量的每个轴分别对应样本、通道、⾼度和宽度。 NiN的想法是在每个像素位置(针对每个⾼度和宽度)应⽤⼀个全连接层 ,

如果我们将权重连接到每个空间位置,我们可以将其视为1 × 1卷积层 。从另⼀个⻆度看,即将空间维度中的每个像素视为单个样本,将通道维度视为不同特征(feature)。
NiN块以⼀个普通卷积层开始,后⾯是两个1 × 1的卷积层。这两个1 × 1卷积层充当带有ReLU激活函数的逐像素全连接层。

import torch 
import torch.nn as nn
import d2l.torch as d2ldef nin_block(in_channels, out_channels, kernel_size, strides, padding):return nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),nn.ReLU(),nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU(),nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU())net = nn.Sequential(nin_block(20, 96, kernel_size=11, strides=4, padding=0),nn.MaxPool2d(3, stride=2),nin_block(96, 256, kernel_size=5, strides=1, padding=2),nn.MaxPool2d(3, stride=2),nin_block(256, 384, kernel_size=3, strides=1, padding=1),nn.MaxPool2d(3, stride=2),nn.Dropout(0.5),# 标签类别数是10nin_block(384, 10, kernel_size=3, strides=1, padding=1),nn.AdaptiveAvgPool2d((1, 1)),# 将四维的输出转成⼆维的输出,其形状为(批量⼤⼩,10)nn.Flatten())X = torch.rand(size=(16, 20, 224, 224))
for layer in net:X = layer(X)print(layer.__class__.__name__,'output shape:\t', X.shape)lr, num_epochs, batch_size = 0.1, 10, 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

含并行连结的网络

inception块

在GoogLeNet中,基本的卷积块被称为Inception块(Inception block)。

import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2lclass Inception(nn.Module):# c1--c4是每条路径的输出通道数def __init__(self, in_channels, c1, c2, c3, c4, **kwargs):super(Inception, self).__init__(**kwargs)# 线路1,单1x1卷积层self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)# 线路2, 1x1卷积层后接3x3卷积层self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)# 线路3, 1x1卷积层后接5x5卷积层self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1)self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)# 线路4, 3x3最⼤汇聚层后接1x1卷积层self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1)def forward(self, x):p1 = F.relu(self.p1_1(x))p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))p4 = F.relu(self.p4_2(self.p4_1(x)))# 在通道维度上连结输出return torch.cat((p1, p2, p3, p4), dim=1)

GoogLeNet模型

批量规范化

训练深层神经⽹络是⼗分困难的,特别是在较短的时间内使他们收敛更加棘⼿。本节将介绍批量规范化(batch normalization),这是⼀种流⾏且有效的技术,可持续加速深层⽹络的收敛速度。再结合后面将介绍的残差块,批量规范化使得研究⼈员能够训练100层以上的⽹络。

、训练深层网络

为什么要批量规范化?

  • ⾸先,数据预处理的⽅式通常会对最终结果产⽣巨⼤影响。
  • 第⼆,对于典型的多层感知机或卷积神经⽹络。当我们训练时,中间层中的变量(例如,多层感知机中的仿射变换输出)可能具有更⼴的变化范围:不论是沿着从输⼊到输出的层,跨同⼀层中的单元,或是随着时间的推移,模型参数的随着训练更新变幻莫测。
  • 第三,更深层的⽹络很复杂,容易过拟合。这意味着正则化变得更加重要。
    批量规范化应⽤于单个可选层(也可以应⽤到所有层),其原理如下:在每次训练迭代中,我们⾸先规范化输⼊,即通过减去其均值并除以其标准差,其中两者均基于当前⼩批量处理。接下来,我们应⽤⽐例系数和⽐例偏移。正是由于这个基于批量统计的标准化,才有了批量规范化的名称。


    注意:我们在⽅差估计值中添加⼀个⼩的常量ϵ > 0,以确保我们永远不会尝试除以零,即使在经验⽅差估计值可能消失的情况下也是如此。估计值µ^Bσ^ B通过使⽤平均值和⽅差的噪声(noise)估计来抵消缩放问题。乍看起来,这种噪声是⼀个问题,⽽事实上它是有益的。
  • 第四,批量规范化层在”训练模式“(通过⼩批量统计数据规范化)和“预测模式”(通过数据集统计规范化)中的功能不同。在训练过程中,我们⽆法得知使⽤整个数据集来估计平均值和⽅差,所以只能根据每个⼩批次的平均值和⽅差不断训练模型。⽽在预测模式下,可以根据整个数据集精确计算批量规范化所需的平均值和⽅差。

、批量规范化层

批量规范化和其他层之间的⼀个关键区别是,由于批量规范化在完整的⼩批量上运⾏,因此我们
不能像以前在引⼊其他层时那样忽略批量⼤⼩。

我们可以分为两种不同情况进行讨论:全连接层和卷积层

全连接层

我们将批量规范化层置于全连接层中的仿射变换和激活函数之间。设全连接层的输⼊为x,权重参数和偏置参数分别为Wb,激活函数为ϕ,批量规范化的运算符为BN。那么,使⽤批量规范化的全连接层的输出的计算详情如下:

卷积层

对于卷积层,我们可以在卷积层之后和⾮线性激活函数之前应⽤批量规范化。 当卷积有多个输出通道
时,我们需要对这些通道的“每个”输出执⾏批量规范化,每个通道都有⾃⼰的拉伸(scale)和偏移(shif)参数,这两个参数都是标量。

代码实现

# 1. 导入库
import torch
from torch import nn
from d2l import torch as d2l# 2. 批量规范化实现def batch_norm(x, gamma, beta, moving_mean, moving_var, eps, momentum):# 通过is_grad_enabled来判断当前模式是训练模式还是预测模式if not torch.is_grad_enabled():# 如果是在预测模式下,直接使⽤传⼊的移动平均所得的均值和⽅差X_hat = (x - moving_mean) / torch.sqrt(moving_var + eps)else:assert len(x.shape) in (2, 4)if len(x.shape) == 2:mean = x.mean(dim = 0)var = ((x - mean) ** 2).mean(dim=0)else:# 使⽤⼆维卷积层的情况,计算通道维上(axis=1)的均值和⽅差。# 这⾥我们需要保持X的形状以便后⾯可以做⼴播运算mean = x.mean(dim=(0, 2, 3), keepdim=True)var = ((x - mean) ** 2).mean(dim=(0, 2, 3), keepdim=True)# 训练模式下,⽤当前的均值和⽅差做标准化X_hat = (x - mean) / torch.sqrt(var + eps)# 更新移动平均的均值和⽅差moving_mean = momentum * moving_mean + (1.0 - momentum) * meanmoving_var = momentum * moving_var + (1.0 - momentum) * varY = gamma * X_hat + beta # 缩放和移位return Y, moving_mean.data, moving_var.data

BatchNorm层

这个层将保持适当的参数:拉伸gamma和偏移beta,这两个参数将在训练过程中更新

# BatchNorm层实现
class BatchNorm(nn.Module):# num_features:完全连接层的输出数量或卷积层的输出通道数。# num_dims: 2表⽰完全连接层, 4表⽰卷积层def __init__(self, num_features, num_dims):super().__init__()if num_dims == 2:shape = (1, num_features)else:shape = (1, num_features, 1, 1)# 参与求梯度和迭代的拉伸和偏移参数,分别初始化成1和0self.gamma = nn.Parameter(torch.ones(shape))self.beta = nn.Parameter(torch.zeros(shape))# ⾮模型参数的变量初始化为0和1self.moving_mean = torch.zeros(shape)self.moving_var = torch.ones(shape)def forward(self, x):# 如果X不在内存上,将moving_mean和moving_var# 复制到X所在显存上if self.moving_mean.device != x.device:self.moving_mean = self.moving_mean.to(x.device)self.moving_var = self.moving_var.to(x.device)# 保存更新过的moving_mean和moving_varY, self.moving_mean, self.moving_var = batch_norm(x, self.gamma, self.beta, self.moving_mean,self.moving_var, eps=1e-5, momentum=0.9)return Y

使⽤批量规范化层的 LeNet

import torch.nn as nn 
import d2l.torch as d2l
import torch
net = nn.Sequential(nn.Conv2d(1, 6, kernel_size=5), BatchNorm(6, num_dims=4), nn.Sigmoid(),nn.AvgPool2d(kernel_size=2, stride=2),nn.Conv2d(6, 16,kernel_size=5), BatchNorm(16, num_dims=4), nn.Sigmoid(),nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),nn.Linear(16 * 4 * 4, 120),BatchNorm(120, num_dims=2), nn.Sigmoid(),nn.Linear(120, 84),BatchNorm(84, num_dims=2), nn.Sigmoid(), nn.Linear(84, 10))
lr, num_epochs, batch_size = 1.0, 30, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())print(net[1].gamma.reshape((-1,)), net[1].beta.reshape((-1,)))

残差神经网络(ResNet)

对于深度神经⽹络,如果我们能将新添加的层训练成恒等映射(identity function) f(x) = x,新模型和原模型将同样有效。同时,由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。
残差⽹络的核⼼思想是:每个附加层都应该更容易地包含原始函数作为其元素之⼀。

⽣成两种类型的⽹络:⼀种是当use_1x1conv=False时,应⽤ReLU⾮线性函数之前,将输⼊添加到输出。另⼀种是当use_1x1conv=True时,添加通过1 × 1卷积调整通道和分辨率

、残差块实现

# 残差块
import torch
import torch.nn as nn 
import d2l.torch as d2l
import torch.nn.functional as Fclass Residual(nn.Module):def __init__(self, input_channels,num_channels, use_1x1_conv = False, strides = 1):super().__init__()self.conv1 = nn.Conv2d(input_channels, num_channels, kernel_size=3,stride=strides, padding = 1)self.bn1 = nn.BatchNorm2d(num_channels)self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3,  padding = 1)self.bn2 = nn.BatchNorm2d(num_channels)if use_1x1_conv:self.conv3 = nn.Conv2d(input_channels, num_channels,kernel_size=3, stride=strides, padding=1)else:self.conv3 = Nonedef forward(self, X):Y = F.relu(self.bn1(self.conv1(X)))Y = self.bn2(self.conv2(Y))if self.conv3:X = self.conv3(X)Y += Xreturn F.relu(Y)

残差神经网络模型

  • ResNet-18

import torch
import torch.nn as nn def res_block(input_channels, num_channels, num_residuals, first_block = False):blk = []for i in range(num_residuals):if i == 0 and not first_block:blk.append(Residual(input_channels, num_channels, use_1x1_conv=True, strides=2))else:blk.append(Residual(input_channels=input_channels, num_channels=num_channels))return blk# 1. 卷积层
b1 = nn.Sequential(nn.Conv2d(1, 64,kernel_size=7,stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.AvgPool2d(kernel_size=3, stride=2, padding=1))
# 2. 残差层
b2 = nn.Sequential(*res_block(64,64, num_residuals=2, first_block=True))
b3 = nn.Sequential(*res_block(64,128, num_residuals=2))
b4 = nn.Sequential(*res_block(128,256, num_residuals=2))
b5 = nn.Sequential(*res_block(256,512, num_residuals=2))# 3. 汇聚层和全连接层
# ResNet-18
net = nn.Sequential(b1, b2, b3, b4, b5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(), nn.Linear(512, 10))'''
每个模块有4个卷积层(不包括恒等映射的1 × 1卷积层)。
加上第⼀个7 × 7卷积层和最后⼀个全连接层,共
有18层。因此,这种模型通常被称为ResNet-18。
'''

查看在ResNet中不同模块的输入形状是怎么变化的

X = torch.rand(size=(1, 1, 224, 224))
for layer in net:X = layer(X)print(layer.__class__.__name__,'output shape:\t', X.shape)

:::color1
Sequential output shape: torch.Size([1, 64, 56, 56])
Sequential output shape: torch.Size([1, 64, 56, 56])
Sequential output shape: torch.Size([1, 128, 28, 28])
Sequential output shape: torch.Size([1, 256, 14, 14])
Sequential output shape: torch.Size([1, 512, 7, 7])
AdaptiveAvgPool2d output shape: torch.Size([1, 512, 1, 1])
Flatten output shape: torch.Size([1, 512])
Linear output shape: torch.Size([1, 10])

:::

模型训练

# 模型训练
lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

http://www.mrgr.cn/news/80562.html

相关文章:

  • vue3设置全局css变量
  • 数据结构—图
  • BERTective: Language Models and Contextual Information for Deception Detection
  • SpringCloud无介绍快使用,sentinel注解@SentinelResource的基本使用(二十三)
  • Python的基础知识
  • steel-browser - 专为AI应用构建的开源浏览器自动化 API
  • VSCode编辑+GCC for ARM交叉编译工具链+CMake构建+OpenOCD调试(基于STM32的标准库/HAL库)
  • FreeBSD安装教程
  • HanLP 2.x 的安装与使用
  • CTFHub ssrf
  • python 配置 oracle instant client
  • 【已解决】启动此实时调试器时未使用必需的安全权限。要调试该进程,必须以管理员身份运行此实时调试器。是否调试该进程?
  • 【计算机网络】期末考试预习复习|上
  • 本地部署大模型QPS推理测试
  • gRPC拦截器
  • 【50】Android自定义身份证拍照相机
  • 【前后端】HTTP网络传输协议
  • Qt设置部件的阴影效果
  • 12.17双向链表,循环链表
  • (2024年最新)Linux(Ubuntu) 中配置静态IP(包含解决每次重启后配置文件失效问题)
  • PSIM不放接地原件
  • 那些不熟悉的C语言关键字-const
  • 3D视觉[一]3D计算机视觉
  • QT5.9.9+ARM开发环境搭建【详细步骤】
  • 3.2.1.2 汇编版 原子操作 CAS
  • Canoe CAPL编程