当前位置:   article > 正文

pytorch学习笔记——2.4torch.nn模块简介

torch.nn

前言

torch.nn模块包含torch为我们准备好的各种层,方便我们调用以构建网络。我们主要介绍卷积层、池化层、激活函数层、循环层、全连接层等的相关使用方法。


一、卷积层

        卷积可以看作是输入与卷积核之间的内积运算,是两个实值函数之间的一种数学运算。在卷积层中,我们通常使用卷积核将输入数据进行卷积运算从而得到输出作为特征映射,通过每一个卷积核我们可以得到一个特征映射。例如下图中的使用2*2卷积核进行步长为1的卷积操作:

        在Pytorch中,针对卷积操作的对象和使用场景不同,我们有一维卷积、二维卷积、三维卷积、转置卷积(卷积的逆操作)等,我们都可以通过torch.nn模块调用它们,具体调用方法如下

常用卷积操作调用
层对应的类功能
torch.nn.Cov1d()对输入信号应用1D卷积
torch.nn.Cov2d()对输入信号应用2D卷积
torch.nn.Cov3d()对输入信号应用3D卷积
torch.nn.CovTranspose1d()对输入信号应用1D转置卷积
torch.nn.CovTranspose2d()对输入信号应用2D转置卷积
torch.nn.CovTranspose3d()对输入信号应用3D转置卷积

         下面我们以2D卷积为例简单介绍卷积层的相关参数,代码示例如下:

  1. #在输入信号上应用1D卷积 torch.nn.Conv1d()
  2. #在输入信号上应用2D卷积 torch.nn.Conv2d()
  3. #在输入信号上应用3D卷积 torch.nn.Conv3d()
  4. #调用2D卷积
  5. torch.nn.Conv2d(in_channels,
  6. out_channels,
  7. kernel_size,
  8. stride=1,
  9. padding=0,
  10. dilation=1,
  11. groups=1,
  12. bias=True
  13. )

其中参数说明如下:

in_channels输入图像的通道数
out_channels经过卷积运算后,输出特征映射的数量
kernel_size卷积核大小
stride卷积步长(默认为1)
padding在输入两边进行0填充的数量(默认为0)
dilation卷积核元素之间的步幅(默认为1)
groups从输入通道到输出通道之间的阻塞连接数
bias是否添加偏置(默认为True)

torch.nn.Conv2d输入的张量为(N,C_{in},H_{in},W_{in}) ,输出的张量为(N,C_{out},H_{out},W_{out}) 。

        下面我们通过对一张图像来展示经过卷积后输出特征映射的结果。其中我们首先导入有关的包和模块,并使用PIL包来读取图像数据,使用matplotlib包来对图像和卷积结果进行可视化处理,代码如下:

        首先,我们导入有关包和模块,之后使用Image.open()函数读取图像数据,并且使用.convert()方法,将其转化为灰度图像,并将其转化为numpy数组,最后使用plt.show()函数将图像可视化,具体代码如下:

  1. # 对一张图像使用二维卷积输出
  2. import torch
  3. import torch.nn as nn
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. from PIL import Image
  7. #读取图像 转化为灰度图片 转化为numpy数组
  8. myim = Image.open(r"C:\Users\18298\Desktop\JupyterProjects\image\mixue.jpg")
  9. myimgray = np.array(myim.convert("L"),dtype=np.float32)
  10. #可视化图片
  11. plt.figure(figsize=(6,6))
  12. plt.imshow(myimgray,cmap=plt.cm.gray)
  13. plt.axis("off")
  14. plt.show()

        原图以及得到图像如下:

原图
得到图像

           通过上述操作,我们得到了一个原图的1280*1280的numpy数组,但是为了使用Pytorch进行卷积操作,我们需要将其转化为1*1*1280*1280的张量,代码如下:

  1. #将数组转化为张量
  2. print(myimgray.shape)
  3. imh,imw = myimgray.shape
  4. myimgray_t = torch.from_numpy(myimgray.reshape((1,1,imh,imw)))
  5. print(myimgray_t.shape)

 结果如下,说明已经将图片信息转化成张量:

  1. (1280, 1280)
  2. torch.Size([1, 1, 1280, 1280])

         卷积过程中需要将图像转化为四维以表示[batch,channel,h,w]。在本次对图像完成卷积操作后,我们将得到两个特征映射:第一个特征映射使用图像轮廓提取卷积核获取、第二个特征映射使用的卷积核为随机数,卷积核大小为5*5,对图像边缘不使用0进行填充(padding=0)。我们对张量使用如下的代码进行卷积运算,并对得到的两个特征映射进行可视化处理:

  1. #第一个特征映射使用图像轮廓提取卷积核获取,第二个特征映射使用卷积核为随机数,大小为5*5,对边缘不用0填充因此输出
  2. #特征映射的尺寸为1076*1076
  3. kersize = 5#定义边缘卷积核 维度处理为1*1*5*5
  4. ker = torch.ones(kersize,kersize,dtype=torch.float32)*-1
  5. ker[2,2] = 24
  6. ker = ker.reshape((1,1,kersize,kersize))
  7. #卷积操作
  8. conv2d = nn.Conv2d(1,2,(kersize,kersize),bias=False)#input:1 output:2
  9. #设置卷积使用的核 第一个核使用边缘检测核
  10. conv2d.weight.data[0] = ker
  11. #对灰度图像进行卷积操作
  12. imconv2dout = conv2d(myimgray_t)
  13. print("未压缩前尺寸:",imconv2dout.shape)
  14. #对卷积输出进行维度压缩
  15. imconv2dout_im = imconv2dout.data.squeeze()#去掉所有为1的维度
  16. print("卷积并压缩后尺寸:",imconv2dout_im.shape)
  17. ##可视化卷积后的图像
  18. plt.figure(figsize=(12,6))
  19. plt.subplot(1,2,1)
  20. plt.imshow(imconv2dout_im[0],cmap=plt.cm.gray)
  21. plt.axis("off")
  22. plt.subplot(1,2,2)
  23. plt.imshow(imconv2dout_im[1],cmap=plt.cm.gray)
  24. plt.axis("off")
  25. plt.show()

结果如下:

  1. 未压缩前尺寸: torch.Size([1, 2, 1276, 1276])
  2. 卷积并压缩后尺寸: torch.Size([2, 1276, 1276])
第一个特征映射                                                                 第二个特征映射

   从结果来看,第一个特征映射通过使用边缘特征提取卷积核,很好的提取了图像的边缘信息;第二个特征映射通过使用随机卷积核,得到与图像相似的卷积结果。


二、池化层

        池化操作的重要目的就是对卷积得到的特征进行进一步处理(降维),就是选取一定大小的区域,将该区域的像素使用一个代表元素表示(平均值池化 最大值池化)。这两种池化方式示例如下所示:

最大值池化

平均值池化

在Pytorch中,为我们提供了许多池化层的类,分别是:最大值池化、最大值池化的逆过程、平均值池化、自适应池化,并且均提供了一维、二维、三维的池化操作。具体的池化类和功能表如下:

Pytorch中常用的池化操作
层对应的类功能
torch.nn.MaxPool1d()对输入信号使用1D最大值池化
torch.nn.MaxPool2d()对输入信号使用2D最大值池化
torch.nn.MaxPool3d()对输入信号使用3D最大值池化
torch.nn.MaxUnPool1d()对输入信号使用1D最大值池化的部分逆运算
torch.nn.MaxUnPool2d()对输入信号使用2D最大值池化的部分逆运算
torch.nn.MaxUnPool3d()对输入信号使用3D最大值池化的部分逆运算
torch.nn.AvgPool1d()对输入信号使用1D平均值池化
torch.nn.AvgPool2d()对输入信号使用2D平均值池化
torch.nn.AvgPool3d()对输入信号使用3D平均值池化
torch.nn.AdaptiveMaxPool1d()对输入信号使用1D自适应最大值池化
torch.nn.AdaptiveMaxPool2d()对输入信号使用2D自适应最大值池化
torch.nn.AdaptiveMaxPool3d()对输入信号使用3D自适应最大值池化
torch.nn.AdaptivAvgPool1d()对输入信号使用1D自适应平均值池化
torch.nn.AdaptiveAvgPool1d()对输入信号使用1D自适应平均值池化
torch.nn.AdaptiveAvgPool1d()对输入信号使用1D自适应平均值池化

         下面我们以2D最大值池化为例简单介绍池化层的相关参数,代码示例如下:

  1. #使用torch.nn.MaxPool2d()池化操作相关参数的应用
  2. torch.nn.MaxPool2d(kersize,
  3. stride=None,
  4. padding=0,
  5. dilation=1,
  6. return_indices=False,
  7. ceil_mode=False)

其中参数说明如下:

kernel_size池化窗口大小
stride池化窗口移动的步长(默认为kernel_size)
padding在输入两边进行0填充的数量(默认为0)
dilation一个控制窗口中元素步幅的参数
return_indicesTrue:返回输出最大值的索引
ceil_mode

True:计算输出信号大小时使用向上取整

False:计算输出信号大小时使用向下取整

torch.nn.Conv2d输入的张量为(N,C_{in},H_{in},W_{in}) ,输出的张量为(N,C_{out},H_{out},W_{out}) 。

        下面我们通过对上面图像卷积后的两个特征映射进行最大值池化、平均值池化、自适应池化来演示如何使用池化层,代码如下:

1.最大值池化:

  1. #对卷积后的结果imconv2dout进行最大值池化
  2. maxpool2 = nn.MaxPool2d(2,stride=2)
  3. pool2_out = maxpool2(imconv2dout)
  4. pool2_out_im = pool2_out.squeeze()
  5. print("卷积后大小为:",imconv2dout.shape)
  6. print("池化后大小为:",pool2_out.shape)
  7. print("池化后压缩后大小为:",pool2_out_im.shape)

输出结果如下,可见大小缩小了一半:

  1. 卷积后大小为: torch.Size([1, 2, 1276, 1276])
  2. 池化后大小为: torch.Size([1, 2, 638, 638])
  3. 池化后压缩后大小为: torch.Size([2, 638, 638])

对结果进行可视化处理:

  1. #可视化最大值池化后的结果
  2. plt.figure(figsize=(12,6))
  3. plt.subplot(1,2,1)
  4. plt.imshow(pool2_out_im[0].data,cmap=plt.cm.gray)
  5. plt.axis("off")
  6. plt.subplot(1,2,2)
  7. plt.imshow(pool2_out_im[1].data,cmap=plt.cm.gray)
  8. plt.axis("off")
  9. plt.show()
  10. print("卷积后大小为:",imconv2dout.shape)
  11. print("池化后大小为:",pool2_out.shape)
  12. print("池化后压缩后大小为:",pool2_out_im.shape)

结果如下:

第一个特征映射                                                                 第二个特征映射
  1. 卷积后大小为: torch.Size([1, 2, 1276, 1276])
  2. 池化后大小为: torch.Size([1, 2, 638, 638])
  3. 池化后压缩后大小为: torch.Size([2, 638, 638])

 2.平均值池化:

  1. #对卷积后的结果进行平均值池化
  2. avgpool2 = nn.AvgPool2d(2,stride=2)
  3. pool2_out = avgpool2(imconv2dout)
  4. pool2_out_im = pool2_out.squeeze()
  5. print("pool2_out:",pool2_out.shape)
  6. print("pool2_out_im:",pool2_out_im.shape)

结果如下:

  1. pool2_out: torch.Size([1, 2, 638, 638])
  2. pool2_out_im: torch.Size([2, 638, 638])

对结果进行可视化处理:

  1. #可视化平均值池化后的结果
  2. plt.figure(figsize=(12,6))
  3. plt.subplot(1,2,1)
  4. plt.imshow(pool2_out_im[0].data,cmap=plt.cm.gray)
  5. plt.axis("off")
  6. plt.subplot(1,2,2)
  7. plt.imshow(pool2_out_im[1].data,cmap=plt.cm.gray)
  8. plt.axis("off")
  9. plt.show()
  10. print("卷积后大小为:",imconv2dout.shape)
  11. print("池化后大小为:",pool2_out.shape)
  12. print("池化后压缩后大小为:",pool2_out_im.shape)

结果如下:

第一个特征映射                                                                 第二个特征映射
  1. 卷积后大小为: torch.Size([1, 2, 1276, 1276])
  2. 池化后大小为: torch.Size([1, 2, 638, 638])
  3. 池化后压缩后大小为: torch.Size([2, 638, 638])

 3.自适应平均值池化:

使用output_size指定输出特征映射的尺寸

  1. #对卷积后的结果进行自适应平均值池化
  2. #可使用output_size函数指定输出特征映射的尺寸
  3. AdaAvgpool2 = nn.AdaptiveAvgPool2d(output_size=(100,100))
  4. pool2_out = AdaAvgpool2(imconv2dout)
  5. pool2_out_im = pool2_out.squeeze()
  6. print("pool2_out:",pool2_out.shape)
  7. print("pool2_out_im:",pool2_out_im.shape)

结果如下(不难发现输出特征映射的尺寸为我们规定的100*100,由于尺寸变小因此更加模糊):

  1. pool2_out: torch.Size([1, 2, 100, 100])
  2. pool2_out_im: torch.Size([2, 100, 100])

 对结果进行可视化处理:

  1. #可视化自适应平均值池化后的结果
  2. plt.figure(figsize=(12,6))
  3. plt.subplot(1,2,1)
  4. plt.imshow(pool2_out_im[0].data,cmap=plt.cm.gray)
  5. plt.axis("off")
  6. plt.subplot(1,2,2)
  7. plt.imshow(pool2_out_im[1].data,cmap=plt.cm.gray)
  8. plt.axis("off")
  9. plt.show()
  10. print("卷积后大小为:",imconv2dout.shape)
  11. print("池化后大小为:",pool2_out.shape)
  12. print("池化后压缩后大小为:",pool2_out_im.shape)

结果如下:

第一个特征映射                                                                 第二个特征映射
  1. 卷积后大小为: torch.Size([1, 2, 1276, 1276])
  2. 池化后大小为: torch.Size([1, 2, 100, 100])
  3. 池化后压缩后大小为: torch.Size([2, 100, 100])

 三、激活函数

        在Pytorch中,常用的激活函数通常为S型(Sigmoid)激活函数、双曲正切(Tanh)激活函数、线性修正单元(ReLU)激活函数等,其类和功能如下:

Pytorch中常用的激活函数操作
层对应的类功能
torch.nn.SigmoidSigmoid激活函数
torch.nn.TanhTanh激活函数
torch.nn.ReLUReLU激活函数
torch.nn.SoftplusSoftplus激活函数

        下面我们简单介绍这几种激函数的功能与计算方式:

       1. Sigmoid激活函数:

        计算方式:f(x)=\frac{1}{1+e^{-x}}

        特点:输出位于(0,1)开区间内,当输入远离坐标原点时,该函数的梯度变得很小趋近于零,会影响参数的更新速度。

        2. Tanh激活函数:

        计算方式:f(x)=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}

        特点:输出位于(-1,1)开区间内,Tanh激活函数曲线和Sigmoid比较相近,在输入很大或很小时梯度很小,不利于权重更新,但由于取值输出以0对称,使用效果会比Sigmoid要好。

         3. ReLU激活函数:

        计算方式:f(x)=max(0,x)

        特点:只保留大于0的输出,其他输出设置为0,计算速度由于不存在梯度饱和的问题会比其他类型激活函数快很多。

         4. Softplus激活函数:

        计算方式:f(x)=\frac{1}{\beta }log(1+e^{\beta x})

        特点:该函数对于任意位置都可以计算导数,并且尽可能保留了ReLu激活函数的优点(β默认为1)。

        下面我们通过绘制以上几种激活函数的图像来更好的说明它们的特征,代码如下:

        首先创建一个线性间距张量:

  1. #激活函数的图像
  2. x = torch.linspace(-6,6,100)#返回一个1维张量,包含在区间startend上均匀间隔的step个点。
  3. print("x是一个线性间距张量",x.shape)

 结果为:

x是一个线性间距张量 torch.Size([100])

         然后分别定义各种激活函数:

Sigmoid激活函数:

  1. sigmoid = nn.Sigmoid()
  2. ysigmoid = sigmoid(x)

Tanh激活函数:

  1. tanh = nn.Tanh()
  2. ytanh = tanh(x)

ReLU激活函数:

  1. relu = nn.ReLU()
  2. yrelu = relu(x)

Softplus激活函数:

  1. softplus = nn.Softplus()
  2. ysoftplus = softplus(x)

        最后对激活函数进行可视化处理:

  1. #可视化激活函数
  2. plt.figure(figsize=(13,3))
  3. plt.subplot(1,4,1)
  4. plt.plot(x.data.numpy(),ysigmoid.data.numpy(),"r-")
  5. plt.title("Sigmoid")
  6. plt.grid()#设置网格线
  7. plt.subplot(1,4,2)
  8. plt.plot(x.data.numpy(),ytanh.data.numpy(),"r-")
  9. plt.title("Tanh")
  10. plt.grid()#设置网格线
  11. plt.subplot(1,4,3)
  12. plt.plot(x.data.numpy(),yrelu.data.numpy(),"r-")
  13. plt.title("ReLU")
  14. plt.grid()#设置网格线
  15. plt.subplot(1,4,4)
  16. plt.plot(x.data.numpy(),ysoftplus.data.numpy(),"r-")
  17. plt.title("Softplus")
  18. plt.grid()#设置网格线

结果如下:


 四、循环层

        在Pytorch中,提供了三种循环层的实现,如下所示:

循环层对应的类与功能
层对应的类功能
torch.nn.RNN()多层RNN单元
torch.nn.LSTM()多层长短期记忆LSTM单元

torch.nn.GRU()

多层门限循环GRU单元

torch.nn.RNNCell()一个RNN循环层单元
torch.nn.LSTMCell()一个长短期记忆LSTM单元

torch.nn.GRUCell()

一个门限循环GRU单元

         RNN的输入为input和h_0,其中input是一个形状为(seq_len,batch,input_size)的张量。h-0则是一个形状为(num_layers*num_directions,batch,hidden_size)保存着初始隐状态的张量。如果不提供就默认为。如果是双向RNN,,num_direction等于2,否则等于1。

        RNN的输出为output和h_n,其中:

        output是一个形状为(seq_len,batch,hidden_size*num_directions)的张量,保存着RNN最后一层的输出特征。如果输入是被填充过的序列,那么输出也是被填充的序列。

        h_n是一个形状为(num_layers*num_directions,batch,hidden_size)的张量,保存着最后一个时刻的隐状态。


 五、全连接层

        全连接层是一个由多个神经元组成的层,其所有的输出和该层的所有输入都有连接,即每个输入都会影响所有神经元的输出。

        下面我们以线性全连接层为例简单介绍全连接层的相关参数,代码示例如下:

  1. torch.nn.Linear(in_features,out_features,bias=True)
  2. #输入为(N,in_feartures)张量(in_features=收入样本的特征数量)
  3. #输出为(N,out_feartures)张量(out_features=输出样本的特征数量)

其中参数说明如下:

        in_features:每个输入样本的特征数量

        out_features:每个输出样本的特征数量

        bias:True代表学习偏置;False代表不学习偏置

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/606218
推荐阅读
相关标签
  

闽ICP备14008679号