赞
踩
学校自然语言处理的第一次大作业,之前没有过python基础,更不会pytorch,花了些功夫算是勉强完成了作业,用这篇博客记录一下。代码不够好,模型准确率也不够高,算是提供一个小白视角吧~
1.掌握卷积神经网络、循环神经网络等深度学习的各项基本技术。
2.加强对pytorch、tensorflow等深度学习框架的使用能力。
任选一个深度学习框架,实现在给定数据集上的基于DNN、CNN、RNN的图片分类模型(给出模型准确率)。
非常重要的一些说明:
本次作业的核心代码参考了这篇博客(十分惭愧,面向CSDN深度学习了)
CNN入门+猫狗大战(Dogs vs. Cats)+PyTorch入门_l1076604169的博客-CSDN博客
在这篇博客中,作者对猫狗大战的数据集,以及CNN的各种知识点做了详细的说明,博主的代码和注释也写得非常漂亮!!!是非常好的CNN的入门资料。
不过,博主只实现了CNN,并且对测试数据集只做了图片显式并没有计算出识别准确率,因此我的作业在此基础上做了以下完善:
1.补充DNN,RNN的实现
2.训练过程绘制损失图像
3.计算测试过程准确率
代码:自然语言处理第一次作业-DNNRNNCNN实现猫狗分类-深度学习文档类资源-CSDN文库
CNN,RNN,DNN三个文件夹中分别存储三个文件:_network.py描述模型结构,_train.py描述模型训练,_test.py描述模型测试,实际上这三份测试代码以及训练代码只有路径不同,但是为了节约调试时间,在这个程序中并没有将三份代码合并起来。
data文件夹中保存了训练图片和测试图片,训练图片共4000张(为了加强模型效果将2000张图片扩充到了4000),测试图片共500张。训练图片与测试图片互斥。
model文件夹中保存了已经训练好的模型
getedata.py用来对图片进行预处理
1.运行_train.py文件训练模型,并将训练好的模型保存在model文件夹中
2.运行_test.py文件测试模型
目前model文件夹中的模型已经训练完成,只需要运行_test.py文件既可以看到模型准确率:CNN:0.704,RNN:0.506,DNN:0.502。
数据预处理流程图:
在原始的数据集中,图片大小不整齐,因此需要对数据进行缩放和裁剪,统一为相同尺寸,这里取了一个相对平均的尺寸200X200,同时也需要将图片格式转化为pytorch的Tensor格式。
- dataTransform = transforms.Compose([
- transforms.Resize(IMAGE_SIZE), # 比例缩放至合适尺寸
- transforms.CenterCrop((IMAGE_SIZE, IMAGE_SIZE)),# 裁剪合适大小的图像
- transforms.ToTensor() # 转换成Tensor形式
-
在data文件夹中有train和test两个子文件夹,分别存储训练图片以及测试图片。观察图片可以看到其命名方式是cat.x.jpg,第一个字符串即为图片的便签,因此可以将图片名称以‘.’划分为三段,name[0]为‘cat’时,在标签中存入0,name[0]=‘dog’时,在标签中存入1。需要注意的是,在训练阶段和测试阶段都需要读取到图片标签,前者用于计算训练过程中的损失,后者用于计算测试过程中的精确度。
卷积神经网络适用于处理图片,因此它也是这三个模型中表现最佳的一个。它由若干卷积池化层以及若干全连接层构成。
本模型采用两层卷积池化,并叠加三层全连接层
输入层:200X200X3
在数据预处理阶段,我们将图片调整为了200X200的尺寸,由于是彩色图片,有RGB三个通道(每一个通道对应一个feature map)所以输入应该为一个200X200X3的矩阵。
卷积层1:卷积核3X3X3X16,卷积输出200X200X16
在卷积层由卷积核对输入层图像进行卷积操作提取特征。每一个卷积核会生成一个feature map,卷积核的尺寸为(SxSxCxN),C表示卷积核的深度,N表示卷积核的个数。第一层卷积层的尺寸为3X3,深度为3,卷积核的深度必须与输入层的通道数量一致,卷积核数量为16。 经过该卷积层后,会输出16个卷积图像,尺寸大小为200X200。
卷积层不影响图片尺寸,只影响通道数目
最大池化层1:池化核2X2,池化输出100X100X16
池化层是对图片进行采样,降低图片的分辨率,最常用的池化方法是最大池化max pooling,它会将池化核内最大的数作为输出。第一层池化核的大小为2X2,它将200X200的图片尺寸转化为100X100的尺寸。
池化层不影响通道数目,只影响图片尺寸
卷积层2:卷积核3X3X16X16,卷积输出100X100X16
卷积核大小为3X3,深度为16,一共16个卷积核,经过卷积计算之后输出16个100X100的卷积图像。
最大池化层2:池化核2X2,池化输出50X50X16
经过第二个池化层,图像尺寸进一步缩小,变为50X50,共输出16个特征图像。
全连接层1:
第一个全连接层接收最后一个池化层的输出,输入结点数目为50X50X16=40000,输出结点个数为128
全连接层2:
输入结点数目为128,输出结点数为64
全连接层3:
输入结点数目为64,输出节点数为2(本实验为二分类问题,因此输出结点为2)
激活函数:
在每一个卷积层以及全连接层后都叠加一层ReLu()激活函数
代码:
- class Net(nn.Module):
- def __init__(self):
- super(Net, self).__init__()
-
-
-
- self.conv1 = torch.nn.Conv2d(3, 16, 3, padding=1) # 第一个卷积层,输入通道数3,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认
- self.conv2 = torch.nn.Conv2d(16, 16, 3, padding=1) # 第二个卷积层,输入通道数16,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认
-
- self.fc1 = nn.Linear(50*50*16, 128) # 第一个全连层,线性连接,输入节点数50×50×16,输出节点数128
- self.fc2 = nn.Linear(128, 64) # 第二个全连层,线性连接,输入节点数128,输出节点数64
- self.fc3 = nn.Linear(64, 2) # 第三个全连层,线性连接,输入节点数64,输出节点数2
-
- def forward(self, x): # 重写父类forward方法,即前向计算,通过该方法获取网络输入数据后的输出值
- x = self.conv1(x) # 第一次卷积
- x = F.relu(x) # 第一次卷积结果经过ReLU激活函数处理
- x = F.max_pool2d(x, 2) # 第一次池化,池化大小2×2,方式Max pooling
-
- x = self.conv2(x) # 第二次卷积
- x = F.relu(x) # 第二次卷积结果经过ReLU激活函数处理
- x = F.max_pool2d(x, 2) # 第二次池化,池化大小2×2,方式Max pooling
-
- x = x.view(x.size()[0], -1) # 由于全连层输入的是一维张量,因此需要对输入的[50×50×16]格式数据排列成[40000×1]形式,-1表示不确定的数
- x = F.relu(self.fc1(x)) # 第一次全连,ReLU激活
- x = F.relu(self.fc2(x)) # 第二次全连,ReLU激活
- y = self.fc3(x) # 第三次激活,ReLU激活
- return y
-
将训练集数量从2000(猫1000,狗1000)扩为4000(猫2000,狗2000),准确率从0.6提升到0.7。
神经网络是基于感知机的扩展,DNN是有很多隐藏层的神经网络。从DNN按不同层的位置划分,DNN内部的神经网络层可以分为三类,输入层,隐藏层和输出层,一般来说第一层是输入层,最后一层是输出层,而中间的层数都是隐藏层。
- class myNet(nn.Module):
- def __init__(self):
- super().__init__()
- self.layers = nn.Sequential(
- nn.Linear(200*200*3,512),
- nn.ReLU(),
-
- nn.Linear(512, 128),
- nn.ReLU(),
-
- nn.Linear(128, 2),
- nn.Softmax(dim=1),
-
- )
本实验的DNN模型由3个全连接层组成
全连接层一:输入200X200X3个结点,输出512个结点
全连接层二:输入512个结点,输出128个结点
全连接层三:输入128个结点,输出2个结点
nn.linear()是用来设置网络中的全连接层,第一个参数为输入的特征数目,第二个参数为输出的特征数。
1.增加一个线性层:
将三层全连接层模型修改为四层以及五层模型,得到准确率都在0.5左右,没有明显提升。
2.增加epoch
将epoch次数从10改为20,准确率无提升。
3.扩充训练数据集
将训练集数量从2000(猫1000,狗1000)扩为4000(猫2000,狗2000),但是准确率没有提升。
本实验采用LSTM模型。LSTM是一种特殊的RNN,相比于原始的RNN的隐层(hidden state), LSTM增加了一个细胞状态(cell state),可以解决RNN无法处理长距离的依赖的问题,一般用于处理序列数据,LSTM能够在更长的序列中有更好的表现。所以在猫狗分类问题中,该模型并没有很优秀的表现。
- class Net(nn.Module):
- def __init__(self):
- super(Net, self).__init__()
- self.rnn = nn.LSTM(
- input_size=120000, # 等于3*200*200
- hidden_size=64, # 隐藏层神经元的个数
- num_layers=1, # RNN层数
- batch_first=True,
- )
-
- self.out = nn.Linear(64, 2)
-
- def forward(self, x):
- x = x.view(len(x), 1, -1)
- r_out, (h_n, h_c) = self.rnn(x, None)
- out = self.out(r_out[:, -1, :])
nn.LSTM()参数描述:
模型训练流程:
首先需要将训练数据集中的图片一张一张加载到模型中,得到模型的预测结果,将预测结果与图片标签传入 CrossEntropyLoss()计算损失,也就是网络输出值和实际label的差异,差异越小说明网络拟合效果越好。误差经过backward() 函数进行反向传播,采用求导的方式,计算网络中每个节点参数的梯度,梯度越大说明参数设置不合理,需要调整。接下来采用adam方法对网络中的各个参数进行调整,最后通过zero_grad()清除优化器中的梯度以便下一次计算,因为优化器默认会保留,不清除的话,每次计算梯度都会累加。
相关参数说明:
workers=8:设置pytorch并行读取数据的线程数量为8,这是有本机电脑配置决定的
batch_size = 16:设置一批次处理的图像数量为16
lr = 0.0001:设置学习率为0.0001,它会作为Adam()函数中对参数进行优化。
nepoch = 10:综合考虑训练数据集的数量以及训练时间,将nepoch设置为10,代表所有的数据要经过模型处理10次
相关函数说明:
backward():自动求导函数。在猫狗分类问题中,模型的输出是一个标量,此时backward函数不需要输入任何参数。
adam():这种优化算法结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点,对内存需求较小,为不同的参数计算不同的自适应学习率。
CrossEntropyLoss():指交叉熵损失函数, 该损失函数结合了nn.LogSoftmax()和nn.NLLLoss()两个函数。
模型测试流程:
首先将测试数据集中图片一张一张送入模型处理,得到一个预测值out,这个预测值需要经过softmax函数处理,得到两个和为1的概率[p猫,p狗],哪一个概率大就将该样本分为哪一类。获取了分类结果之后,将其与图片的标签进行比较,如果匹配,count值加1,最后将模型的准确率定义为acc=count/N,其中N为测试图片的总数500。
准确率结果:
CNN:0.704
RNN:0.506
DNN:0.502
PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】_哔哩哔哩_bilibili
例如:训练样本10000条,batchsize设置为20,将所有的训练样本在同一个模型中训练5遍,则epoch=5,batchsize=20, iteration=10000/20=500
标量是零阶张量,向量是一阶张量,矩阵是二阶张量。可以把张量想象成一桶数字张量和多维数组是不同类型的对象。前者是一种函数,后者是适宜在坐标系统中表示张量的一种数据结构态创建的计算图自动计算所有参数上的梯度。总的来说,这一步进行的是梯度下降和反向传播。
参考:如何确定神经网络的层数和隐藏层神经元数量 - 知乎 (zhihu.com)
没有隐藏层:仅能够表示线性可分函数或决策
隐藏层数=1:可以拟合任何“包含从一个有限空间到另一个有限空间的连续映射”的函数
隐藏层数=2:搭配适当的激活函数可以表示任意精度的任意决策边界,并且可以拟合任何精度的任何平滑映射
隐藏层数>2:多出来的隐藏层可以学习复杂的描述(某种自动特征工程)
层数越深,理论上拟合函数的能力增强,效果按理说会更好,但是实际上更深的层数可能会带来过拟合的问题,同时也会增加训练难度,使模型难以收敛。因此一般的经验是,在使用BP神经网络时,可以参照已有的表现优异的模型,如果实在没有,则可以,从一两层开始尝试,尽量不要使用太多的层数。
双向RNN时bidirectional设置为true
一般的按序列顺序过来的RNN会记录、保存来自前面序列的信息,这些历史信息对当前的输出是很有帮助的。但是有些问题,序列当前位置历史信息和这个位置未来的信息会共同对计算当前位置的输出有帮助,例如在NLP里面的人名识别里面, 如果我们很确信下一个字符是人名的开始,那么当前位置再是人名的开始的概率就会相当的低。
猫狗分类问题首先并不是序列问题,且未来的信息并不会对当前的预测结果产生任何影响,因此在猫狗分类问题中,不应该设置双向RNN
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。