今天是深度学习七日打卡营的第二天,学到了第二课,也是时候了解图像识别领域的常规算法,卷积神经网络啦
人工智能有三大主要板块:图像识别、语音处理和推荐系统,而在图像识别领域,分类问题是一个不可避免的问题
经过数据处理,用卷积神经网络搭建相关模型,来达到图像分类的目的
生活中到处可见图像分类用途场景,适用范围广泛,比如二分类任务,分类猫狗图片,多分类任务给多种图片贴上标签,多标签任务给同一图片的不同物种贴上标签等。
如图,计算机看到的图像是一堆矩阵,图像在计算机中是[0,255]区间的象素点,在图像分类任务中,该图像属于‘0’类的概率最大,故将其归为0类。
通常,在图像识别时,因为计算机只能识别像素点的关系,我们会出现语义鸿沟现象,图像的特征提取成为图像识别的一个难点(以往的机器学习为人工提取特征)
与人眼识别图像相类似,卷积神经网络通过不断抽象的方式提取图像特征,通过图像送入,发现边缘和方向,再不断抽象,提取相关特征,最后达到分类目的。
在1998年。LeNet网络架构被提出,从此卷积神经网络被引入科学领域,特别是AlexNet赢得imageNet冠军之后,卷积神经网络越来越被人们所重视,经过若干发展呢,现在在图像识别领域取得了较大成功, 其中GoogleNet又被叫做Inception V3/V4
神经元
其一般公式为:y=w*x+b,其中b为偏置项,通过计算,得出数值,向前传播
神经网络:
许多神经元的运算集合就是神经网络啦
单通道卷积
通过卷积的内积操作,如左上角为 10+21+42+53=25,得到卷积后的第一个元素,而后向右移动步长1,继续卷积操作
多通道卷积:
与单通道卷积不同的是,比如输入的是一个彩色图像,其高度为3维的,故卷积核对应的高度也应该是三维的,分别做内积再相加,得到输出为一维的矩阵
当想要输出几个通道的图像,卷积核就用几个通道的,比如我们是两个通道的卷积核,故结果也输出两个通道的特征图像
目前池化有两种操作,平均池化和最大池化
(使用某一位置的相邻输出的总体统计特征代替网络在该位置的输出,其好处是当输入数据做出少量平移时,经过池化函数后的大多数输出还能保持不变。)
比如:当识别一张图像是否是人脸时,我们需要知道人脸左边有一只眼睛,右边也有一只眼睛,而不需要知道眼睛的精确位置,这时候通过池化某一片区域的像素点来得到总体统计特征会显得很有用。由于池化之后特征图会变得更小,如果后面连接的是全连接层,能有效的减小神经元的个数,节省存储空间并提高计算效率。
即特征选择和信息过滤,减少参数运算量(卷积神经网络的三大特性:局部连接,位置共享和下采样)
Avg Pooling 平均池化与最大池化
平均池化是一个区域像素点的平均值代替整体特征,其好处是提取到了局部的所有特征 | |
---|---|
对邻域内特征点求平均
优缺点:能很好的保留背景,但容易使得图片变模糊
正向传播:邻域内取平均
反向传播:特征值根据领域大小被平均,然后传给每个索引位置
平均池化是一个区域像素点的平均值代替整体特征,其好处是提取到了局部的所有特征
而最大池化是选取局部区域的最大像素值作为整体特征点,其特点是选取最有效的特征 | |
---|---|
优缺点:能很好的保留一些关键的纹理特征,现在更多的再使用Max Pooling而很少用Avg Pooling
正向传播:取邻域内最大,并记住最大值的索引位置,以方便反向传播
反向传播:将特征值填充到正向传播中,值最大的索引位置,其他位置补0
其中W是输入图片宽度,p是padding值,k是卷积核宽度,S是Stride步长
高度计算同理
1):当进行卷积操作时,左角边缘或右角边缘总是只计算一次(少量计算),而中间区域进行多次叠加操作,故角落边缘特征提取少,为提取角落边缘特征,我们需用padding进行填充操作
2):为达到与原始图像一样的大小,我们也可以用padding’操作
Sigmoid函数
Tanh函数
Sigmoid和Tanh激活函数有共同的缺点:即在值很大或很小时,梯度几乎为零,因此使用梯度下降优化算法更新网络很慢。
ReLU函数
Relu目前是选用比较多的激活函数,但是也存在一些缺点,在z小于0时,斜率即导数为0。 为了解决这个问题,后来也提出来了Leaky Relu激活函数,不过目前使用的不是特别多。
当一个复杂的前馈神经网络被训练在小的数据集时,容易造成过拟合。为了防止过拟合,可以通过随机丢弃部分特征节点的方式来减少这个问题发生。
学会了基础知识,那现在就来实践一下吧
import paddle
import numpy as np
import matplotlib.pyplot as plt
paddle.__version__
# 数据预处理
import paddle.vision.transforms as T
# 数据预处理,TODO:找一下提出的原论文看一下
transform = T.Normalize(mean=[127.5], std=[127.5])#归一化数据
# 训练数据集
train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform)
# 验证数据集
eval_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform)
print('训练样本量:{},测试样本量:{}'.format(len(train_dataset), len(eval_dataset)))
print('图片:')
print(type(train_dataset[0][0]))
print(train_dataset[0][0])
print('标签:')
print(type(train_dataset[0][1]))
print(train_dataset[0][1])
# 可视化展示
plt.figure()
plt.imshow(train_dataset[0][0].reshape([28,28]), cmap=plt.cm.binary)
plt.show()
import paddle.nn as nn#导入相关接口
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 全连接层
)
按照LeNet模型搭建就可
模型可视化:
model = paddle.Model(network)#封装已经设置好的网络结构
#模型可视化
model.summary((1, 28, 28))
对比:
paddle.summary(network, (1, 1, 32, 32))
import paddle.nn as nn
network_2 = nn.Sequential(
nn.Conv2D(in_channels=1, out_channels=6, kernel_size=3, stride=1, padding=1),#kernerl为3*3
nn.ReLU(),
nn.MaxPool2D(kernel_size=2, stride=2),
nn.Conv2D(in_channels=6, out_channels=16, kernel_size=5, stride=1, padding=0),
nn.ReLU(),
nn.MaxPool2D(kernel_size=2, stride=2),
nn.Flatten(),
nn.Linear(in_features=400, out_features=120), # 400 = 5x5x16,输入形状为32x32, 输入形状为28x28时调整为256
nn.Linear(in_features=120, out_features=84),
nn.Linear(in_features=84, out_features=10)
)
paddle.summary(network_2, (1, 1, 28, 28))#输入的是28*28,上面的网络模型也做出了一些调整
class LeNet(nn.Layer):
"""
继承paddle.nn.Layer定义网络结构
"""
def __init__(self, num_classes=10):
"""
初始化函数
"""
super(LeNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2D(in_channels=1, out_channels=6, kernel_size=3, stride=1, padding=1), # 第一层卷积
nn.ReLU(), # 激活函数
nn.MaxPool2D(kernel_size=2, stride=2), # 最大池化,下采样
nn.Conv2D(in_channels=6, out_channels=16, kernel_size=5, stride=1, padding=0), # 第二层卷积
nn.ReLU(), # 激活函数
nn.MaxPool2D(kernel_size=2, stride=2) # 最大池化,下采样
)
self.fc = nn.Sequential(
nn.Linear(400, 120), # 全连接
nn.Linear(120, 84), # 全连接
nn.Linear(84, num_classes) # 输出层
)
def forward(self, inputs):
"""
前向计算
"""
y = self.features(inputs)
y = paddle.flatten(y, 1)
out = self.fc(y)
return out
network_3 = LeNet()
network_4 = paddle.vision.models.LeNet(num_classes=10)
搭好模型后就是模型的训练了,与第一天的步骤一样
# 模型封装
model = paddle.Model(network_4)
# 模型配置
model.prepare(paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters()), # 优化器
paddle.nn.CrossEntropyLoss(), # 损失函数
paddle.metric.Accuracy()) # 评估指标
# 启动全流程训练
model.fit(train_dataset, # 训练数据集
eval_dataset, # 评估数据集
epochs=5, # 训练轮次
batch_size=64, # 单次计算数据样本量
verbose=1) # 日志展示形式
模型评估:
result = model.evaluate(eval_dataset, verbose=1)
print(result)
模型预测:
# 进行预测操作
result = model.predict(eval_dataset)
# 定义画图方法
import random
def show_img(img, predict):
plt.figure()
plt.title('predict: {}'.format(predict))
plt.imshow(img.reshape([28, 28]), cmap=plt.cm.binary)
plt.show()
# 抽样展示
indexs=[]
i=10
while i>0:
a=random.randint(0,10000)
indexs.append(a)
i=i-1
print(indexs)
for idx in indexs:
show_img(eval_dataset[idx][0], np.argmax(result[0][idx]))
model.save('finetuning/mnist')
#继续调优训练
from paddle.static import InputSpec
network = paddle.vision.models.LeNet(num_classes=10)
# 模型封装,为了后面保存预测模型,这里传入了inputs参数
model_2 = paddle.Model(network, inputs=[InputSpec(shape=[-1, 1, 28, 28], dtype='float32', name='image')])
# 加载之前保存的阶段训练模型
model_2.load('finetuning/mnist')
# 模型配置
model_2.prepare(paddle.optimizer.Adam(learning_rate=0.0001, parameters=network.parameters()), # 优化器
paddle.nn.CrossEntropyLoss(), # 损失函数
paddle.metric.Accuracy()) # 评估函数
# 模型全流程训练
model_2.fit(train_dataset, # 训练数据集
eval_dataset, # 评估数据集
epochs=2, # 训练轮次
batch_size=64, # 单次计算数据样本量
verbose=1) # 日志展示形式
# 保存用于后续推理部署的模型
model_2.save('infer/mnist', training=False)
今天简单认识了卷积神经网络及初等LeNet模型,图像识别领域中,卷积神经网络是基础模型。