写在前面:大家好!我是【AI 菌】,一枚爱弹吉他的程序员。我
热爱AI、热爱分享、热爱开源
! 这博客是我对学习的一点总结与记录。如果您也对深度学习、机器视觉、算法、Python、C++
感兴趣,可以关注我的动态,我们一起学习,一起进步~
我的博客地址为:【AI 菌】的博客
我的Github项目地址是:【AI 菌】的Github
本教程会持续更新,如果对您有帮助的话,欢迎star收藏~
前言:
本专栏将分享我从零开始搭建神经网络的学习过程,注重理论与实战相结合,力争打造最易上手的小白教程。在这过程中,我将使用谷歌TensorFlow2.0框架逐一复现经典的卷积神经网络:LeNet、AlexNet、VGG系列、GooLeNet、ResNet 系列、DenseNet 系列,以及现在很流行的:RCNN系列、YOLO系列等。
本博客将持续更新,欢迎关注。本着学习的心。希望和大家相互交流,一起进步~
学习记录:
深度学习环境搭建:Anaconda3+tensorflow2.0+PyCharm
TF2.0深度学习实战(一):分类问题之手写数字识别
TF2.0深度学习实战(二):用compile()和fit()快速搭建MINIST分类器
TF2.0深度学习实战(三):LeNet-5搭建MINIST分类器
TF2.0深度学习实战(四):搭建AlexNet卷积神经网络
TF2.0深度学习实战(五):搭建VGG系列卷积神经网络
在 1990 年代,Yann LeCun 等人提出了用于手写数字和机器打印字符识别的神经网络,被命名为 LeNet-5 (Lecun, Bottou, Bengio, & Haffner, 1998)。LeNet-5 的提出,使得卷积神经网络在当时能够成功被商用,广泛应用在邮政编码、支票号码识别等任务中。
论文下载地址:Gradient-Based Learning Applied to Document Recognition
为了方便业界统一测试和评估算法, (Lecun, Bottou, Bengio, & Haffner, 1998)发布了手写数字图片数据集,命名为 MNIST,它包含了 0~9 共 10 种数字的手写图片,每种数字一共有 7000 张图片,采集自不同书写风格的真实手写图片,一共 70000 张图片。其中60000张图片作为训练集,用来训练模型。10000张图片作为测试集,用来训练或者预测。训练集和测试集共同组成了整个 MNIST 数据集。
MINIST数据集中的每张图片,大小为28 × \times × 28,同时只保留灰度信息(即单通道)。
LeNet网络逐层结构:
图片输入:32 × \times × 32 × \times × 1
第一层:卷积核( 6个,5 × \times × 5 × \times × 1 ,步长:1),输出:28 × \times × 28 × \times × 6
最大池化层:卷积核(2 × \times × 2,步长:2),输出:14 × \times × 14 × \times × 6
第二层:卷积核(16个,5 × \times × 5 × \times × 6,步长:1),输出:10 × \times × 10 × \times × 16
最大池化层:卷积核(2 × \times × 2,步长:2),输出:5 × \times × 5 × \times × 16
拉直后输出:5 × \times × 5 × \times × 16 = 400
全连接层1:120个节点
全连接层2:84个节点
输出层:10个节点
注:在进行卷积运算时,没有对输入进行填充。因此第一层的输出size=32-5+1=28,第二层的输出size=14-5+1=10
整个过程可分为以下几步:
(1)数据集准备
(2)搭建网络
(3)模型训练
(1)本实验通过在线读取下载minist数据集,TF2.0实现方式为:
# 加载数据集,返回的是两个元组,分别表示训练集和测试集
(x, y), (x_val, y_val) = datasets.mnist.load_data()
(2)将数据集格式转换为张量,方便进行张量运算,并且将灰度值缩放到0-1,方便训练。
x = tf.convert_to_tensor(x, dtype=tf.float32)/255. # 转换为张量,并缩放到0~1
y = tf.convert_to_tensor(y, dtype=tf.int32) # 转换为张量(标签)
(3)构建数据集对象,设置batch和epos
train_dataset = tf.data.Dataset.from_tensor_slices((x, y)) # 构建数据集对象
train_dataset = train_dataset.batch(32).repeat(10) # 设置批量训练的batch为32,要将训练集重复训练10遍
由于MNIST数据集的图片大小为28 × \times × 28,与原输入32 × \times × 32不一致,因此这里需要对网络结构进行了简单的调整,主要表现在两个方面:
(1)输入变为28 × \times × 28
(2)卷积核size变为3 × \times × 3,个数不变。
使用Keras.Sequential搭建网络:
# 2.搭建网络
network = Sequential([ # 搭建网络容器
layers.Conv2D(6, kernel_size=3, strides=1), # 第一个卷积层,6个3*3*1卷积核
layers.MaxPooling2D(pool_size=2, strides=2), # 池化层,卷积核2*2,步长2
layers.ReLU(), # 激活函数
layers.Conv2D(16, kernel_size=3, strides=1), # 第二个卷积层,16个3*3*6卷积核
layers.MaxPooling2D(pool_size=2, strides=2), # 池化层
layers.ReLU(), # 激活函数
layers.Flatten(), # 拉直,方便全连接层处理
layers.Dense(120, activation='relu'), # 全连接层,120个节点
layers.Dense(84, activation='relu'), # 全连接层,84个节点
layers.Dense(10) # 输出层,10个节点
])
network.build(input_shape=(None, 28, 28, 1)) # 定义输入,batch_size=32,输入图片大小是28*28,通道数为1。
network.summary() # 显示出每层的待优化参数量
该网络一共有60074个参数,具体每层参数表如下:
每层参数计算公式:
卷积层1:6个卷积核,每个size为3 * 3 * 1;卷积运算后,每个通道需额外加上一个偏置b
参数量:3 * 3 * 6 + 6 = 60
卷积层2:16个卷积层,每个size为3 * 3 * 6;卷积运算后,16个通道都要加上一个偏置b
参数量:3 * 3 * 6 * 16 +16 = 880
全连接层参数计算公式:前层节点数 * 后层节点数 + 后层节点数
全连接层1:400 * 120 +120 = 48120
全连接层2:120 * 84 + 84 = 10164
输出层: 84 * 10 + 10 = 850
网络总参数量:60 + 880 + 48120 +10164 + 850 = 60074
(1)选择优化器,迭代算法
# 声明采用批量随机梯度下降方法,学习率=0.01
optimizer = optimizers.SGD(lr=0.01)
(2)搭建accuracy测量器
acc_meter = metrics.Accuracy() # 新建accuracy测量器
(3)搭建梯度记录环境,进行迭代训练,每200轮打印出测试效果
for step, (x, y) in enumerate(train_dataset): # 一次输入batch组数据进行训练
with tf.GradientTape() as tape: # 构建梯度记录环境
x = tf.reshape(x, (32, 28, 28, 1)) # 将输入拉直,[b,28,28]->[b,784]
# x = tf.extand_dims(x, axis=3)
out = network(x) # 输出[b, 10]
y_onehot = tf.one_hot(y, depth=10) # one-hot编码
loss = tf.square(out - y_onehot)
loss = tf.reduce_sum(loss)/32 # 定义均方差损失函数,注意此处的32对应为batch的大小
grads = tape.gradient(loss, network.trainable_variables) # 计算网络中各个参数的梯度
optimizer.apply_gradients(zip(grads, network.trainable_variables)) # 更新网络参数
acc_meter.update_state(tf.argmax(out, axis=1), y) # 比较预测值与标签,并计算精确度(写入数据,进行求精度)
if step % 200 == 0: # 每200个step,打印一次结果
print('Step', step, ': Loss is: ', float(loss), ' Accuracy: ', acc_meter.result().numpy()) # 读取数据
acc_meter.reset_states() # 清零测量器l
"""
author: AI JUN
function: LeNet-5 by python
data: 2020/3/12
"""
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics, losses
# 1.数据集准备
(x, y), (x_val, y_val) = datasets.mnist.load_data() # 加载数据集,返回的是两个元组,分别表示训练集和测试集
x = tf.convert_to_tensor(x, dtype=tf.float32)/255. # 转换为张量,并缩放到0~1
y = tf.convert_to_tensor(y, dtype=tf.int32) # 转换为张量(标签)
print(x.shape, y.shape)
train_dataset = tf.data.Dataset.from_tensor_slices((x, y)) # 构建数据集对象
train_dataset = train_dataset.batch(32).repeat(10) # 设置批量训练的batch为32,要将训练集重复训练10遍
# 2.搭建网络
network = Sequential([ # 搭建网络容器
layers.Conv2D(6, kernel_size=3, strides=1), # 第一个卷积层,6个3*3*1卷积核
layers.MaxPooling2D(pool_size=2, strides=2), # 池化层,卷积核2*2,步长2
layers.ReLU(), # 激活函数
layers.Conv2D(16, kernel_size=3, strides=1), # 第二个卷积层,16个3*3*6卷积核
layers.MaxPooling2D(pool_size=2, strides=2), # 池化层
layers.ReLU(), # 激活函数
layers.Flatten(), # 拉直,方便全连接层处理
layers.Dense(120, activation='relu'), # 全连接层,120个节点
layers.Dense(84, activation='relu'), # 全连接层,84个节点
layers.Dense(10) # 输出层,10个节点
])
network.build(input_shape=(None, 28, 28, 1)) # 定义输入,batch_size=32,输入图片大小是28*28,通道数为1。
network.summary() # 显示出每层的待优化参数量
# 3.模型训练(计算梯度,迭代更新网络参数)
optimizer = optimizers.SGD(lr=0.01) # 声明采用批量随机梯度下降方法,学习率=0.01
acc_meter = metrics.Accuracy() # 新建accuracy测量器
for step, (x, y) in enumerate(train_dataset): # 一次输入batch组数据进行训练
with tf.GradientTape() as tape: # 构建梯度记录环境
x = tf.reshape(x, (32, 28, 28, 1)) # 将输入拉直,[b,28,28]->[b,784]
# x = tf.extand_dims(x, axis=3)
out = network(x) # 输出[b, 10]
y_onehot = tf.one_hot(y, depth=10) # one-hot编码
loss = tf.square(out - y_onehot)
loss = tf.reduce_sum(loss)/32 # 定义均方差损失函数,注意此处的32对应为batch的大小
grads = tape.gradient(loss, network.trainable_variables) # 计算网络中各个参数的梯度
optimizer.apply_gradients(zip(grads, network.trainable_variables)) # 更新网络参数
acc_meter.update_state(tf.argmax(out, axis=1), y) # 比较预测值与标签,并计算精确度(写入数据,进行求精度)
if step % 200 == 0: # 每200个step,打印一次结果
print('Step', step, ': Loss is: ', float(loss), ' Accuracy: ', acc_meter.result().numpy()) # 读取数据
acc_meter.reset_states() # 清零测量器l