PPT讲义

chap-卷积神经网络.pptx


博客:用PyTorch搭建卷积神经网络

原文地址
在PyTorch中神经网络的构建是通过torch.nn工具
在前馈神经网络已经介绍过autograd,而nn正是基于autograd来定义模型并求取其中的各种梯度

  • nn.Module定义了网络的每一层
  • forward(input)则用来计算网络的输出

§ 5. 卷积神经网络 - 图1
来看这样一个用来识别手写体数字的网络,一个典型的训练神经网络的步骤是:

  • 1.定义一个包含一组带学习的参数的神经网络
  • 2.将数据输入到神经网络中并进行前向传播
  • 3.根据损失函数计算输出结果与目标值之间的差距
  • 4.进行梯度反向传播到各个参数
  • 5.更新网络参数,典型的更新方式是:weight = weight - learning_rater * gradient

    Define the network

    image.png
    image.png

    卷积核大小

  • 什么是卷积核?

image.png
由这个图我们知道,卷积核指的就是我们要乘得§ 5. 卷积神经网络 - 图5

代码

  1. import torch
  2. from torch.autograd import Variable
  3. import torch.nn as nn
  4. import torch.nn.functional as F
  5. class Net(nn.Module):
  6. def __init__(self):
  7. #使用super()方法调用基类的构造器,即nn.Module.__init__(self)
  8. super(Net,self).__init__()
  9. '''
  10. 第1个卷积层
  11. 1 input image channel, 6 output channel, 5x5 square convolution kernel
  12. 1个图像通道,因为是灰度图,6个输出频道,应该指的是用6个5x5大小的卷积核(待确定)
  13. '''
  14. self.conv1 = nn.Conv2d(1,6,5)
  15. '''
  16. 第2个卷积层
  17. 6 input channel, 16 output channels, 5x5 square convolution kernel
  18. 这个卷积层应该是连接上一个池化层,所以输入是6个
  19. 然后使用16个5x5大小的卷积层
  20. '''
  21. self.conv2 = nn.Conv2d(6,16,5)
  22. #an affine operation: y = WX+b
  23. self.fc1 = nn.Linear(16 * 5 * 5,120)
  24. self.fc2 = nn.Linear(120, 84)
  25. self.fc3 = nn.Linear(85,10)
  26. def forward(self,x):
  27. '''
  28. x是网络的输入,然后将x前向传播,最后得到输出
  29. 下面两句定义了两个2x2的池化层
  30. '''
  31. x = F.max_pool2d(F.relu(self.conv1(x)),(2,2))
  32. #if the size is square you can only specify a single number
  33. x = F.max_pool2d(F.relu(self.conv2(x)),2)
  34. x = x.view(-1,self.num_flat_features(x))
  35. x = F.relu(self.fc1(x))
  36. x = F.relu(self.fc2(x))
  37. x = self.fc3(x)
  38. return x
  39. def num_flat_features(self,x):
  40. size = x.size()[1:] #all dimensions except the batch dimension
  41. num_features = 1
  42. for s in size:
  43. num_features *= s
  44. return num_features
  45. net = Net()
  46. print(net)

以上是定义了一个网络,并且定义了值得前向传播过程,可以进行各种Tensor可以进行的运算,而在梯度反向传播得时候,要用到上章介绍的autograd。

卷积运算

这是卷积核矩阵和输入矩阵中的感受野矩阵之间的内积,也就是两个向量之间的内积,如两个§ 5. 卷积神经网络 - 图6维向量§ 5. 卷积神经网络 - 图7§ 5. 卷积神经网络 - 图8的内积为
§ 5. 卷积神经网络 - 图9
那么torch.nn.Conv2d()是怎么运算的呢?先来看下它有哪些参数(把这些参数的作用搞清楚,就基本弄清了卷积层的具体工作方式):

  • in_channels(int) : Number of channels in the input image.
  • out_channels(int): Number of channels producted by the convolution.
  • kernel_size(int or tuple): Size of the convolution. Default: 1.
  • stride(int or tuple, optional): Zero-padding added to both sides of the input. Default: 0.
  • dilation(int or tuple, optional): Spacing between kernel elements. Default: 1
  • groups(int, optional): Number of blocked connections from input channels to output channels. Default: 1
  • bias(bool, optional): If True, adds a learnable bias to the output. Default: True.

点击这里,PyTorch的详细文档
网络的输入用四元组§ 5. 卷积神经网络 - 图10来表示,输出表示为§ 5. 卷积神经网络 - 图11。其中:

  • § 5. 卷积神经网络 - 图12§ 5. 卷积神经网络 - 图13分别表示矩阵的高和宽
  • § 5. 卷积神经网络 - 图14表示数据的batch dimension,即批处理的数据一批有多少个
  • § 5. 卷积神经网络 - 图15表示输入数据的通道数,按上图中可以理解为输入该层的有几个矩阵
  • § 5. 卷积神经网络 - 图16表示输出数据的通道数,可以理解为输出有多少个矩阵

卷积层的输出满足这样的表达式:
§ 5. 卷积神经网络 - 图17
现在只看其中一个数据,即§ 5. 卷积神经网络 - 图18为一个常数,代表当前数据是batch中的第几个数据,以上面定义的网络的第一个卷积层为例:

§ 5. 卷积神经网络 - 图19
蓝色方框中的红点称为A元素,对这一层上面表达式的各个数值为:

  • § 5. 卷积神经网络 - 图20:假设为一批数据中的第1个,即§ 5. 卷积神经网络 - 图21
  • § 5. 卷积神经网络 - 图22:输出有6个通道,§ 5. 卷积神经网络 - 图23,对A点§ 5. 卷积神经网络 - 图24
  • § 5. 卷积神经网络 - 图25:输入通道,只有1个通道
  • § 5. 卷积神经网络 - 图26:是计数器,对应着输入数据的每一个通道,这里只有一个
  • § 5. 卷积神经网络 - 图27:偏置参数,维度和§ 5. 卷积神经网络 - 图28一致

卷积核或者说过滤器§ 5. 卷积神经网络 - 图29是一个§ 5. 卷积神经网络 - 图30的多维向量,§ 5. 卷积神经网络 - 图31确定了其第一维,§ 5. 卷积神经网络 - 图32确定了其第二维。对于输入向量§ 5. 卷积神经网络 - 图33§ 5. 卷积神经网络 - 图34确定了其第一维。于是求和符号里就是两个向量的内积。当§ 5. 卷积神经网络 - 图35大于1时,来看第二个卷积层:

§ 5. 卷积神经网络 - 图36
这时§ 5. 卷积神经网络 - 图37有6个取值,求和符号§ 5. 卷积神经网络 - 图38中,§ 5. 卷积神经网络 - 图39,以右侧红点元素为例:当§ 5. 卷积神经网络 - 图40时是绿色§ 5. 卷积神经网络 - 图41的向量和左边第一个红色§ 5. 卷积神经网络 - 图42的向量做内积,然后§ 5. 卷积神经网络 - 图43是绿色§ 5. 卷积神经网络 - 图44的向量和第二个红色§ 5. 卷积神经网络 - 图45的向量做内积,做了6次之后,把这6个内积的结果加和,再加上和第一个输出通道对应的§ 5. 卷积神经网络 - 图46,就得到了右侧红点元素的值。
然后左侧红色方框即感受野再按步长移动,输出向量的尺寸是这样的(floor代表向下取整):
§ 5. 卷积神经网络 - 图47
可以看到上面两个式子涉及到了torch.nn.Conv2d()中的4个参数,kernel_size和stride都好理解,分别是卷积核尺寸和移动步长。

零填充

在网络的早期层中,我们想要尽可能多地保留原始输入内容的信息,这样我们就能提取出那些低层的特征。比如说我们想要应用同样的卷积层,但又想让输出量维持为 32 x 32 x 3 。为做到这点,我们可以对这个层应用大小为 2 的零填充(zero padding)。零填充在输入内容的边界周围补充零。

§ 5. 卷积神经网络 - 图48
关于padding图片来自这里,这是一个很好的讲解卷积神经网络的文章,还有文字来自对这篇文章翻译的中文版
dilation:对于这个参数,链接中的最后一个图是关于dilation的。
no_padding_no_strides.gif
来自这个问题下贾扬清的回答,有几个说明卷积运算过程的图(其中KxK代表卷积核的size,其他符号和上文所用的一致):

§ 5. 卷积神经网络 - 图50
§ 5. 卷积神经网络 - 图51

§ 5. 卷积神经网络 - 图52
§ 5. 卷积神经网络 - 图53
对于卷积神经网络每一层究竟做了什么,提取出什么特征,这里有个将神经网络可视化的视频

参数个数

对于卷积层来说,参数个数就是§ 5. 卷积神经网络 - 图54向量和§ 5. 卷积神经网络 - 图55向量的元素个数和,比如对第二个卷积层来说,就是§ 5. 卷积神经网络 - 图56
可以通过:

  1. net.parameters()

来获得网络的参数。

  1. params = list(net.parameters())
  2. print(len(params))
  3. for i in range(len(params)):
  4. print(params[i].size())
  5. +++++++++
  6. 10
  7. torch.Size([6,1,5,5])
  8. torch.Size([6])
  9. torch.Size([16,6,5,5])
  10. torch.Size([16])
  11. torch.Size([120,400])
  12. torch.Size([120])
  13. torch.Size([84,120])
  14. torch.Size([84])
  15. troch.Size([10,84])
  16. torch.Size([10])
  17. ++++++++++

网络的输入与输出

下面来给网络一个输入:

  1. input = Variable(torch.randn(1,1,32,32))
  2. out = net(input)
  3. print(out)
  4. +++++++
  5. Variable containing:
  6. 0.0246 0.0021 -0.0395 -0.0225 -0.0905 -0.0091 -0.0931 -0.0234 -0.1005 0.0795
  7. [torch.FloatTensor of size 1x10]
  8. +++++++

在前面写卷积运算的时候也提到过,实际上网络的输入向量,第一个维度§ 5. 卷积神经网络 - 图57是批处理数据的个数。在torch.nn方法中必须有这一维的数值,即输入的向量必须是§ 5. 卷积神经网络 - 图58这样的四维向量。而如果数据没这第一个维度,也就是并不是一批数据,那么可以通过:

  1. torch.Tensor.unsqueeze(0)

在不改变数据所含数值的情况,增加一个维度。
§ 5. 卷积神经网络 - 图59
§ 5. 卷积神经网络 - 图60
§ 5. 卷积神经网络 - 图61

损失函数

如何定义损失函数也很简单的:

  1. output = net(input)
  2. target = Variable(torch.arange(1,11))#a dumy target, for example
  3. criterion = nn.MSELoss() #mean-squared error between the input and target
  4. loss = criterion(output,target)
  5. print(loss)

torch.nn中还有很多损失函数可以使用,点击这里查看

梯度的反向传播

这就要用到autograd.Variable()中的backward()功能。在学习autograd.Variable时也发现,如果Variable.grad已经存储有梯度值,那么当再次写出backward()命令进行梯度回传时,计算出的值将与各个创建变量(graph leaves)的grad已有的值累加。所以对于神经网络,要使用net.zero_grad()清楚掉原有的梯度值

  1. net.zero_grad() #zeroes the gradient buffers of all parameters
  2. print('conv1.bias.grad before backward')
  3. print(net.conv1.bias.grad)
  4. loss.backward()
  5. print('conv1.bias.grad after backward')
  6. print(net.conv1.bias.grad)

§ 5. 卷积神经网络 - 图62
如果原来已有梯度值,然后执行net.zero_grad(),梯度值就变为0,如果原来没有梯度值,那么变量x处的梯度x.grad = None

参数更新

使用torch.optim可以方便的定义参数更新算法:

  1. import torch.optim as optim
  2. #create your optimizer,such as SGD
  3. optimizer = optim.SGD(net.parameters(),lr = 0.01)#lr is learning rate
  4. #in your training loop:
  5. optimizer.zero_grad() #zero the gradient buffers
  6. output = net(input)
  7. loss = criterion(output,target)
  8. loss.backward()
  9. optimizer.step() #Does the update

使用PyTorch搭建前馈神经网络对图片进行分类

原文链接

一、数据集

数据集的来源,该数据集主要是10个种类的衣服,然后90%用作训练集,10%用作测试集。所有图像的大小都是(28*28)的灰度图像。数据集中的两个文件夹,每个都是.csv文件,该文件具有图像的id和相应的标签。
fashion-mnist-master.zip
在PyTorch的手册中,有关于用TensorBoard来训练数据的教程,链接。但是我是在Google Colab中写的代码,所以没有使用这种方式。

二、博客2

这篇博客讲的是使用PyTorch进行的分类。

  • 导入库 ```python

    导入库

    import pandas as pd import numpy as np

读取展示图片

from skimage.io import imread import matplotlib.pyplot as plt %matplotlib inline

创建验证集

from sklearn.model_selection import train_test_split

评估模型

from sklearn.metrics import accuracy_score from tqdm import tqdm

PyTorch的相关库

import torch from torch.autograd import Variable from torch.nn import Linear, ReLU, CrossEntropyLoss, Sequential, Conv2d, MaxPool2d, Module, Softmax, BatchNorm2d, Dropout from torch.optim import Adam, SGD

  1. - 加载数据集
  2. 现在,让我们加载数据集,包括训练,测试样本:
  3. ```python
  4. #加载数据集
  5. train = pd.read_csv('train_LbELTWX/train.csv')
  6. test = pd.read_csv('test_ScVgIM0/test.csv')
  7. sample_submission = pd.read_csv('sample_submission_I5njJSF.csv')
  8. train.head()

§ 5. 卷积神经网络 - 图63

  • 该训练文件包含每个图像的id及其对应的标签
  • 另一方面,测试文件有id,我们必须预测它们对应的标签
  • 样例提交文件将告诉我们预测的格式

我们将一个接一个地读取所有图像,并将它们堆叠成一个数组。我们还将图像的像素值除以255,使图像的像素值在[0,1]范围内。这一步有助于优化模型的性能,让我们来加载图像:

  1. #加载训练图像
  2. train_img = []
  3. for img_name in tqdm(train['id']):
  4. #定义图像路径
  5. image_path = 'train_LbELtWX/train/' + str(img_name) + '.png'
  6. #读取图片
  7. img = imread(image_path, as_grey=True)
  8. #归一化像素值
  9. img /= 255.0
  10. #转换为浮点数
  11. img = img.astype('float32')
  12. #添加到列表
  13. train_img.append(img)
  14. #转换为numpy数组
  15. train_x = np.array(train_img)
  16. #定义目标
  17. train_y = train['label'].values
  18. train_x.shape

§ 5. 卷积神经网络 - 图64
如你所见,我们在训练集中有60,000张大小(28,28)的图像。由于图象是灰度格式的,我们只有一个单一通道,因此形状为(28,28)。现在让我们研究数据和可视化一些图像:

  1. #可视化图片
  2. i = 0
  3. plt.figure(figsize=(10, 10))
  4. plt.subplot(221), plt.imshow(train_x[i], camp='gray')
  5. plt.subplot(222), plt.imshow(train_x[i+25], camp='gray')
  6. plt.subplot(223), plt.imshow(train_x[i+50], camp='gray')
  7. plt.subplot(224), plt.imshow(train_x[i+75], camp='gray')

§ 5. 卷积神经网络 - 图65
以下是来自数据集的一些示例。接下来,我们把图像分成训练集和验证集。

  • 创建验证集并对图像进行预处理
    1. #创建验证集
    2. train_x, val_x, train_y, val_y = train_test_split(train_x,train_y,test_size = 0.1)
    3. (train_x.shape, tain_y.shape), (val_x.shape, val_y.shape)
    § 5. 卷积神经网络 - 图66
    我们在验证集中保留了10%的数据,在训练集中保留了10%的数据。接下来将图片和目标转成torch格式: ```python

    转换为torch张量

    train_x = train_x.reshape(54000, 1, 28, 28) train_x = torch.from_numpy(train_x)

转换为torch张量

train_y = train_y.astype(int); train_y = torch.from_numpy(train_y)

训练集形状

train_x.shape, train_y.shape

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1238812/1597634224803-a1fd3565-9ede-41ab-a9f9-83619ac525b7.png#height=26&id=fXb2i&originHeight=26&originWidth=436&originalType=binary&size=0&status=done&style=none&width=436)<br />同样,我们将转换验证图像:
  2. ```python
  3. #转换为torch张量
  4. val_x = val_x.reshape(6000, 1, 28, 28)
  5. val_x = torch.from_numpy(val_x)
  6. #转换为torch张量
  7. val_y = val_y.astype(int);
  8. val_y = torch.from_numpy(val_y)
  9. #验证集形状
  10. val_x.shape, val_y.shape

§ 5. 卷积神经网络 - 图67
我们的数据现在已经准备好了。最后,我们要创建CNN模型。

  • 使用PyTorch实现CNNS

我们将使用一个非常简单的CNN结构,只有两个卷积层来提取图像的特征。然后,我们将使用一个完全连接的Dense层将这些特征分类到各自的类别中,让我们定义一下架构

  1. class Net(Module):
  2. def__init__(self):
  3. super(Net,self).__init__()
  4. self.cnn_layers = Sequential(
  5. #define 2D convolution layers
  6. Conv2d(1, 4, kernel_size=3, stride=1, padding=1),
  7. BatchNorm2d(4),
  8. ReLU(inplace=True),
  9. MaxPool2d(kernel_size=2, stride=2),
  10. #define another 2d convolution layer
  11. Conv2d(4, 4, kernel_size_size=3, stride=1, padding=1),
  12. BatchNorm2d(4),
  13. ReLU(inplace=True),
  14. MaxPool2d(kernel_size=2, stride=2),
  15. )
  16. self.linear_layers = Sequential(
  17. Linear(4 * 7 * 7, 10)
  18. )
  19. #forward propogation
  20. def forward(self, x):
  21. x = self.cnn_layers(x)
  22. x = x.view(x.size(0), -1)
  23. x = self.liear_layers(x)
  24. return x
  25. '''
  26. We call the model defined now, and then define the optimizer and the loss function of the module
  27. '''
  28. #define the module
  29. module = Net()
  30. #define a optimizer
  31. optimizer = Adam(model.parameters(), lr=0.07)
  32. #define the loss function
  33. criterion = CrossEntropyLoss()
  34. #check for the availability of the GPU
  35. if torch.cuda.is_available():
  36. model = model.cuda()
  37. criterion = criterion.cuda()
  38. print(model)

§ 5. 卷积神经网络 - 图68
这是模型的架构,我们有两个卷积层和一个线性层。接下来,我们将定义一个函数来训练模型:

  1. def train(epoch):
  2. model.train()
  3. tr_loss = 0
  4. #acquisition the training dataset
  5. x_train, y_trian = Variable(train_x), Variable(train_y)
  6. #acqusition the validating dataset
  7. x_val, y_val = Variabel(val_x), Variable(val_y)
  8. #transform the variable into GPU format
  9. if torch.cuda.is_availabel():
  10. x_train = x_train.cuda()
  11. y_train = y_train.cuda()
  12. x_val = x_val.cuda()
  13. y_val = y_val.cuda()
  14. #clear the gradient
  15. optimizer.zero_grad()
  16. #predict the training and validating dataset
  17. output_train = model(x_train)
  18. output_val = model(x_val)
  19. #calculate the loss of training and validating dataset
  20. loss_train = criterion(output_train, y_train)
  21. loss_val = criterion(output_val, y_val)
  22. train_losses.append(loss_train)
  23. val_losses.append(loss_val)
  24. #update the weight
  25. loss_train.backward()
  26. optimizer.step()
  27. tr_loss = lss_train.item()
  28. if epoch%2 == 0:
  29. #print the loss of validating dataset
  30. print('Epoch:',epoch+1,'\t','loss:',loss_val)

最后,我们将对模型进行25个epoch的训练,并存储训练和验证损失:

  1. #define the times of training
  2. n_epochs = 25
  3. #store the loss of training with empty list
  4. train_losses = []
  5. #store the loss of validating with empty list
  6. val_losses = []
  7. #train the module
  8. for epoch in range(n_epochs):
  9. train(epoch)

§ 5. 卷积神经网络 - 图69
可以看出,随着epoch的增加,验证损失逐渐减小,让我们通过绘图来可视化训练和验证的损失:

  1. #drwa the curve of the loss
  2. plt.plot(train_losses, label='Training loss')
  3. plt.plot(val_losses, label='Validation loss')
  4. plt.legend()
  5. plt.show()

§ 5. 卷积神经网络 - 图70
我们可以清楚地看到,训练和验证损失是同步的,这是一个好迹象,因为模型在验证集上进行了很好的泛化。让我们在训练和验证集上检查模型的准确性:
§ 5. 卷积神经网络 - 图71

  1. #prediction of the training dataset
  2. with torch.no_grad():
  3. output = model(train_x.cuda())
  4. softmax = torch.exp(output).cpu()
  5. prob = list(softmax.numpy())
  6. predictions = np.argmax(prob, axis=1)
  7. #the accuracy of training dataset
  8. accuracy_score(train_y,predictions)

§ 5. 卷积神经网络 - 图72
训练集的准确率约为72%,相当不错。让我们来检查验证集的准确性:

  1. # prediction of validation dataset
  2. with torch.no_grad():
  3. output = model(val_x.cuda())
  4. softmax = torch.exp(output).cpu()
  5. prob = list(softmax.numpy())
  6. predictions = np.argmax(prob, axis=1)
  7. #the accuracy of validation dataset
  8. accuracy_score(val_y, predictions)

§ 5. 卷积神经网络 - 图73
正如我们看到的损失,准确度也是同步的,我们在验证集得到了72%的准确度。

  • 为验证集生成预测

最后为测试机生成预测。我们将加载测试集中的所有图像,执行与训练集相同的预处理步骤,最后生成预测。所以,让我们开始加载测试图像:

  1. #load the training images
  2. test_img = []
  3. for img_name in tqdm(test['id']):
  4. #define the path of images
  5. image_path = 'test_ScVgIM0/test/' + str(img_name) + '.png'
  6. #read the picture
  7. img = imread(image_path, as_gray=True)
  8. #normalize the pixel
  9. img /= 255.0
  10. #transform the dtype of image into float
  11. img = img.astype('float32')
  12. #add the image to list
  13. test_img.append(img)
  14. #transform the "test_img" into numpy arrsy
  15. test_x = np.array(test_img)
  16. test_x.shape

§ 5. 卷积神经网络 - 图74
现在,我们将对这些图像进行预处理步骤,类似于我们之前对训练图像所做的:

  1. #transform the "test_x" into torch format
  2. test_x = test_x.reshape(10000, 1, 28, 28)
  3. test_x = torch.from_numpy(test_x)
  4. test_x.shape

§ 5. 卷积神经网络 - 图75
最后,我们将生成对测试集的预测:

  1. #generate the prediction of training dataset
  2. with torch.no_grad():
  3. output = model(test_x.cuda())
  4. softmax = torch.exp(output).cpu()
  5. prob = list(softmax.numpy())
  6. predicitons = np.argmax(prob, axis=1)

用预测替换样本提交文件中的标签,最后保存文件并提交到排行榜

  1. #replace the label of sample submission file with prediction
  2. sample_submission['label'] = predictions
  3. sample_submission.head()

§ 5. 卷积神经网络 - 图76

  1. #save the file
  2. sample_submission.to_csv('submission.csv',index=False)

你将在当前目录中看到一个名为submission.csv的文件。你只需要把它上传到问题页面的解决方案检查器上,它就会生成分数,点击链接。我们的CNN模型在测试集上给出了大约71%的准确率,这与我们在上一篇文章中使用简单的神经网络得到的65%的准确率相比是一个很大的进步。

  • 结尾

在这篇文章中,我们研究了CNNs是如何从图像中提取特征的。他们帮助我们将之前的神经网络模型的准确率从65%提高到71%,这是一个重大的进步。你可以尝试使用CNN模型的超参数,并尝试进一步提高准确性。要调优的超参数可以是卷积层的数量、每个卷积层的滤波器数量、epoch的数量、全连接层的数量、每个全连接层的隐藏单元的数量等。

博客3

原文链接