CIFAR-100数据集在简易CNN(LeNet-5修正)和简易DNN的作用对比
项目阐明
该项目为课程作业,试验进程等没有十分严谨,如有问题请纠正,会及时改正!
摘要
本文对相同数据集运用简易线性神经网络和卷积神经网络进行探求,别离运用3层线性网络和LeNet-5的修正版别(通道数修正)进行练习并对数据状况进行分析。经过试验探求简易卷积网络和线性网络在相同数据集下的练习作用和状况,初步探求练习,简易CNN和DNN模型在该数据集的作用及含义。
关键词
神经网络,CIFAR-100,CNN,DNN
导言
在人工智能技能盛行的今天,在cv(核算机视觉)领域各种作用极佳的模型纷纷积极,回顾开展前史卷积网络的提出有着不行忽视的含义和作用,为了探求相同数据集在简易卷积神经网络和线性神经网络之间的作用差异,进行下列试验,经过数据集引进,数据处理,模型构建,模型练习,练习结果可视化等方法对数据作用进行展现,通Loss和ACC对模型作用进行简易判别。
import paddle
import numpy as np
import matplotlib.pyplot as plt
import paddle.nn as nn
from paddle.vision.datasets import Cifar100
from paddle.vision.transforms import Normalize
paddle.__version__
'2.3.0'
数据阐明
CIFAR-100 数据集由 100 个类别的 60000 个 32×32 五颜六色图画组成,每个类别包含 6000 个图画。有 500 个练习图画和 100 个测验图画。CIFAR-100 中的 100 个类分为 20 个超类。每个图画都带有一个“精细”标签(它所属的类)和一个“粗略”标签(它所属的超类)。
超类 | 课程 |
---|---|
水生哺乳动物 | 海狸, 海豚, 水獭, 海豹, 鲸鱼 |
鱼 | 观赏鱼, 比目鱼, 射线, 鲨鱼, 鳟鱼 |
花卉 | 兰花、罂粟、玫瑰、向日葵、郁金香 |
食品容器 | 瓶、碗、罐、杯、盘 |
生果和蔬菜 | 苹果、蘑菇、橙子、梨、甜椒 |
家用电器 | 时钟, 电脑键盘, 灯, 电话, 电视 |
家居家具 | 床, 椅子, 沙发, 桌子, 衣柜 |
昆虫 | 蜜蜂, 甲虫, 蝴蝶, 毛毛虫, 甲由 |
大型食肉动物 | 熊, 豹, 狮子, 虎, 狼 |
大型人造野外物品 | 桥, 城堡, 屋, 路, 摩天大楼 |
大型天然野外场景 | 云, 森林, 山, 平原, 海 |
大型杂食动物和草食动物 | 骆驼, 牛, 黑猩猩, 大象, 袋鼠 |
中型哺乳动物 | 狐狸、豪猪、负鼠、浣熊、臭鼬 |
非昆虫无脊椎动物 | 螃蟹、龙虾、蜗牛、蜘蛛、蠕虫 |
人们 | 宝物, 男孩, 女孩, 男人, 女人 |
爬虫类 | 鳄鱼, 恐龙, 蜥蜴, 蛇, 龟 |
小型哺乳动物 | 仓鼠, 老鼠, 兔子, 鼩鼱, 松鼠 |
树木 | 枫木, 橡木, 棕榈, 松树, 杨柳 |
车辆 1 | 自行车, 公共汽车, 摩托车, 皮卡车, 火车 |
车辆 2 | 割草机, 火箭, 有轨电车, 坦克, 拖拉机 |
参阅地址:www.cs.toronto.edu/~kriz/cifar…
数据导入
# 数据预处理界说
normalize = Normalize(mean=[0.5, 0.5, 0.5],
std=[0.5, 0.5, 0.5],
data_format='HWC')
# 练习集和验证集界说
train_cifar100 = Cifar100(mode='train', transform=normalize)
test_cifar100 = Cifar100(mode='test', transform=normalize)
结构数据并修正shape
数据的形状为(32,32,3)里边的通道平和时经常运用的方法不相同所以经过paddle.io.dataset
从头进行结构使得数据变成(3,32,32)的样式
'''
自界说数据集
'''
from paddle.io import Dataset
class MyDataset(paddle.io.Dataset):
"""
进程一:承继paddle.io.Dataset类
"""
def __init__(self, data_):
"""
进程二:完成结构函数,界说数据集巨细
"""
super(MyDataset, self).__init__()
self.data = []
self.label = []
for i in range(len(data_)):
x = data_[i][0].transpose((2, 0, 1))
y = data_[i][1]
self.data.append(x)
self.label.append(np.array(y).astype('int64'))
def __getitem__(self, index):
"""
进程三:完成__getitem__办法,界说指定index时如何获取数据,并回来单条数据(练习数据,对应的标签)
"""
# 回来单一数据和标签
data = self.data[index]
label = self.label[index]
# 注:回来标签数据时必须是int64
return data, np.array(label, dtype='int64')
def __len__(self):
"""
进程四:完成__len__办法,回来数据集总数目
"""
# 回来数据总数
return len(self.data)
# 测验界说的数据集
train_dataset = MyDataset(data_=train_cifar100)
eval_dataset = MyDataset(data_=test_cifar100)
print('=============train_dataset =============')
# 输出数据集的形状和标签
print(train_dataset.__getitem__(1)[0].shape,train_dataset.__getitem__(1)[1])
# 输出数据集的长度
print(train_dataset.__len__())
print('=============eval_dataset =============')
# 输出数据集的形状和标签
for data, label in eval_dataset:
print(data.shape, label)
break
# 输出数据集的长度
print(eval_dataset.__len__())
DNN网络(深度神经网络)
DNN属于第3代神经网络,一般含义上大于两层的线性网络属于DNN。DNN有时也叫做多层感知机(Multi-Layer perceptron,MLP)
DNN是一种最简略的神经网络。各个神经元别离属于不同的层,每个神经元和前一层的一切神经元相衔接,信号从输入层向输出层单向传达。
从DNN按不同层的方位区分,DNN内部的神经网络层能够分为三类,输入层,躲藏层和输出层,如下图示例,一般来说第一层是输入层,最终一层是输出层,而中间的层数都是躲藏层。
层与层之间是全衔接的,也就是说,第i层的恣意一个神经元必定与第i+1层的恣意一个神经元相连。虽然DNN看起来很杂乱,可是从小的部分模型来说,仍是和感知机相同,即一个线性关系 加上一个激活函数。
简略来说就是一个输入层加上躲藏层加上输出层构成的简易神经网络。躲藏层的深度在必定程度上决定模型的杂乱度和作用。
DNN模型的构建
from paddle.nn import Linear
import paddle.nn.functional as F
import paddle
# 界说DNN网络
class MyDNN(paddle.nn.Layer):
def __init__(self):
super(MyDNN, self).__init__()
self.hidden1 = Linear(3*32*32, 2048) # 输入层界说
self.hidden2 = Linear(2048, 1024) # 躲藏层1界说
self.hidden3 = Linear(1024, 128) # 躲藏层2界说
self.hidden4 = Linear(128, 100) # 输出层界说
def forward(self, input):
# print(input.shape)
x = paddle.reshape(input, shape=[-1,3*32*32]) # 数据拉直
x = self.hidden1(x) # 输入层
x =F.relu(x) # 激活函数
# print(x.shape)
x = self.hidden2(x) # 躲藏层1
x = F.relu(x)
# print(x.shape)
x = self.hidden3(x) # 躲藏层2
x = F.relu(x)
# print(x.shape)
x = self.hidden4(x) # 输出层
y = F.softmax(x)
# print(y.shape)
return y
model = MyDNN() # 网络实例化
paddle.summary(model,(3,32,32)) # 网络结构检查
---------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===========================================================================
Linear-1 [[1, 3072]] [1, 2048] 6,293,504
Linear-2 [[1, 2048]] [1, 1024] 2,098,176
Linear-3 [[1, 1024]] [1, 128] 131,200
Linear-4 [[1, 128]] [1, 100] 12,900
===========================================================================
Total params: 8,535,780
Trainable params: 8,535,780
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.01
Forward/backward pass size (MB): 0.03
Params size (MB): 32.56
Estimated Total Size (MB): 32.60
---------------------------------------------------------------------------
{'total_params': 8535780, 'trainable_params': 8535780}
运用DNN模型对数据进行练习
经过上面临模型的结构检查能够清楚看到,输入层为(1, 3072)就是(3, 32, 32)数据拉平。躲藏层为两层的线性结构。输出层为100个输出的输出层。
对吗,模型进行练习,此处运用了Adam优化器,能够运用梯度的一阶矩估量和二阶矩估量动态调整每个参数的学习率。其参数更新的核算公式如下:
参阅文献:Adam: A Method for Stochastic Optimization arxiv.org/abs/1412.69…
丢失函数:运用的是CrossEntropyLoss,用于核算输入input和标签label间的穿插熵丢失 ,它结合了 LogSoftmax 和 NLLLoss 的核算,适用于练习一个 n 类分类器。
评价目标:运用的是Accuracy,用于核算准确率。
依据练习轮数50轮,批次128,打乱样本进行练习。
# 用Model封装模型
model = paddle.Model(MyDNN())
# 界说丢失函数
model.prepare(paddle.optimizer.Adam(parameters=model.parameters()),
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy(topk=(1,5)))
# 练习可视化VisualDL东西的回调函数
visualdl = paddle.callbacks.VisualDL(log_dir='visualdl_log')
# 发动模型全流程练习
model.fit(train_dataset, # 练习数据集
eval_dataset, # 评价数据集
epochs=50, # 总的练习轮次
batch_size = 128, # 批次核算的样本量巨细
shuffle=True, # 是否打乱样本集
verbose=1, # 日志展现格局
save_dir='./chk_points/', # 分阶段的练习模型存储途径
callbacks=[visualdl]) # 回调函数运用
# 保存模型
model.save('model_save_dir')
练习数据可视化
经过把练习数据进行可视化展现能够得到以下数据
DNN数据信息
经过练习数据能够看出来测验集里边loss比较稳定,acc存在必定的动摇,可是loss值偏高,ACC偏低,能够看出作用较差。经过loss的稳定能够看出来在必定程度上该模型现已到达一种饱满程度,模型杂乱的和深度不足以支撑该任务的练习。
CNN
CNN是一种经过卷积核算的前馈神经网络,其是受生物学上的感受野机制提出的,具有平移不变性,运用卷积核,最大的运用了部分信息,保留了平面结构信息。
运用反向传达算法练习的多层神经网络构成了成功的根据梯度的学习技能,给定恰当的网络架构,根据梯度的学习算法可用于合成杂乱的决议计划面,该决议计划面能够对手写字符等高维形式进行分类,且预处理最少。卷积神经网络专门规划用于处理 2D 形状的可变性,其功能优于当时的一切其他技能。(该技能为1998年的技能,不是最新成果)。首要运用的是LeNet-5在手写数字辨认(yann.lecun.com/exdb/mnist/ )项目上的,在当时有着比较好的作用。
LeNet-5
LeNet-5是基础卷积神经网络
是从原始信号—>发现边际和方向—>不断抽象—>不断抽象的一个进程
LeNet-5由输入层,两个卷积层两个均匀池化层组成,后面接了两个线性层还有一个输出层。
详细今天常考下面的图及原论文。
参阅地址:ieeexplore.ieee.org/document/72…
论文地址(直接可读):arxiv.org/abs/1412.69…
[1] Y. Lecun, L. Bottou, Y. Bengio and P. Haffner, “Gradient-based learning applied to document recognition,” in Proceedings of the IEEE, vol. 86, no. 11, pp. 2278-2324, Nov. 1998, doi: 10.1109/5.726791.
卷积、池化和激活函数阐明
卷积
每个卷积单元的参数都是经过反向传达算法最佳化得到的。卷积运算的意图是提取输入的不同特征,第一层卷积层或许只能提取一些初级的特征如边际、线条和角等层级,更多层的网路能从初级特征中迭代提取更杂乱的特征。
- 单层卷积: 输入数据在和卷积核在指定做卷积操作。然后生成新的具有详细特征的新数据
- 多层卷积:
和单层卷积相同,可是生成新的数据也是多层的数据
- 填充(Padding)
角落边际的像素,只被一个过滤器输出所运用,因为它坐落这个33的区域的一角。但假如是在中间的像素点,就会有许多33的区域与之堆叠。 所以那些在角落或许边际区域的像素点在输出中采用较少,意味着你丢掉了图画边际方位的许多信息。 那么呈现的一个处理办法就是填充操作,在原图画外围以0进行填充,在不影响特征提取的同时,添加了对边际信息的特征提取。 别的一个优点是,咱们在做卷积操作时,每经过一次卷积咱们的输入图画巨细就会变小,最终经过多次卷积或许咱们的图画会变得特别小,咱们不希望图画变小的话就能够经过填充操作。
池化
池化是运用某一方位的相邻输出的总体核算特征替代网络在该方位的输出,其优点是当输入数据做出少数平移时,经过池化函数后的大多数输出还能坚持不变。比方:当辨认一张图画是否是人脸时,咱们需求知道人脸左边有一只眼睛,右边也有一只眼睛,而不需求知道眼睛的精确方位,这时候经过池化某一片区域的像素点来得到总体核算特征会显得很有用。因为池化之后特征图会变得更小,假如后面衔接的是全衔接层,能有用的减小神经元的个数,节省存储空间并提高核算功率。
池化的作用
池化层是特征挑选和信息过滤的进程,进程中会丢失一部分信息,可是会同时会削减参数和核算量,在模型作用和核算功能之间寻觅平衡,跟着运算速度的不断提高,渐渐或许会有一些规划上的改变,现在有些网络现已开端少用或许不必池化层。
池化的步长和卷积核的巨细有关,默认长度为2
均匀池化(Avg Pooling)
对邻域内特征点求均匀
- 优缺点:能很好的保留背景,但简略使得图片变含糊
- 正向传达:邻域内取均匀
- 反向传达:特征值依据领域巨细被均匀,然后传给每个索引方位
最大池化(Max Pooling)
对邻域内特征点取最大
- 优缺点:能很好的保留一些关键的纹理特征,现在更多的再运用Max Pooling而很少用Avg Pooling
- 正向传达:取邻域内最大,并记住最大值的索引方位,以便利反向传达
- 反向传达:将特征值填充到正向传达中,值最大的索引方位,其他方位补0
核算结果的巨细公示
激活函数
- Sigmoid
- Tanh
Sigmoid和Tanh激活函数有一同的缺点:即在z很大或很小时,梯度简直为零,因此运用梯度下降优化算法更新网络很慢。
- ReLU
Relu目前是选用比较多的激活函数,可是也存在一些缺点,在z小于0时,斜率即导数为0。
为了处理这个问题,后来也提出来了Leaky Relu激活函数,不过目前运用的不是特别多。
- 注此处运用的是Tanh激活函数
LeNet-5模型搭建
# LeNet-5
network = nn.Sequential(
nn.Conv2D(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=0), # C1 卷积层
nn.Tanh(),
nn.AvgPool2D(kernel_size=2, stride=2), # S2 平局池化层
nn.Sigmoid(), # Sigmoid激活函数
nn.Conv2D(in_channels=6, out_channels=16, kernel_size=5, stride=1, padding=0), # C3 卷积层
nn.Tanh(),
nn.AvgPool2D(kernel_size=2, stride=2), # S4 均匀池化层
nn.Sigmoid(), # Sigmoid激活函数
nn.Conv2D(in_channels=16, out_channels=120, kernel_size=5, stride=1, padding=0), # C5 卷积层
nn.Tanh(),
nn.Flatten(),
nn.Linear(in_features=120, out_features=84), # F6 全衔接层
nn.Tanh(),
nn.Linear(in_features=84, out_features=10) # OUTPUT 全衔接层
)
paddle.summary(network, (1, 1, 32, 32))
模型修正为3层
# LeNet-5改
network = nn.Sequential(
nn.Conv2D(in_channels=3, out_channels=6, kernel_size=5, stride=1, padding=0), # C1 卷积层
nn.Tanh(),
nn.AvgPool2D(kernel_size=2, stride=2), # S2 平局池化层
nn.Sigmoid(), # Sigmoid激活函数
nn.Conv2D(in_channels=6, out_channels=16, kernel_size=5, stride=1, padding=0), # C3 卷积层
nn.Tanh(),
nn.AvgPool2D(kernel_size=2, stride=2), # S4 均匀池化层
nn.Sigmoid(), # Sigmoid激活函数
nn.Conv2D(in_channels=16, out_channels=120, kernel_size=5, stride=1, padding=0), # C5 卷积层
nn.Tanh(),
nn.Flatten(),
nn.Linear(in_features=120, out_features=100), # F6 全衔接层
nn.Tanh(),
nn.Linear(in_features=100, out_features=100) # OUTPUT 全衔接层
)
paddle.summary(network, (1, 3, 32, 32))
根据最新的结构函数进行改写
对paddle.nn.Layer进行承继,函数和向前核算网络进行分隔书写,愈加便利对网络的了解和对问题的检查
class LeNet_G(nn.Layer):
"""
承继paddle.nn.Layer界说网络结构
"""
def __init__(self, num_classes=100):
"""
初始化函数
"""
super(LeNet_G, self).__init__()
self.conv2D1 = nn.Conv2D(in_channels=3, out_channels=6, kernel_size=5, stride=1, padding=0) # C1 卷积层
self.conv2D2 = nn.Conv2D(in_channels=6, out_channels=16, kernel_size=5, stride=1, padding=0) # C3 卷积层
self.conv2D3 = nn.Conv2D(in_channels=16, out_channels=120, kernel_size=5, stride=1, padding=0) # C5 卷积层
self.avgpool2D1 = nn.AvgPool2D(kernel_size=2, stride=2) # S2均匀池化
self.avgpool2D2 = nn.AvgPool2D(kernel_size=2, stride=2) # S4 均匀池化层
self.sigmoid = nn.Sigmoid() # 激活函数
self.tanh = nn.Tanh()
self.linear1 = nn.Linear(in_features=120, out_features=100) # F6 全衔接层
self.linear2 = nn.Linear(in_features=100, out_features=num_classes) # OUTPUT 全衔接层
def forward(self, inputs):
"""
前向核算
"""
inputs = paddle.to_tensor(inputs)
x = self.conv2D1(inputs)
x = self.tanh(x)
x = self.avgpool2D1(x)
x = self.sigmoid(x)
x = self.conv2D2(x)
x = self.tanh(x)
x = self.avgpool2D2(x)
x = self.sigmoid(x)
x = self.conv2D3(x)
x = self.tanh(x)
x = paddle.flatten(x, 1)
x = self.linear1(x)
x = self.tanh(x)
y = self.linear2(x)
return x
network_1 = LeNet_G()
paddle.summary(network_1, (1, 3, 32, 32))
---------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===========================================================================
Conv2D-1 [[1, 3, 32, 32]] [1, 6, 28, 28] 456
Tanh-1 [[1, 100]] [1, 100] 0
AvgPool2D-1 [[1, 6, 28, 28]] [1, 6, 14, 14] 0
Sigmoid-1 [[1, 16, 5, 5]] [1, 16, 5, 5] 0
Conv2D-2 [[1, 6, 14, 14]] [1, 16, 10, 10] 2,416
AvgPool2D-2 [[1, 16, 10, 10]] [1, 16, 5, 5] 0
Conv2D-3 [[1, 16, 5, 5]] [1, 120, 1, 1] 48,120
Linear-1 [[1, 120]] [1, 100] 12,100
Linear-2 [[1, 100]] [1, 100] 10,100
===========================================================================
Total params: 73,192
Trainable params: 73,192
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.01
Forward/backward pass size (MB): 0.07
Params size (MB): 0.28
Estimated Total Size (MB): 0.36
---------------------------------------------------------------------------
{'total_params': 73192, 'trainable_params': 73192}
根据模型进行练习
# 用Model封装模型
model = paddle.Model(network_1)
# 界说丢失函数
model.prepare(paddle.optimizer.Adam(learning_rate=0.0001, parameters=model.parameters()),
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy())
# 练习可视化VisualDL东西的回调函数
visualdl = paddle.callbacks.VisualDL(log_dir='visualdl_log')
# 发动模型全流程练习
model.fit(train_dataset, # 练习数据集
eval_dataset, # 评价数据集
epochs=50, # 总的练习轮次
batch_size = 256, # 批次核算的样本量巨细
shuffle=True, # 是否打乱样本集
verbose=1, # 日志展现格局
save_dir='./CNN_points/', # 分阶段的练习模型存储途径
callbacks=[visualdl]) # 回调函数运用
# 保存模型
model.save('cnn_model_save_dir')
练习数据可视化
CNN练习信息
经过练习集和测验集能够明显看出loss处于动摇下降的状况,能够看出“学习”是具有成效的,经过ACC逐步上升也能够证明练习的结果是有成效的。可是就练习的数据来看,作用极差,首要原因或许根据以下几个方面:
1、数据种类较多,可是单个样本的数据量并不满足
2、模型较为简略,杂乱的和深度都不够
3、练习轮数,学习率等参数还能够进行修正和调整
在当时结果下能够看出该模型是有必定作用的,可是在当时的参数和数据下作用不明显,还有较大的提高空间。
总结
经过卷积和线性神经网络对图画的分类任务看来,简略的线性作用没有简略的卷积好,并且简略的线性网络提高空间现已没有了,到达极限,只能够经过加深网络等其他的方法进行,在相同轮数的练习下,卷积网络还有较大的提高空间。在必定程度上能够得到定论在对图画分类的项目上简略线性网络作用没有简略的卷积好,并且在50轮的一个练习轮数下,简略线性网络现已到达了学习极值,上升空间小,而简略的卷积网络在不改变网络杂乱的和深度的前提下,还有较大的提高空间,例如:添加练习轮数,修正学习率等参数,还有较大提高空间。
经过本试验必定程度上了解了简略的DNN网络(3层)和CNN网络(变形的LeNet-5网络,修正成了3通道)在学习率为:0.001,丢失函数为穿插熵(cross entropy)(CrossEntropyLoss),评价目标为Accuracy的前提下,对图画进行分类处理(CV分类任务),经过50轮的练习得到了作用,经过作用得出定论,简易CNN在对图画分类任务上的作用好于简易DNN,且在该条件下CNN还有进一步学习和优化的空间。
卷积神经网络在对图画的特征信息提取上具有不行磨灭的优势,且在近些年的各类研究成果中也不断证明卷积的作用是有用的。
作者简介
作者:三岁 阅历:自学python,现在混迹于paddle社区,希望和大家一同从基础走起,一同学习Paddle csdn地址:blog.csdn.net/weixin_4562… 我在AI Studio上取得至尊等级,点亮10个徽章,来互关呀~ aistudio.baidu.com/aistudio/pe…
传说中的飞桨社区最菜代码人,让咱们一同努力! 记住:三岁出品必是精品 (
不要脸系列)