一、初始Pytorch卷积模块
1、卷积神经网络基本结构
(1)输入层
一般输入层的大小应该是能够以被2整出多次的,常用的数字包括32,64,96,224。可以将图片缩放到常用尺寸,进行输入
(2)卷积层
应该尽可能使用小尺寸的滤波器,比如3×3或5×5,滑动步长1。还有一点就是需要对输入数据进行零填充,这样可以有效的保证卷积层不会改变输入数据体的空间尺寸。如果必须使用更大的滤波器尺寸如7×7,通常用在第一个面对原始图像的卷积层上。
(3)池化层:
负责对输入的数据空间维度进行下采样,常用的设置使用2×2的感受野,步长设置为2。池化层的感受野大小很少超过3。因为这回导致池化过程过于激进,造成特征信息的丢失,使得算法性能变差。
(4)零填充
零填充的使用可以让卷积层的输入和输出在空间上的维度保持一致。除此之外,如果不使用零填充,在不断进行卷积过程中,数据的尺寸会不断减少,造成图像边缘信息的的损失。
2、Pytorch中常用卷积模块
(1)卷积层:nn.Conv2d()
参数 | 含义 |
---|---|
in_channels | 输入数据的深度(图片的通道数) |
out_channels | 输出数据的深度(卷积后输出结果的通道数(神经元个数)) |
kernal_size | 卷积核的形式。可以用一个数字表示高和宽相等的卷积核,如kernal size=3;高和宽不同的卷积核,可以表示为kernal size=(3,2) |
stride | 卷积每次移动的步长。默认为1 |
padding | 处理边界时使用零填充的像素点个数。默认为1 |
dilation | 采样间隔数量,默认为1,表示无间隔采样 |
group | 输入与输出通道的分组数量。默认为1(全连接) |
bisa | 是一个布尔值,默认为True,表示有偏置项 |
(2)池化层:nn.MaxPool2d()
- nn.MaxPool2d()表示网络中的最大池化
参数 | 含义 |
---|---|
kernel_size | 最大池化操作时的窗口大小 |
stride | 最大池化操作时窗口移动的补偿,默认值是kernel_size |
padding | 输入的每一条边补充0的层数 |
dilation | 用于控制窗口中元素的步长 |
return_indices | 表示是否返回最大值所处的下表,默认为return indices=False |
ceil_mode | 默认为False。如果等于True。在计算输出大小时,将采用向上取整来代替默认的向下取整 |
- nn.AvgPool2d()表示均值池化,里面的参数和nn.MaxPool2d()类似,但多一个参数count_include_pad,这个参数表示计算均值的时候是否包含零填充,默认为True
3、构建简单的多层卷积神经网络
(1)代码实现
import numpy as np
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
#定义简单的四层神经网络
class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN,self).__init__() #输入图像维度 b,3,32,32
#定义第一层网络:卷积+激活+池化
layer1 = nn.Sequential()
layer1.add_module('conv1',nn.Conv2d(3,32,3,1,padding=1)) #卷积层:输入数据的深度为3,输出数据深度为32,卷积核形状3*3,卷积移动步长1
#卷积后输出维度:b,32,32,32
layer1.add_module('relu1',nn.ReLU(True)) #relu不改变维度
layer1.add_module('pool1',nn.MaxPool2d(2,2)) #池化层:池化的窗口形状为2*2
#池化后输出维度:b,32,16,16
self.layer1 = layer1
#定义第二层网络:卷积+激活+池化
layer2 = nn.Sequential()
layer2.add_module('conv2',nn.Conv2d(32,64,3,1,padding=1)) #卷积层:输入数据深度为32,输出数据深度为64,卷积核形状为3*3,卷积移动步长为1
#卷积后输出维度:b,64,16,16
layer2.add_module('rule2',nn.ReLU(True))
layer2.add_module('pool2',nn.MaxPool2d(2,2)) #池化层:池化的窗口形状为2*2
#卷积后输出维度:b,64,8,8
self.layer2 = layer2
#定义第三层网络:卷积+激活+池化
layer3 = nn.Sequential()
layer3.add_module('conv3',nn.Conv2d(64,128,3,1)) #卷积层:输入数据深度为64,输出数据深度为128,卷积核形状为3*3,卷积移动步长为1
#卷积后输出维度:b,128,8,8
layer3.add_module('relu3',nn.ReLU(True))
layer3.add_module('pool3',nn.MaxPool2d(2,2)) #池化层:池化的窗口形状为2*2
#池化后输出维度:b,128,4,4
self.layer3 = layer3
#定义第四层网络:全连接层+激活层+全连接层+激活层+全连接层
layer4 = nn.Sequential()
#输入维度b,2048
layer4.add_module('fc1',nn.Linear(2048,512)) #输入维度2-48,输出维度512
#全连接层输出维度:b,512
layer4.add_module('fc_relu1',nn.ReLU(True))
layer4.add_module('fc2',nn.Linear(512,64)) #输入维度512,输出维度64
#全连接层输出维度:b,64
layer4.add_module('fc_relu2',nn.ReLU(True))
layer4.add_module('fc3',nn.Linear(64,10)) #输入维度64,输出维度10
#全连接层输出维度:b,10(例如10分类问题,每个位置表示对应列别的权重大小,如果假如sigmoid函数,则表示对应的概率,一般取最大的最为判断类别)
self.layer4 = layer4
def forward(self,x):
conv1 = self.layer1(x)
conv2 = self.layer2(conv1)
conv3 = self.layer3(conv2)
fc_input = conv3.view(conv3.size(0),-1) #b,128*4*4 = b,2048 将维度展平
fc_out = self.layer4(fc_input)
return fc_out
model = SimpleCNN()
print(model)
(2)打印层结果如下图,括号里面表示层结构的名字:
二、模型参数的提取
nn.Module的几个重要属性:
- children():返回下一级代码块的迭代器,比如SimpleCNN()模型,它只会返回self.layer1,self.layer2,self.layer3,self.layer4的迭代器,不会返回他的内部东西。
- modules():会返回模型中的所有模块的迭代器,可以访问到模型的最内层。比如,self.layer2.conv1模块
- named children以及named modules分别与上两个属性对应。这两个不仅会返回模块的迭代器,还会返回网络层的名字。
1、从已定义的网路中,提取若干模块作为新的模型
#取出model的前2层网络,构成新的模型new_model
new_model = nn.Sequential(*list(model.children()))[:2]
print(new_model)
2、只提取model模型中的所有卷积层,构成新的模型conv_model
conv_model = nn.Sequential()
for layer in model.named_modules():
if isinstance(layer[1],nn.Conv2d):
conv_model.add_module(layer[0][-5:],layer[1])
print(conv_model)
3、提取每层网络的参数
for param in model.named_parameters():
print(param[0]) #param[1]输出参数值
三、自定义初始化
for m in model.modules():
if isinstance(m,nn.Conv2d): #判断是否为卷积层
init.normal(m.weight.data) #如果是卷积层,提取权重数值;使用正态分布对权重初始化
init.xavier_normal(m.weight.data) #基本思想是通过网络层是,输入和输出的方差相同,包括前向传播和反向求导。
init.kaiming_normal(m.weight.data) #均值为0,方差为(8)或(15)的高斯分布
m.bias.data.fill_(0) #偏置项使用0进行初始化
elif isinstance(m,nn.Linear): #判断是否为全连接层
m.weight.data.normal_() #如果是全连接层,使用正态分布对权值初始化
xavier_normal
1、要求激活函数关于0对称,且主要针对于全连接神经网络。适用于tanh、softmoid
2、如果初始化值很小,那么随着层数的传递,方差就会趋于0,此时输入值也变得越来越小,在sigmoid上就是在0附近,接近于线性,失去了非线性
3、如果初始值很大,那么随着层数的传递,方差会迅速增加,此时输入值变得很大,而sigmoid在大输入值写倒数趋近于0,反向传播时会遇到梯度消失的问题
参考文献:《深度学习之Pytorch》