简述网站开发平台及常用工具,建筑招投标网官网,注册人力资源公司需要什么条件,wordpress如何配置深度卷积神经网络#xff08;AlexNet#xff09;
经典机器学习的流水线#xff1a;
①获取一个有趣的数据集#xff1b;
②根据光学、几何学#xff0c;手动对特征数据集进行预处理#xff1b;
③通过标准的特征提取算法#xff0c;如SIFT#xff08;尺度不变特征变…深度卷积神经网络AlexNet
经典机器学习的流水线
①获取一个有趣的数据集
②根据光学、几何学手动对特征数据集进行预处理
③通过标准的特征提取算法如SIFT尺度不变特征变换和SURF加速鲁棒特征或其他手动调整的流水线来输入数据
④将提取的特征送入最喜欢的分类器中
学习表征
特征应该由多个共同学习的神经网络层组成每个层都有可学习的参数。
在机器视觉中底层可能检测边缘、颜色和纹理。在网络的底层模型学习到了一些类似于传统滤波器的特征提取器 AlexNet比较小的LeNet要深得多 AlexNet由8层组成5个卷积层、2个全连接隐藏层和1个全连接输出层AlexNet使用ReLU而不是sigmoid作为其激活函数 LeNet简介
模型设计
在AlexNet的第一层卷积窗口的形状是11*11由于ImageNet中大多数图像的高和宽比MNIST图像的大10倍以上因此需要一个更大的卷积窗口来捕获目标。第二层中的卷积窗口形状被缩减为5*5然后是3*3.在第1层、第二层、第五层卷积层之后加入窗口形状为3*3、步幅为2的最大池化层。AlexNet的卷积通道数是LeNet的10倍
容量控制和预处理 暂退法在前向传播过程中计算每一内部层的同时注入噪声。 AlexNet通过暂退法控制全连接层的模型复杂度而LeNet只使用了权重衰减
import torch
from torch import nn
from d2l import torch as d2lnet nn.Sequential(nn.Conv2d(1, 96, kernel_size11, stride4, padding1), nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2),# 减小卷积窗口使用填充为2来使得输入和输出的高和宽一致nn.Conv2d(96, 256, kernel_size5, padding2), nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2),# 使用3个连续的卷积层和较小的卷积窗口除了最后的卷积层输出通道数进一步增加nn.Conv2d(256, 384, kernel_size3, padding1), nn.ReLU(),nn.Conv2d(384, 384, kernel_size3, padding1), nn.ReLU(),nn.Conv2d(384, 256, kernel_size3, padding1), nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2),nn.Flatten(),# 全连接层的输出数量是LeNet中的好几倍,使用暂退层来缓解过拟合nn.Linear(6400, 4096), nn.ReLU(),nn.Dropout(p0.5),nn.Linear(4096, 4096), nn.ReLU(),nn.Dropout(p0.5),nn.Linear(4096, 10)
)
接下来我们构造高度和宽度都为224的单通道数据来观察每一层输出的形状
x torch.randn(1, 1, 224, 224)
print(x)
for layer in net:x layer(x)print(layer.__class__.__name__, output shape:\t, x.shape)
读取数据集
batch_size 128
# resize224是因为Fashion-MNIST图像的分辨率28*28像素低于ImageNet图像224*224
train_iter, test_iter d2l.load_data_fashion_mnist(batch_size, resize224)
训练AlexNet
相比于LeNet我们需要使用更低的学习率训练因为网络更深更广、图像分辨率更高训练卷积神经网络的成本更高
使用块的网络VGG
经典卷积神经网络的基本组成是
(1)带填充以保持分辨率的卷积层
(2)非线性激活函数
(3)池化层 一个VGG块由一系列卷积层组成后面再加上用于空间降采样的最大池化层。 # 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_size3, padding1))layers.append(nn.ReLU())in_channels out_channels# 向 layers 列表添加一个最大池化层其池化核大小为 2x2步长为 2。这将使空间尺寸减半layers.append(nn.MaxPool2d(kernel_size2, stride2))return nn.Sequential(*layers) VGG网络 第一部分由卷积层和池化层组成第二部分由全连接层组成 原始VGG网络有5个卷积块其中前2个块各包含1个卷积层后3个块各包含2个卷积层第一个块有64个输出通道后续每个块将输出通道数翻倍直到输出通道数为512由于该网络使用8个卷积层和3个全连接层因此称为VGG-11
# conv_arch指定了每个VGG块中卷积层个数和输出通道数
conv_arch ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
def vgg(conv_arch):conv_blks []in_channels 1# 卷积层部分for (num_convs, out_channels) in conv_arch:conv_blks.append(vgg_block(num_convs, in_channels, out_channels))in_channels out_channelsreturn nn.Sequential(*conv_blks, nn.Flatten(),# nn.Dropout(0.5) 时它会在训练过程中随机将输入张量中的一半50%的元素设置为 0# dropout 只在训练过程中使用。在评估或测试模型时通常不使用 dropout因此所有神经元都会被激活# 全连接层部分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))
net vgg(conv_arch)
接下来我们构建一个高度和宽度都为224的单通道数据样本以观察每个层输出的形状
x torch.randn(size(1, 1, 224, 224))
for blk in net:x blk(x)print(blk.__class__.__name__,output shape:\t, x.shape)
我们要将每个块的高度和宽度减半最后高度和宽度都为7。最后展平表示进入全连接层处理
训练模型
# 由于VGG-11比AlexNet的计算量更大# 因此构建一个通道数较少的网络足够用于训练Fashion-MNIST数据集
ratio 4
# 对于conv_arch中的每个元组对pair它都会创建一个新的元组对
# 其中第一个元素保持不变即卷积层个数而第二个元素则是原始通道数除以ratio即4。
# //是Python中的整数除法所以结果将是一个整数。
# 这行代码的目的是减少每个卷积层的输出通道数从而得到一个“较小”的卷积架构
small_conv_arch [(pair[0], pair[1] // ratio) for pair in conv_arch]
net vgg(small_conv_arch)
网络中的网络NiN
在每个像素的通道上分别使用多层感知机
NiN块
卷积层的输入和输出由四维张量组成张量的每个轴分别对应样本、通道、高度和宽度。另外全连接层的输入和输出通常是分别对应于样本和特征的二维张量。 NiN的想法是在每个像素位置针对每个高度和宽度应用一个全连接层。如果将权重连接到每个空间位置我们可以将其视为1*1卷积层将空间维度中的每个像素视为单个样本将通道维度视为不同特征 NiN块以一个普通卷积层开始后面是两个1*1的卷积层。这两个1*1卷积层充当带有ReLU激活函数的逐像素全连接层。第一层的卷积窗口形状通常由用户设置随后的卷积窗口形状固定为1*1
import torch
from torch import nn
from d2l import 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_size1), nn.ReLU(),nn.Conv2d(out_channels, out_channels, kernel_size1), nn.ReLU())
NiN模型
NiN使用窗口形状为11*115*5和3*3的卷积层输出通道数与AlexNet中的相同。每个NiN块后有一个最大池化层池化窗口形状为3*3步幅为2 NiN和AlexNet之间的一个显著区别是NiN模型完全取消了全连接层而使用一个NiN块其输出通道数等于标签类别数最后放一个全局平均池化层生成一个对数几率NiN显著减少了模型所需参数的数量但是会增加训练模型的时间 net nn.Sequential(nin_block(1, 96, kernel_size11, strides4, padding0),nn.MaxPool2d(3, stride2),nin_block(96, 256, kernel_size5, strides1, padding2),nn.MaxPool2d(3, stride2),nin_block(256, 384, kernel_size3, strides1, padding1),nn.MaxPool2d(3, stride2),nn.Dropout(0.5),# 标签类别数是10nin_block(384, 10, kernel_size3, strides1, padding1),# nn.AdaptiveAvgPool2d((1, 1)) 是一个自适应平均池化层# 它会根据输入张量的大小自适应地调整池化窗口的大小以产生一个特定大小在此例中是 1x1的输出。# 这通常用于在卷积神经网络的末尾将不同尺寸的特征图转换为固定尺寸的表示以便于后续的全连接层处理nn.AdaptiveAvgPool2d((1, 1)),# 将四维的输出转为二维的输出其形状为批量大小 10nn.Flatten()
)
模型块的输出
# 创建一个数据样本来查看每个块的输出形状
x torch.rand(size(1, 1, 224, 224))
for layer in net:x layer(x)print(layer.__class__.__name__,output shape:\t, x.shape)
含并行连接的网络GoogLeNet
Inception块 在GoogLeNet中基本的卷积块称为Inception块。 4条并行路径组成。前3条路径使用窗口大小为1*13*3和5*5的卷积层从不同空间大小中提取信息中间的2条路径在输入上执行1*1卷积以减少通道数从而降低模型的复杂度第4条路径使用3*3最大池化层然后使用1*1卷积层来改变通道数这4条路径都使用合适的填充以使输入与输出的高度和宽度一致最后将多条路径的输出在通道维度上合并Inception块中通常调整的超参数是每层输出通道数 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单1*1卷积层self.p1_1 nn.Conv2d(in_channels, c1, kernel_size1)# 路径2 1*1卷积层后接3*3卷积层self.p2_1 nn.Conv2d(in_channels, c2[0], kernel_size1)self.p2_2 nn.Conv2d(c2[0], c2[1], kernel_size3, padding1)# 路径3 1*1卷积层后接5*5卷积层self.p3_1 nn.Conv2d(in_channels, c3[0], kernel_size1)self.p3_2 nn.Conv2d(c3[0], c3[1], kernel_size5, padding2)# 路径4 3*3卷积层后接1*1卷积层self.p4_1 nn.MaxPool2d( kernel_size1, stride1, padding1)self.p4_2 nn.Conv2d(in_channels, c4, kernel_size1)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), dim1)
GoogleNet模型
GoogleNet模型一共使用9个Inception块和全局平均池化层的堆叠来生成其估计值
Inception块之间的最大池化层可降低维度
全局平均池化层避免了在最后使用全连接层
GoogleNet模块
第一个模块输出64个通道7*7卷积层
b1 nn.Sequential(nn.Conv2d(1, 64, kernel_size7, stride2, padding3),nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2, padding1))
第二个模块使用两个卷积层第一个卷积层是64个通道、1*1卷积层; 第二个卷积层使用将通道数增加为3的3*3卷积层
b2 nn.Sequential(nn.Conv2d(64, 64, kernel_size1),nn.ReLU(),nn.Conv2d(64, 192, kernel_size3, padding1),nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2, padding1)) 第三个模块串联两个完整的Inception块。第一个Inception块的输出通道数为641283232256 4条路径的输出通道数之比为641283232 2411 第二条和第三条路径首先将输入通道数分别减少到96/1921/2,和16/1921/12然后连接第二个卷积层。第二个Inception块的输出通道数增加到12819296644804条路径的输出通道数之比为1281929664 4632。第二条路径和第三条路径先将输入通道数分别减少到128/2561/2和32/2561/8 # 输入通道数: 192
# 第一条路径的输出通道数: 164
# 第二条路径的输出通道数两个数字表示两个卷积层的输出通道数: (96, 128)
# 第三条路径的输出通道数两个数字表示两个卷积层的输出通道数: (16, 32)
# 第四条路径的输出通道数: 32
# 256 64 128 32 32
b3 nn.Sequential(Inception(192, 64, (96, 128), (16, 32), 32),Inception(256, 128, (128, 92), (32, 96), 64),nn.MaxPool2d(kernel_size3, stride2, padding1)) 第四个模块
# 第四个模块更加复杂它串联了5个Inception块其输出通道数分别是1922084864512、1602246464512、1282566464512
# 1122886464528和256320128128832
# 这些路径通道数的分配和第三个模块中的类似首先是输出通道数最多的含3*3卷积层的第二条路径其次是仅含1*1卷积层的第一条路经
# 最后是含5*5卷积层的第三条路经和含3*3最大池化层的第四条路径其中第二条和第三条路径都会先按比例减少通道数
b4 nn.Sequential(Inception(480, 192, (96, 208), (16, 48), 64),Inception(512, 160, (112, 224), (24, 96), 64),Inception(512, 128, (128, 256), (24, 64), 64),Inception(512, 112, (144, 288), (32, 64), 64),Inception(528, 256, (160, 320), (32, 128), 128),nn.MaxPool2d(kernel_size3, stride2, padding1)) 第五个模块
# 第五个模块包含输出通道数为256320128128832、3843841281281024的两个Inception块
# 第五个模块的后面紧跟输出层该模块同NiN一样使用全局平均池化层将每个通道的高度和宽度变为1
# 最后我们将输出变为二维数组再连接一个输出个数为标签类别数的全连接层
b5 nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128),Inception(832, 384, (192, 384), (48, 128), 128),# 无论输入特征图的大小如何输出都将是一个 1x1 的张量nn.AdaptiveAvgPool2d((1, 1)),nn.Flatten())net nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10)) 如果我们要在Fashion-MNIST数据集上进行训练需要将输入的高度和宽度从224降到96.
GoogLeNet模型的计算比较复杂不如VGG一样便于修改通道数
x torch.rand(size(1, 1, 96, 96))
for layer in net:x layer(x)print(layer.__class__.__name__,output shape:\t,x.shape)
批量规范化 为什么需要批量规范化层 (1)数据预处理的方式通常会对最终结果产生巨大影响 (2)对于典型的多层感知机或卷积神经网络当训练时中间层中的变量可能具有更广的变化范围 (3)更深层的网络更复杂容易过拟合 训练深层网络
从形式上来说用表示一个来自小批量B的输入批量规范化BN根据以下表达式转换x 是小批量B的样本均值是小批量B的样本标准差。应用标准化后生成的小批量的均值为0单位方差为1. 和的形状与 x 相同。
批量规范化层
批量规范化层和其他层之间的一个关键区别是由于批量规范化在完整的小批量上执行因此我们不能像之前在引入其他层时那样忽略批量大小。
全连接层 假设全连接层的输入是x权重参数和偏置参数分别是W和b激活函数为批量规范化的运算符为BN。
从零开始实现批量规范化层
def batch_norm(x, gamma, beta, moving_mean, moving_var, eps, momentum):# 通过is_grad_enabled方法来判断当前模式是训练模式还是预测模式if not torch.is_grad_enabled():# 如果是在预测模式下直接使用传入的移动平均所得的均值和方差# 移动平均均值/方差在训练过程中计算并保存的# eps指的是一个很小的常数用于防止分母为0或防止数值不稳定# torch.sqrt计算的是每个元素的平方根x_hat (x - moving_mean) / torch.sqrt(moving_var eps)# 数据大致会被归一化到均值为0、方差为1的分布else:# 确保输入张量的维度是2或4assert len(x.shape) in (2, 4)# 输入的张量是2维的if len(x.shape) 2:# 在全连接层的情况计算每个特征维上的均值和方差# dim0意味着沿着批次维度计算均值和方差mean x.mean(dim0)var ((x - mean) ** 2).mean(dim0)else:# 使用二维卷积层的情况计算通道维上(axis1)的均值和方差# 我们需要保持x的形状以便后面可以做广播运算# dim(0, 2, 3)意味着沿着批次、高度和宽度维度计算均值和方差。# keepdimTrue确保输出的均值和方差张量具有与输入相同的维度mean x.mean(dim(0, 2, 3), keepdimTrue)var ((x - mean) ** 2).mean(dim(0, 2, 3), keepdimTrue)# 训练模式下用当前的均值和方差做标准化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这两个参数将在训练过程中更新。此外我们的层将保持均值和方差的移动平均值
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, eps1e-5, momentum0.9)return y
使用批量规范化层的LeNet 批量规范化是应用在卷积层或全连接层之后、相应的激活函数之前 net nn.Sequential(nn.Conv2d(1, 6, kernel_size5), BatchNorm(6, num_dims4), nn.Sigmoid(),nn.AvgPool2d(kernel_size2, stride2),nn.Conv2d(6, 16, kernel_size5), BatchNorm(16, num_dims4), nn.Sigmoid(),nn.AvgPool2d(kernel_size2, stride2), nn.Flatten(),nn.Linear(16*4*4, 120), BatchNorm(120, num_dims2), nn.Sigmoid(),nn.Linear(120, 84), BatchNorm(84, num_dims2), nn.Sigmoid(),nn.Linear(84, 10)
)
残差网络
只有当复杂的函数类包含较小的函数类时我们才能确保提高它们的性能
对于深度神经网络如果我们能将新添加的层训练为恒等函数f(x)x新模型和原模型同样有效
残差网络(ResNet)的核心思想是每个附加层都应该更容易地包含原始函数作为其元素之一
残差块
残差块里首先有2个有相同输出通道数的3*3卷积层每个卷积层后接一个批量规范化层和ReLU激活函数之后我们通过跨层数据通道跳过这两个卷积运算将输入直接加在最后的ReLU激活函数前这样的设计要求2个卷积层的输出与输入形状相同从而使它们可以相加。 残差网络是由多个残差块组成的每个残差块都包含多个卷积层、批量归一化层和激活函数等 在残差块中输入数据会经过多个卷积层的处理然后再与原始输入数据进行相加得到最终的输出数据 残差块的代码实现
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l# 该类是PyTorch中nn.Module的子类用于实现残差块Residual Block
class Residual(nn.Module): #save# 输入张量的通道数、卷积层输出的通道数、是否使用1*1卷积来改变输入张量的通道数# strides是卷积的步长默认为1def __init__(self, input_channels, num_channels, use_1x1convFalse,strides1):# 调用父类nn.Module的初始化函数super().__init__()self.conv1 nn.Conv2d(input_channels, num_channels, kernel_size3, padding1, stridestrides)# 默认步长为1self.conv2 nn.Conv2d(num_channels, num_channels, kernel_size3, padding1)if use_1x1conv:self.conv3 nn.Conv2d(input_channels, num_channels, kernel_size1, stridestrides)else:self.conv3 None# 定义两个批量归一化层它们分别用于conv1和conv2的输出self.bn1 nn.BatchNorm2d(num_channels)self.bn2 nn.BatchNorm2d(num_channels)def forward(self, x):# 将输入x通过conv1卷积层然后通过bn1批量归一化层最后应用ReLU激活函数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)
# 查看输入和输出形状一致的情况
blk Residual(3, 3)
x torch.rand(4, 3, 6, 6)
y blk(x)
y.shape# 在增加输出通道的同时减半输出的高度和宽度
blk Residual(3, 6, use_1x1convTrue, strides2)
blk(x).shape
ResNet模型
# ResNet的前两层和之前介绍的GoogleNet中的一样在输出通道数为64、步幅为2的7*7卷积层后
# 接步幅为2的3*3的最大池化层
# 不同之处在于ResNet的每个卷积层后增加了批量规范化层
b1 nn.Sequential(nn.Conv2d(1, 64, kernel_size7, stride2, padding3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2, padding1))
ResNet使用4个由残差块组成的模块每个模块使用若干输出通道数相同的残差块第一个模块的通道数同输入通道数一致但是之前使用了步幅为2的最大池化层因此无需减少高度和宽度之后的每个模块在第一个残差块里将上一个模块的通道数翻倍并将高度和宽度减半
# num_residuals指的是残差单元的数量
def resnet_block(input_channels, num_channels, num_residuals, first_blockFalse):# blk是一个列表用于存储构成该块的残差单元blk []for i in range(num_residuals):if i 0 and not first_block:# 下采样减少特征图的大小blk.append(Residual(input_channels, num_channels, use_1x1convTrue, strides2))else:# 创建一个标准的残差单元blk.append(Residual(num_channels, num_channels))return blk
# 接着在ResNet加入所有残差块每个模块使用两个残差块
b2 nn.Sequential(*resnet_block(64, 64, 2, first_blockTrue))
b3 nn.Sequential(*resnet_block(64, 128, 2))
b4 nn.Sequential(*resnet_block(128, 256, 2))
b5 nn.Sequential(*resnet_block(256, 512, 2))
# 最后与GoogleNet一样在ResNet中加入全局平均池化层以及全连接层输出
net nn.Sequential(b1, b2, b3, b4, b5,# 适应性池化,经过池化层之后变为指定的大小nn.AdaptiveAvgPool2d((1, 1)),nn.Flatten(), nn.Linear(512, 10))
# 在之前的架构中分辨率降低、通道数增加、直到平均池化层聚合所有特征
x torch.rand(size(1, 1, 224, 224))
for layer in net:x layer(x)print(layer.__class__.__name__,output shape:\t, x.shape)
稠密连接网络DenseNet ResNet将函数展开为f(x) x g(x) ResNet将f分解为两部分一个简单的线性项和一个复杂的非线性项 DenseNet和ResNet的关键区别在于DenseNet输出是连接而不是简单相加 稠密网络主要由两部分组成稠密块和过渡层。前者定义如何连接输入和输出后者则控制通道数 稠密连接网络使用了残差连接网络的“批量规范化层、激活层和卷积层”架构
import torch
from torch import nn
from d2l import torch as d2ldef conv_block(input_channels, num_channels):return nn.Sequential(nn.BatchNorm2d(input_channels), nn.ReLU(),nn.Conv2d(input_channels, num_channels, kernel_size3, padding1))
一个稠密块由多个卷积块组成每个卷积块使用相同数量的输出通道
在前向传播中,我们将每个卷积块的输入和输出在通道维度上连接
class DenseBlock(nn.Module):def __init__(self, num_convs, input_channels, num_channels):super(DenseBlock, self).__init__()layer []for i in range(num_convs):layer.append(conv_block(num_channels * i input_channels, num_channels))self.net nn.Sequential(*layer)def forward(self, x):for blk in self.net:y blk(x)# 连接通道维度上每个卷积的输入和输出x torch.cat((x, y), dim1)return x
定义一个有2个输出通道数为10的DenseBlock,使用通道数为3的输入时,我们会得到通道数为32*1023的输出
# 卷积块的通道数控制了输出通道数相对于输入通道数的增长程度;也被称为增长率
blk DenseBlock(2, 3, 10)
x torch.randn(4, 3, 8, 8)
y blk(x)
y.shape
过渡层
由于每个稠密快都会带来通道数的增加,因此使用过多会过于复杂化模型.而过渡层可以用来控制模型复杂度
通过1*1卷积层来减小通道数,并使用步幅为2的平均池化层减半高度和宽度
def transition_block(input_channels, num_channels):return nn.Sequential(nn.BatchNorm2d(input_channels), nn.ReLU(),nn.Conv2d(input_channels, num_channels, kernel_size1),nn.AvgPool2d(kernel_size2, stride2))
# 对稠密块的输出使用通道数为10的过渡层.此时输出的通道数减为10,高度和宽度均减半
blk transition_block(23, 10)
blk(y).shape
torch.Size([4, 10, 4, 4])
DenseNet模型
# 首先,DenseNet使用同ResNet一样的单卷积层和最大池化层
b1 nn.Sequential(nn.Conv2d(1, 64, kernel_size7, stride2, padding3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size3, stride2, padding1))
num_channels, growth_rate 64, 32
# growth_rate: DenseNet中每一层增加的新通道数
num_convs_in_dense_blocks [4, 4, 4, 4]
# num_convs_in_dense_blocks: 一个列表表示每个稠密块中卷积层的数量
blks []
# 初始化一个空列表blks用于存储DenseNet中的各个块
for i, num_convs in enumerate(num_convs_in_dense_blocks):# num_convs表示的是卷积层个数blks.append(DenseBlock(num_convs, num_channels, growth_rate))# 上一个稠密块的输出通道数num_channels num_convs * growth_rate# 在稠密块之间添加一个过渡层,使通道数减半if i ! len(num_convs_in_dense_blocks) - 1:# 当前通道数num_channels和下一阶段的通道数num_channels // 2即当前通道数的一半blks.append(transition_block(num_channels, num_channels // 2))num_channels num_channels // 2
# 与ResNet类似,最后连接全局池化层和全连接层来输出结果
net nn.Sequential(b1, *blks,nn.BatchNorm2d(num_channels), nn.ReLU(),nn.AdaptiveAvgPool2d((1, 1)),nn.Flatten(),nn.Linear(num_channels, 10)
) 文章转载自: http://www.morning.yfmxn.cn.gov.cn.yfmxn.cn http://www.morning.qbfwb.cn.gov.cn.qbfwb.cn http://www.morning.rfrx.cn.gov.cn.rfrx.cn http://www.morning.zqfz.cn.gov.cn.zqfz.cn http://www.morning.synlt.cn.gov.cn.synlt.cn http://www.morning.nrydm.cn.gov.cn.nrydm.cn http://www.morning.zrgx.cn.gov.cn.zrgx.cn http://www.morning.yqhdy.cn.gov.cn.yqhdy.cn http://www.morning.wjxyg.cn.gov.cn.wjxyg.cn http://www.morning.mlnbd.cn.gov.cn.mlnbd.cn http://www.morning.bxqtq.cn.gov.cn.bxqtq.cn http://www.morning.nxrgl.cn.gov.cn.nxrgl.cn http://www.morning.lbywt.cn.gov.cn.lbywt.cn http://www.morning.ydrml.cn.gov.cn.ydrml.cn http://www.morning.tzkrh.cn.gov.cn.tzkrh.cn http://www.morning.pgmyn.cn.gov.cn.pgmyn.cn http://www.morning.pwhjr.cn.gov.cn.pwhjr.cn http://www.morning.yixingshengya.com.gov.cn.yixingshengya.com http://www.morning.qykxj.cn.gov.cn.qykxj.cn http://www.morning.jsljr.cn.gov.cn.jsljr.cn http://www.morning.tpnch.cn.gov.cn.tpnch.cn http://www.morning.qhczg.cn.gov.cn.qhczg.cn http://www.morning.nqmhf.cn.gov.cn.nqmhf.cn http://www.morning.ranglue.com.gov.cn.ranglue.com http://www.morning.ddfp.cn.gov.cn.ddfp.cn http://www.morning.hsxkq.cn.gov.cn.hsxkq.cn http://www.morning.ybyln.cn.gov.cn.ybyln.cn http://www.morning.wchsx.cn.gov.cn.wchsx.cn http://www.morning.bkppb.cn.gov.cn.bkppb.cn http://www.morning.xmyrn.cn.gov.cn.xmyrn.cn http://www.morning.pqkrh.cn.gov.cn.pqkrh.cn http://www.morning.wqkfm.cn.gov.cn.wqkfm.cn http://www.morning.pkwwq.cn.gov.cn.pkwwq.cn http://www.morning.fjmfq.cn.gov.cn.fjmfq.cn http://www.morning.bfhrj.cn.gov.cn.bfhrj.cn http://www.morning.crsqs.cn.gov.cn.crsqs.cn http://www.morning.sqlh.cn.gov.cn.sqlh.cn http://www.morning.kcyxs.cn.gov.cn.kcyxs.cn http://www.morning.xsfny.cn.gov.cn.xsfny.cn http://www.morning.lxngn.cn.gov.cn.lxngn.cn http://www.morning.htbgz.cn.gov.cn.htbgz.cn http://www.morning.krywy.cn.gov.cn.krywy.cn http://www.morning.ntqgz.cn.gov.cn.ntqgz.cn http://www.morning.juju8.cn.gov.cn.juju8.cn http://www.morning.lbbrw.cn.gov.cn.lbbrw.cn http://www.morning.pfnrj.cn.gov.cn.pfnrj.cn http://www.morning.hlppp.cn.gov.cn.hlppp.cn http://www.morning.ztnmc.cn.gov.cn.ztnmc.cn http://www.morning.pcjw.cn.gov.cn.pcjw.cn http://www.morning.jggr.cn.gov.cn.jggr.cn http://www.morning.lnwdh.cn.gov.cn.lnwdh.cn http://www.morning.xcjwm.cn.gov.cn.xcjwm.cn http://www.morning.rtlth.cn.gov.cn.rtlth.cn http://www.morning.fkflc.cn.gov.cn.fkflc.cn http://www.morning.rfwkn.cn.gov.cn.rfwkn.cn http://www.morning.tfznk.cn.gov.cn.tfznk.cn http://www.morning.hbtarq.com.gov.cn.hbtarq.com http://www.morning.lkbkd.cn.gov.cn.lkbkd.cn http://www.morning.syfty.cn.gov.cn.syfty.cn http://www.morning.wrdlf.cn.gov.cn.wrdlf.cn http://www.morning.cbmqq.cn.gov.cn.cbmqq.cn http://www.morning.ypnxq.cn.gov.cn.ypnxq.cn http://www.morning.tkzqw.cn.gov.cn.tkzqw.cn http://www.morning.krlsz.cn.gov.cn.krlsz.cn http://www.morning.zbkdm.cn.gov.cn.zbkdm.cn http://www.morning.ftync.cn.gov.cn.ftync.cn http://www.morning.xpqyf.cn.gov.cn.xpqyf.cn http://www.morning.tjkth.cn.gov.cn.tjkth.cn http://www.morning.yongkangyiyuan-pfk.com.gov.cn.yongkangyiyuan-pfk.com http://www.morning.ruyuaixuexi.com.gov.cn.ruyuaixuexi.com http://www.morning.jcfqg.cn.gov.cn.jcfqg.cn http://www.morning.nlhcb.cn.gov.cn.nlhcb.cn http://www.morning.gqtw.cn.gov.cn.gqtw.cn http://www.morning.gbxxh.cn.gov.cn.gbxxh.cn http://www.morning.qwfl.cn.gov.cn.qwfl.cn http://www.morning.lgtzd.cn.gov.cn.lgtzd.cn http://www.morning.bklhx.cn.gov.cn.bklhx.cn http://www.morning.qbfwb.cn.gov.cn.qbfwb.cn http://www.morning.xnkh.cn.gov.cn.xnkh.cn http://www.morning.yjmlg.cn.gov.cn.yjmlg.cn