
# 图6-1是用一个比较简单的卷积神经网络对手写输入数据进行分类
# 由卷积层(conv2d), 池化层(MaxPool2d)和全连接层(Linear)叠加而成
import torch.nn as nn
import torch.nn.functional as F
# 这个地方的cuda:0 实际上并不是0号GPU,他取决于CUDA_VISIBLE_DEVICES
# 然后逻辑GPU和物理GPU有一个对应关系
# 如果CUDA_VISIBLE_DEVICES为2,1,3
# 那么CUDA:0就是2号GPU, CUDA:1 就是1号GPU CUDA:3 就是3号GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
class CNNNet(nn.Module):
def __init__(self):
super(CNNNet, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=16,kernel_size=5, stride=1)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=16, out_channels=36, kernel_size=3, stride=1)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
# nn.Linear是一个类,使用时进行类的实例化
# 实例化的时候,nn.Linear需要输入两个参数,
# in_features为上一层神经元的个数,out_features为这一层的神经元个数
self.fc1 = nn.Linear(1296,128)
self.fc2 = nn.Linear(128,10)
def forward(self, x):
x = self.pool1(F.relu(self.conv1(x)))
x = self.pool2(F.relu(self.conv2(x)))
print(x.shape)
# 其维度进行随意变化的话,可以使用view()方法,
# 比如我想将其维度变化成:1*6,则使用方法view(1,6)即可。
x = x.view(-1, 36*6*6)
x = F.relu(self.fc2(F.relu(self.fc1(x))))
return x
net = CNNNet()
net = net.to(device)
卷积核是整个卷积过程的核心,比较简单的卷积核或过滤器有Horizontalfilter, Verticalfilter, Sobel Filter等,这些过滤器能够检测图像的水平边缘,垂直边缘,增强图像中心区域权重等
卷积核的值在移动的过程中都是共享的,卷积神经网络采用参数共享的方法大大降低了参数的数量。

要进行padding的原因:

彩色图片就3通道,RGB,3通道图片的卷积运算与单通道图片的卷积运算基本一致,对于3通道的RGB图片,其对应的滤波器算子同样也是3通道的。

例如上图是663,表示高度height, 宽度width,通道数 channel。过程是将每个单通道(RGB)与对应的filter进行卷积运算求和,然后再将3通道的和相加,得到输出图片的一个像素值。
不同滤波器组卷积得到不同的输出,个数由滤波器组决定,图6-11中只有1个滤波器组,所以结果只有一个;如果有2个333的filter,则会得到442的输出
卷积神经网络与标准的神经网络类似,为保证其非线性,也需要使用激活函数,即在卷积运算后,把输出值另加偏移量,输入到激活函数,然后作为下一层的输入

常见的激活函数为:nn.Sigmoid, nn.ReLU, nn.LeakyReLU, nn.Tanh
torch.nn.Conv2d


dilation:其默认值为1

这种情况实际为Pytorch中,dilation = 2的情况。
特别注意:在Pytorch中,dilation = 1等同于没有dilation的标准卷积。
转置卷积(Transposed Convolution)也叫反卷积(Deconvolution)或部分跨越卷积(Fractionally-Strided Convolution)
通过卷积的正向传播的图像一般越来越小,记为下采样(Downsampled).卷积的方向传播实际上就是一种转置卷积,它是上采样(up-Sampling)
转置卷积详解1

总结:
转置卷积的作用:上采样

这里不能使用
I
=
O
C
−
1
I = OC^{-1}
I=OC−1,因为不能保证C可逆,可逆只对方阵而言,但并不能保证C是方阵。所以放宽条件:不要求还原卷积前的数据,只要求还原卷积前的大小即可
torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros')
池化(Pooling)又称为下采样,通过卷积层获得图像特征后,理论上可以直接使用这些特征训练分类器。但这样计算量太大,且容易产生过拟合现象。
故要对卷积层进行池化处理,有3中方法:
1。最大池化Max Pooling:选择Pooling窗口中的最大值作为采样值
2。均值池化Mean Pooling:将Pooling窗口中的所有值相加取平均,以平均值作为采样值
3。全局最大(或均值)池化:与平常最大或最小池化相对而言,全局池化是对整个特征图的池化而不是在移动窗口范围内的池化

池化的作用:
在CNN中可用来减小尺寸,提高运算速度,减小噪声影响,让各特征更具有健壮性。
池化层比卷积层更简单,它没有卷积运算,只是在滤波器算子滑动区域内取最大值或平均值。
池化的作用则体现在降采样:保留显著特征,降低特征维度,增大感受野。
深度网络越往后面越能捕捉到物体的语义信息,这种语义信息是建立在较大的感受野基础上。
在移动窗口内的池化被称为局部池化
nn.MaxPool2d
nn.AvgPool2d -----更常用
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
全局池化也分为最大或平均池化。
全局平均池化(Global Average Pooling, GAP),不以窗口的形式取均值,而是以特征图为单位进行均质化,即一个特征图输出一个值


使用全局平均池化代替CNN中传统的全连接层。在使用卷积层的识别任务中,全局平均池化能够为每一个特定的类别生成一个特征图(Feature Map)
GAP的优势在于:
(1)模型架构
输出层–卷积层–池化层–卷积层–池化层–全连接层–全连接层–输出
(2)模型特点
Tanh比sigmoid函数晚诞生,sigmoid函数的一个缺点就是输出不以0为中心,使得收敛变慢。
而tanh则解决了这个问题,它是一个奇函数
tanh
(
x
)
=
sinh
(
x
)
cosh
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
\tanh (x)=\frac{\sinh (x)}{\cosh (x)}=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}
tanh(x)=cosh(x)sinh(x)=ex+e−xex−e−x

尽管tanh函数和sigmoid函数存在梯度消失的问题,但是与之类似,如果函数的梯度过大又会导致梯度爆炸的问题,显然tanh和sigmoid的导函数非常有界,根据导数公式,很容易得出t a n h ′ ( x ) ∈ [ 0 , 1 ] 所以完全不用担心因为使用激活函数而产生梯度爆炸的问题。