导言
- 本文是运用pytorch对卷积神经网络(Convolutional Neural Network, CNN)的代码完结,作为之前介绍CNN原理的一个代码补充。
- 本文代码相关介绍相对较为具体,也为自己的一个学习进程,有错误的当地欢迎纠正。
- 本人介绍CNN原理的链接:
- [CNN原理介绍1] (blog.csdn.net/Lian_Ge_Blo…)
- [ CNN原理介绍2 ](深度学习4 — 卷积神经网络 2_Lian_Ge_Blog的博客-CSDN博客)
简述CNN结构
- 为便利了解,如下图所示(具体介绍看上方链接)
- 结构:一个卷积神经网络由若干卷积层、Pooling层、全衔接层组成
- 流程浅显了解(卷积):输入图片通过卷积核提取特征参数Feature Maps,此为卷积层的操作;
- 流程浅显了解(池化):得到的特征参数通过池化层进行化简削减参数量,此为池化层的操作
- 流程浅显了解(全衔接):将最终提取到的特征信息输入到全衔接神经网络进行计算
- 上面图中一张图变成了3张Feature Maps是由于Feature Maps数量跟图片通道数有关,卷积核也同样是对应的,一个通道对应一个卷积核
- 具体名称的介绍能够见:名称介绍
完整流程:
step1 导入需求的包
import torch
import torch.nn as nn
import torch.utils.data as Data
from torch.autograd import Variable
import torchvision # pytorch的一个视觉处理工具包(需独自装置)
- PyTorch中首要的包
- torch.nn :包括用于构建神经网络的模块和可扩展类的子包。
- torch.autograd :支持PyTorch中所有的可微张量运算的子包
- torch.nn.functional :一种功用接口,包括用于构建神经网络的典型操作,如丢失函数、激活函数和卷积运算
- torch.optim :包括标准优化操作(如SGD和Adam)的子包。
- torch.utils :工具包,包括数据集和数据加载程序等实用程序类的子包,使数据预处理更简单
- torchvision :一个供给对流行数据集、模型架构和计算机视觉图画转化的拜访的软件包
- 这些包或许代码中并未用到;参阅资料1
step2 数据预处理
首要是关于将数据转化成tensor的原因
- Tensor的意义:Tensor之于PyTorch就比方是array之于Numpy或许DataFrame之于Pandas,都是构建了整个框架中最为底层的数据结构;
- Tensor的区别:Tensor又与普通的数据结构不同,具有一个极为关键的特性——主动求导并且它表明一个多维矩阵,在计算方面还能够在GPU上运用以加快计算.
- PyTorch中关于数据集的处理有三个非常重要的类:Dataset、Dataloader、Sampler,它们均是 torch.utils.data 包下的模块(类)
- Dataloader是数据的加载类,它是关于Dataset和Sampler的进一步包装,用于实际读取数据,能够了解为它是这个作业的实在实践者。参阅资料2
关于torchvision中的数据集
- torchvision中datasets中所有封装的数据集都是torch.utils.data.Dataset的子类,它们都能够用torch.utils.data.DataLoader进行数据加载。以datasets.MNIST类为例,具体参数和用法如下所示:
CLASS torchvision.datasets.MNIST(
root: str,
train: bool = True,
transform: Optional[Callable] = None,
target_transform: Optional[Callable] = None,
download: bool = False
)
- root (string): 表明数据集的根目录,其间根目录存在MNIST/processed/training.pt和MNIST/processed/test.pt的子目录(其实便是对下载的文件指定方位)
- train (bool, optional): 假如为True,则从training.pt创立数据集,否则从test.pt创立数据集
- download (bool, optional): 假如为True,则从internet下载数据集并将其放入根目录。假如数据集已下载,则不会再次下载
- transform (callable, optional): 接纳PIL图片并回来转化后版本图片的转化函数(便是把图片或许numpy中的数组转化成tensor)
- target_transform (callable, optional): 接纳PIL接纳目标并对其进行变换的转化函数
- 具体参阅:参阅资料3
什么是Variable?
- variable是tensor的封装,在神经网络中,常需求反向传达这些的,所以需求各个节点是衔接在一同的,是个计算图,tensor的数据格式就比方星星之火,但是无法汇聚一同;等变成variable之后就能够慢慢燎原了。
- 关于Variable
代码分析
- Data.DataLoader:加载数据
- shuffle:表明打乱数据次序
- torch.unsqueeze:个人了解便是改变数据shape,此处便是把练习数据本来是一维的给”竖”起来作为一条一条数据进行练习(等今后我想起来更浅显的再修正,具体函数用法看下面参阅资料)
- 详情介绍:参阅资料4 参阅资料5
- 图画尺度是28*28的,具体验证可见最下面我jupyter转成的html成果
step3 界说网络结构
- Net需求继承自nn.Module,通过super(python中的超类)完结父类的初始化,个人了解类比于python中界说类要继承Object类,这样很多根底界说就能够略去了
- nn.Module是nn中十分重要的类,包括网络各层的界说及forward办法参阅资料6
- nn.Sequential回来的是一个序列容器用于建立神经网络的模块,依照被传入构造器的次序添加到nn.Sequential()容器中,比方con1中便是进行封装先进行第一层的卷积和池化,conv2同理,然后界说前向传达(注意是卷积两次) 参阅资料7
- nn.Conv2d: 在Pytorch的nn模块中,封装了nn.Conv2d()类作为二维卷积的完结,二维卷积应该是最常用的卷积方法了 参阅资料8
关于网络结构中流程梳理
-
关于卷积层nn.Conv2d中的参数介绍:in_channels 输入图片的通道数,同理out_channels为输出图片的通道数(都为自界说的,我搜集资料看到有1,3,16,32等等);kernel_size为卷积核巨细,5 * 5能够直接简写,假如是3 * 5就要写成元组的方式;stride为卷积核移动的步长;padding为填充的巨细,具体意义可见文章顶部原理介绍
-
nn.ReLU()为激活函数,运用ReLU激活函数有处理梯度消失的效果(具体效果看文章顶部原理中有介绍)
-
nn.MaxPool2d:maxpooling有部分不变性并且能够提取明显特征的同时降低模型的参数,然后降低模型的过拟合,具体操作看下图,除了最大值,还能够取平均值
-
nn.Linear首要是用于全衔接层 参阅资料8
-
x.view()便是对tensor进行reshape:参阅资料9
-
关于卷积时分图片size的变化
- 首要输入通道为1,尺度巨细为28 * 28,即(1,28,28)
- 通过卷积后由于自界说输出通道为16,那么尺度为(16,28,28)
- 通过池化层,由于卷积核是2 * 2的,所以尺度降低为(16,14,14)
- 持续conv2卷积,池化后就尺度变成了(32,7,7)
- nn.Linear具体用法(10是由于这个辨认成果是0-9,为10个类别):参阅资料10
-
关于输出尺度的计算公式
- O = (I – K + 2P)/ S +1
- I输入尺度,K是卷积核巨细,P是padding巨细,S是步长
step4 练习模型
- 关于优化器torch.optim.Adam,个人也还不是特别了解具体的效果,此处待补充链接
- 交叉熵丢失函数(合适分类模型): 个人之前的总结
- optimizer.zero_grad()关于优化器清空上层的梯度
- 有介绍是为了优化内存的 : 参阅资料11
- 有介绍是一种tips成心不清零来进行梯度优化的
- 总归仍是那句,优化器我搞懂了再补上…
- 其实单纯论反向传达得时分梯度的更新形似没有提及是否需求清空上层梯度:梯度下降与反向传达
- loss.backward()反向传达上面介绍Variable的时分现已讲过,现已封装好了(看CNN原理部分反向传达求各个参数不同层级参数的偏导求得类似,成果在这里只需求调用api…)
- torch.max 浅显讲便是回来列表中最大的数,具体用法能够见最下方html文件
完整代码
- 参阅资料12
- 参阅资料13
import torch
import torch.nn as nn
import torchvision
import torch.utils.data as Data
torch.manual_seed(1) # 设置随机种子, 用于复现
# 超参数
EPOCH = 1 # 前向后向传达迭代次数
LR = 0.001 # 学习率 learning rate
BATCH_SIZE = 50 # 批量练习时分一次送入数据的size
DOWNLOAD_MNIST = True
# 下载mnist手写数据集
# 练习集
train_data = torchvision.datasets.MNIST(
root = './MNIST/',
train = True,
transform = torchvision.transforms.ToTensor(),
download=DOWNLOAD_MNIST
)
# 测验集
test_data = torchvision.datasets.MNIST(root='./MNIST/', train=False) # train设置为False表明获取测验集
# 一个批练习 50个样本, 1 channel通道, 图片尺度 28x28 size:(50, 1, 28, 28)
train_loader = Data.DataLoader(
dataset = train_data,
batch_size=BATCH_SIZE,
shuffle=True
)
# 测验数据预处理;只测验前2000个
test_x = torch.unsqueeze(test_data.data,dim=1).float()[:2000] / 255.0
# shape from (2000, 28, 28) to (2000, 1, 28, 28)
test_y = test_data.targets[:2000]
class CNN(nn.Module):
def __init__(self):
super(CNN,self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d( # 输入的图片 (1,28,28)
in_channels=1,
out_channels=16, # 通过一个卷积层之后 (16,28,28)
kernel_size=5,
stride=1, # 假如想要 con2d 出来的图片长宽没有变化, padding=(kernel_size-1)/2 当 stride=1
padding=2
),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2) # 通过池化层处理,维度为(16,14,14)
)
self.conv2 = nn.Sequential(
nn.Conv2d( # 输入(16,14,14)
in_channels=16,
out_channels=32,
kernel_size=5,
stride=1,
padding=2
), # 输出(32,14,14)
nn.ReLU(),
nn.MaxPool2d(kernel_size=2) # 输出(32,7,7)
)
self.out = nn.Linear(32*7*7,10)
def forward(self, x):
x = self.conv1(x) #(batch_size,16,14,14)
x = self.conv2(x) # 输出(batch_size,32,7,7)
x = x.view(x.size(0),-1) # (batch_size,32*7*7)
out = self.out(x) # (batch_size,10)
return out
cnn = CNN()
optimizer = torch.optim.Adam(cnn.parameters(),lr=LR) # 界说优化器
loss_func = nn.CrossEntropyLoss() # 界说丢失函数
for epoch in range(EPOCH):
for step,(batch_x,batch_y) in enumerate(train_loader):
pred_y = cnn(batch_x)
loss = loss_func(pred_y,batch_y)
optimizer.zero_grad() # 清空上一层梯度
loss.backward() # 反向传达
optimizer.step() # 更新优化器的学习率,一般依照epoch为单位进行更新
if step % 50 == 0:
test_output = cnn(test_x)
pred_y = torch.max(test_output, 1)[1].numpy() # torch.max(test_out,1)回来的是test_out中每一行最大的数)
# 回来的方式为torch.return_types.max(
# values=tensor([0.7000, 0.9000]),
# indices=tensor([2, 2]))
# 后边的[1]代表获取indices
print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.numpy())
# 打印前十个测验成果和实在成果进行比照
test_output = cnn(test_x[:10])
pred_y = torch.max(test_output, 1)[1].numpy()
print(pred_y, 'prediction number')
print(test_y[:10].numpy(), 'real number')
补充一个测验进程掌握的小tips
- 比方运用anaconda中的jupyter测验,但是anaconda中又很多虚拟环境,怎样将jupyter切换到测验用的虚拟环境呢
- 需求注册,具体参阅: 参阅资料14
- 比方我创立CNN测验环境是名称是python36,而默认是python3也便是base环境,依照上述装置并注册后就能够切换啦
附件
- 测验CNN代码的具体过程html文件(未找到编辑器怎么传附件,那就弄成链接吧)
-
- 测验CNN代码的具体过程html文件
- GitHub上资源(html文件自己下载用浏览器打开)
- csdn资源(这个我设置确实是免费,形似下载仍是要收费)