深度神经网络
计算机视觉和图像处理
- Tensorflow入门
- 深度神经网络
- 图像分类
- 目标检测
- 图像分割
- OpenCV
- 图像特征提取与描述
- 视频操作
- 人脸案例
深度神经网络
- 一、神经网络介绍
- 1.1 什么是神经网络
- 1.2 神经元是如何工作的?
- 1.2.1 激活函数(Activation Function)
- 1.2.1.1 Sigmoid函数
- 1.2.1.2 tanh函数
- 1.2.1.3 RELU函数
- 1.2.1.4 LeakReLu函数
- 1.2.1.5 SoftMax函数
- 1.2.1.7 如何选择激活函数
- 1.2.2 参数初始化
- 1.3 神经网络的搭建
- 1.3.1 通过Sequential构建
- 1.3.2 利用function API构建
- 1.3.3 通过Model的子类构建
- 1.4 神经网络的优缺点
- 二、常见损失函数
- 2.1 分类任务
- 2.1.1 多分类任务
- 2.1.2 二分类任务
- 2.2 回归任务
- 2.2.1 MAE损失
- 2.2.2 MSE损失
- 2.2.3 smooth L1损失
- 三、深度学习的优化方法
- 3.1 梯度下降算法
- 3.2 反向传播算法(BP算法)
- 3.3 梯度下降优化方法
- 3.3.1 动量梯度下降算法
- 3.3.2 AdaGrad
- 3.3.3 RMSprop
- 3.3.4 Adam
- 四、深度学习正则化
- 4.1 L1和L2正则化
- 4.2 Droupout正则化
- 4.3 提前停止
- 4.4 批标准化
- 五、神经网络案例
- 六、卷积神经网络CNN
- 6.1 CNN网络的构成
- 6.2 卷积层
- 6.3 池化层
- 6.4 全连接层
- 6.5 卷积神经网络的构建
一、神经网络介绍
1.1 什么是神经网络
人工神经网络( Artificial Neural Network, 简写为ANN)简称为神
经网络(NN),是⼀种模仿生物神经网络结构和功能的计算模型。
人工神经元接收来⾃其他神经元或外部源的输入,每个输入都有⼀个相关的权值(w),它是根据该输入对当前神经元的重要性来确定的,对该输入加权并与其他输入求和后,经过⼀个激活函数f,计算得到该神经元的输出。
神经网络中信息只向⼀个方向移动,即从输入节点向前移动,通过隐藏节
点,再向输出节点移动,网络中没有循环。其中的基本构件是:
- 输⼊层:即输入x的那⼀层
- 隐藏层:输入层和输出层之间都是隐藏层
- 输出层:即输出y的那⼀层
1.2 神经元是如何工作的?
人工神经元接收到⼀个或多个输入,对他们进行加权并相加,总和通过⼀个非线性函数产生输出。
1.2.1 激活函数(Activation Function)
1.2.1.1 Sigmoid函数
数学表达式:
f
(
x
)
=
1
1
+
e
−
x
f(x)={1 \over 1+e^{-x}}
f(x)=1+e−x1
曲线图形如下所示:
sigmoid在定义域内处处可导,且两侧倒数逐渐趋近于0。如何x的值很大或者很小的时候,呢么函数的梯度(函数的斜率)会非常小,在反向传播的过程中,导致了向低层传递的梯度也变得非常小。。这种现象被称为梯度消失。⼀般来说,sigmoid 网络在5层之内就会产生梯度消失现象。而且,该激活函数并不是以0为中心的,所以在实践中这种激活函数使用的很少。sigmoid函数一般只用于二分类的输出层。
实现方法:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# 定义x, y
x = np.linspace(-10,10,100)
y = tf.nn.sigmoid(x)
# 绘图
plt.plot(x,y)
plt.grid()
plt.rcParams['axes.unicode_minus'] = False
plt.show()
1.2.1.2 tanh函数
数学表达式:
t
a
n
h
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
tanh(x)={e^x - e^{-x} \over e^x + e^{-x}}
tanh(x)=ex+e−xex−e−x
曲线图形如下所示:
tanh也是一种非常常见的激活函数。与sigmoid相比,他是以0为中心的,使得其收敛速度要比sigmoid快,减少迭代次数。然而,从图中可以看出,tanh两侧的导数也为0,同样也造成梯度消失。
若使用时可在隐藏层使用tanh函数,在输出层使用sigmoid函数。
实现方法:
x = np.linspace(-10,10,100)
y = tf.nn.tanh(x)
plt.plot(x,y)
plt.grid()
plt.show()
1.2.1.3 RELU函数
数学表达式:
f
(
x
)
=
m
a
x
(
0
,
x
)
f(x)={max(0,x)}
f(x)=max(0,x)
曲线图形如下所示:
ReLU是目前最常用的激活函数。从图中可以看到,当x<0时,relu导数为0,而当x>0时,则不存在饱和问题。所以,ReLU能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。然而,随着训练的推进,部分输入会落入小于0区域,导致对应权重无法更新,这种现象被称为“神经元死亡”。
实现方法:
x = np.linspace(-10,10,100)
y = tf.nn.relu(x)
plt.plot(x,y)
plt.grid()
plt.show()
1.2.1.4 LeakReLu函数
数学表达式:
f
(
x
)
=
m
a
x
(
0.1
x
,
x
)
f(x)={max(0.1x,x)}
f(x)=max(0.1x,x)
曲线图形如下所示:
实现方法:
x = np.linspace(-10,10,100)
y = tf.nn.leaky_relu(x)
plt.plot(x,y)
plt.grid()
plt.show()
1.2.1.5 SoftMax函数
数学表达式:
s
o
f
t
m
a
x
(
z
i
)
=
e
z
i
∑
j
e
z
j
softmax(z_i)={e^{z_i} \over \sum_je^{z_j}}
softmax(zi)=∑jezjezi
使用方法:
softmax用于多分类过程,它是二分类函数sigmoid在多分类上的推广,目的是将多分类的结果以概率的形式展现出来。
softmax直白来说就是将网络输出的logits通过softmax函数,就映射成为(0,1)的值,而这些值得累和为1(满足概率得性质),呢么我们将它理解成概率,选取概率最大(也就是值对应最大的)结点,作为我们得预测目标类别。
实现方法:
x = tf.constant([0.2,0.02,0.15,1.3,0.5,0.06,1.1,0.05,3.75])
y = tf.nn.softmax(x)
y
1.2.1.7 如何选择激活函数
隐藏层:
- 优先选择ReLU激活函数
- 如果ReLU效果不好,呢么尝试其他激活,如LeakyReLU等。
- 一般不使用sigmoid、tanh函数。
输出层:
- 二分类问题选择sigmoid激活函数
- 多分类问题选择softmax激活函数
- 回归问题选择identity激活函数
1.2.2 参数初始化
对于某一个神经元来说,需要初始化的参数有两类:1.权重W,2.偏置b。偏置b初试化为0即可。
-
随机初始化
从均值为0,标准差为1的高斯分布中取样,使用一些很小的值对参数w进行初始化。 -
标准初始化
从区间均匀随机取值。即在( − 1 d , 1 d {-1 \over \sqrt d},{1 \over \sqrt d} d−1,d1)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量 -
Xavier初始化
基本思想:
各层的激活值和梯度的方差在传播过程中保持一致,也叫做Glorot初始化。实现的方法:
- 正态化Xavier初始化:
从以0为中心,标准差为 s t d e v = ( 2 / ( f a n _ i n + f a n _ o u t ) ) stdev = \sqrt (2 / (fan\_in + fan\_out)) stdev=(2/(fan_in+fan_out))的正态分布中抽取样本,其中fan_in是输入神经元的个事,fan_out是输出神经元个数。
实现方法:
# 正态分布 # 实例化 initializern = tf.keras.initializers.glorot_normal() value = initializern((9,1)) value
- 标准化Xavier初始化:
从[-limit,limit]中的均匀分布中抽取样本,其中limit是 6 f a n _ i n + f a n _ o u t \sqrt {6 \over fan\_in + fan\_out} fan_in+fan_out6,其中fan_in是输入神经元的个数,fan_out是输出神经元个数。
实现方法:
# 标准化:均匀分布 initializern = tf.keras.initializers.glorot_uniform() value = initializern((9,1)) value
- 正态化Xavier初始化:
-
He初始化
基本思想:
正向传播时,激活值的方差保持不变;反向传播时,关于状态值的梯度的方差保持不变。实现的方法:
- 正态化的he初始化
He正态分布初始化是以0为中心,标准差为 s t d d e v = 2 f a n _ i n stddev = {\sqrt {2 \over fan\_in}} stddev=fan_in2的截断正态分布中抽取样本,其中fan_in是输入神经元。
#正态分布 initializer = tf.keras.initializers.he_normal() values = initializer((9,1)) values
- 标准化的he初始化
从[-limit,limit]中的均匀分布中抽取样本,其中limit是 6 f a n _ i n \sqrt {6 \over fan\_in } fan_in6,其中fan_in是输入神经元的个数。
# 标准化 initializer = tf.keras.initializers.he_uniform() values = initializer((9,1)) values
- 正态化的he初始化
1.3 神经网络的搭建
tf.keras中构建模型有两种方式,一种是通过Sequential构建,一种是通过Model类构建。Sequential是按一定的顺序对层进行堆叠,而Model用来构建比较复杂的网络模型。
1.3.1 通过Sequential构建
from tensorflow import keras
from tensorflow.keras import layers
# 定义Sequential模型
model = keras.Sequential([
# 第一个隐藏层
layers.Dense(3,activation="relu",kernel_initializer="he_normal",input_shape=(3,)),
# 第二个隐藏层
layers.Dense(2,activation="relu",kernel_initializer="he_normal"),
# 输出层
layers.Dense(2,activation="sigmoid",kernel_initializer="he_normal")
],
name = "my_Sequential"
)
# 展示模型结果
model.summary()
# 展示模型结果
keras.utils.plot_model(model,show_shapes=True)
1.3.2 利用function API构建
# 定义模型的输入
inputs = keras.Input(shape=(3,),name="input")
# 第一层
x = layers.Dense(3,activation="relu",name="layer1")(inputs)
# 第二层
x = layers.Dense(2,activation="relu",name="layer2")(x)
# 第三层(输出层)
outputs = layers.Dense(2,activation="sigmoid",name="layer3")(x)
# 使用Model来创建模型,指明输入和输出
model = keras.Model(inputs=inputs,outputs=outputs,name="my_model")
model.summary()
keras.utils.plot_model(model,show_shapes=True)
1.3.3 通过Model的子类构建
# 定义一个MyModel类,该类继承keras.Model
class MyModel(keras.Model):
# 定义网络的层结构
def __init__(self):
# 调用父类 keras.Model的构造函数,确保父类的构造函数被正确执行
super(MyModel,self).__init__()
# 第一个隐藏层
self.layer1 = layers.Dense(3,activation="relu",name="layer1")
# 第二个隐藏层
self.layer2 = layers.Dense(2,activation="relu",name="layer2")
# 输出层
self.layer3 = layers.Dense(2,activation="sigmoid",name="layer3")
# 定义网络的向前传播
def call(self,inputs):
x = self.layer1(inputs)
x = self.layer2(x)
outputs = self.layer3(x)
return outputs
# 实例化MyModel
model = MyModel()
# 设置输入
x = tf.ones((1,3))
y = model(x)
y
model.summary()
1.4 神经网络的优缺点
- 优点
- 精度高,性能优于其他的机器学习方法
- 可以近似任意的非线性函数
- 有大量的框架和库可供调用
- 缺点
- 黑箱,很难解释模型是怎么工作的
- 训练时间长,需要大量的计算力
- 网络模型复杂,需要调整参数
- 小数据集上表现不佳,容易发生过拟合
二、常见损失函数
2.1 分类任务
在深度学习的分类任务中使用最多的是交叉熵损失函数。
2.1.1 多分类任务
多分类任务通常使用softmax将logits转换为概率的形式,所以多分类的交叉熵损失也叫softmax损失。
在tf.keras中使用Cat实现:
# 设置真实值和预测值
y_true = [[0, 1, 0], [0, 0, 1]]
y_pred = [[0.05, 0.95, 0], [0.1, 0.8, 0.1]]
# 将列表转换为张量
y_true = tf.convert_to_tensor(y_true)
y_pred = tf.convert_to_tensor(y_pred)
# 或者直接创建张量
# y_true= tf.constant([[0, 1, 0], [0, 0, 1]])
# y_pred = tf.constant([[0.05, 0.95, 0], [0.1, 0.8, 0.1]] )
# 实例化交叉熵损失
cce = tf.keras.losses.CategoricalCrossentropy()
# 计算损失结果
cce(y_true,y_pred)
2.1.2 二分类任务
处理二分类任务时,使用sigmoid激活函数,所以损失函数使用二分类的交叉熵损失函数。
# 设置真实值和预测值(张量类型)
y_true = tf.constant([[0],[1]])
y_pred = tf.constant([[0.4],[0.6]])
# 实例化
bca = tf.keras.losses.BinaryCrossentropy()
# 计算损失结果
bca(y_true,y_pred)
2.2 回归任务
2.2.1 MAE损失
Mean absolute loss(MAE)也称为L1 Loss
# 设置真实值和预测值(张量类型)
y_true = tf.constant([[0],[1]])
y_pred = tf.constant([[0.4],[0.6]])
# 实例化
bca = tf.keras.losses.BinaryCrossentropy()
# 计算损失结果
bca(y_true,y_pred)
2.2.2 MSE损失
Mean Squated Loss也被称为L2 loss。
# 设置真实值和预测值
y_true = tf.constant([[0.],[1.]])
y_pred = tf.constant([[1.],[1.]])
# 实例化
mae = tf.keras.losses.MeanAbsoluteError()
# 计算损失结果
mae(y_true,y_pred)
2.2.3 smooth L1损失
通常在目标检测中使用该损失函数。
# 设置真实值和预测值
y_true = tf.constant([[0],[1]])
y_pred = tf.constant([[0.6],[0.4]])
# 实例化
h = tf.keras.losses.Huber()
# 计算损失结果
h(y_true,y_pred)
三、深度学习的优化方法
3.1 梯度下降算法
梯度下降法简单来说就是一种寻找使损失函数最小化的方法。
梯度下降算法 | 定义 | 缺点 | 优点 |
---|---|---|---|
BGD(批量梯度下降) | 每次迭代时需要计算每个样本损失函数的梯度并求和 | 计算量大、迭代速度满 | 全局最优化 |
SGD(随机梯度下降) | 每次迭代时只采集一个样本,计算这个样本损失函数的梯度并更新参数 | 准确度下降、存在噪音、非全局最优化 | 训练速度快、支持在线学习 |
MAGD(小批量梯度下降) | 每次迭代时,随机选取一小部分训练样本来计算梯度并更新参数 | 准确度不如BGD、非全局最优解 | 计算小批量数据得梯度更加高效、支持在线学习 |
实际中使用较多的是小批量的梯度下降算法。
实现过程:
import tensorflow as tf
# 实例化优化方法:SGD
opt = tf.keras.optimizers.SGD(learning_rate=0.1)
# 定义要调整的参数
var = tf.Variable(1.0)
# 使用GradientTape记录损失函数的计算过程
with tf.GradientTape() as tape:
# 定义损失函数
loss = (var ** 2) / 2.0
# 计算梯度
grad = tape.gradient(loss,var)
# 应用梯度到对应的变量上
opt.apply_gradients([(loss,var)])
# 展示参数更新结果
var.numpy()
3.2 反向传播算法(BP算法)
反向传播的核心思想是在前向传播计算输出之后,从输出层开始,沿着网络层往输入方向逐层计算每个权重对损失函数的影响(即梯度)。然后使用这些梯度来更新网络中的权重。
3.3 梯度下降优化方法
3.3.1 动量梯度下降算法
动量梯度下降引入了一个“动量”项,使得梯度更新不仅依赖于当前的梯度,还考虑了之前梯度的方向。这种机制可以帮助加速收敛速度。
在tf.keras中使用Momentum算法仍使用SGD方法,但要设置momentum参数,实现过程:
# 实例化
opt = tf.keras.optimizers.SGD(learning_rate=0.1,momentum=0.9)
# 定义要更新的参数
var = tf.Variable(1.0)
var0 = var.value()
# 进行循环训练
for step in range(2):
with tf.GradientTape() as tape:
# 定义损失函数
loss = (var ** 2)/2.0
# 计算梯度
gradient = tape.gradient(loss,[var])
# 应用梯度到对应的变量上
opt.apply_gradients([(loss,var)])
# 第一次更新更新
if step == 0:
var1 = var.value()
# 第一次更新更新
elif step == 1:
var2 = var.value()
var0, var1, var2
# 第一次更新步长
var0 - var1
# 第二次更新步长
var1 - var2
3.3.2 AdaGrad
AdaGrad 根据每个参数的历史梯度大小动态调整学习率,使得在梯度变化较大的维度上学习率减小,而在梯度变化较小的维度上学习率增大。这有助于解决某些问题中的稀疏性和非均匀性。
# 实例化
# initial_accumulator_value累加器用于存储每个参数的历史梯度的平方和
# epsilon为了避免梯度平方和的倒数为无穷大(即分母为零的情况),会在分母加上一个很小的正值。这也有助于提高数值稳定性。
opt = tf.keras.optimizers.Adagrad(learning_rate=0.1,initial_accumulator_value=0.1,epsilon=1e-06)
# 定义要更新的参数
var = tf.Variable(1.0)
# 使用GradientTape记录损失函数计算过程
with tf.GradientTape() as tape:
loss = (var ** 2) / 2.0
# 计算梯度
gradient = tape.gradient(loss,[var])
# 应用梯度到对应的变量上
opt.apply_gradients([(loss,var)])
# 展示更新结果
var
3.3.3 RMSprop
RMSProp 改进了 AdaGrad 的一些不足之处,通过使用指数加权平均来计算梯度的平方根,从而避免了学习率过快衰减的问题。
# 实例化
opt = tf.keras.optimizers.RMSprop(learning_rate=0.1,rho=0.9)
# 定义要更新的参数
# rho控制了对过去梯度平方的指数衰减平均
var = tf.Variable(1.0)
# 使用GradientTape记录损失函数计算过程
with tf.GradientTape() as tape:
loss = (var ** 2) / 2.0
# 计算梯度
gradient = tape.gradient(loss,[var])
# 应用梯度到对应的变量上
opt.apply_gradients([(loss,var)])
# 展示更新结果
var
3.3.4 Adam
Adam 结合了 Momentum 和 RMSProp 的优点,使用了一阶矩(动量)和二阶矩(RMS)的指数加权平均来适应性地调整学习率。Adam 在实践中非常流行,因为它通常表现出色,并且需要很少的手动调参。
# 实例化
opt = tf.keras.optimizers.Adam(learning_rate=0.1)
# 定义要调整的参数
var = tf.Variable(1.0)
# 使用GradientTape记录损失函数计算过程
with tf.GradientTape() as tape:
loss = (var ** 2) / 2.0
# 计算梯度
gradient = tape.gradient(loss,[var])
# 应用梯度到对应的变量上
opt.apply_gradients([(loss,var)])
# 展示更新结果
var
四、深度学习正则化
在设计机器学习算法时不仅要求在训练集上误小,而且希望在新样本上
的泛化能力强。许多机器学习算法都采用相关的策略来减小测试误差,这
些策略被统称为正则化(缓解网络过拟合的策略)。因为神经网络的强大的表示能力经常遇到过拟合,所以需要使用不同形式的正则化策略。
4.1 L1和L2正则化
L1和L2是最常见的正则化方法。它们在损失函数中增加一个正则项,由于添加了这个正则化项,权重矩阵的值减小,因为它假定具有更小权重矩阵的神经网络导致更简单的模型。因此,它也会在一定程度上减少过拟合。
- L1正则化
tf.keras.regularizers.L1(l1=0.01)
- L2正则化
tf.keras.regularizers.L2(l2=0.01)
- L1L2正则化
tf.keras.regularizers.L1L2(l1=0.0,l2=0.0)
我们直接在某一层的layers中指明正则化类型和超参数即可:
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import regularizers
# 定义Sequential模型
model = keras.Sequential([
# 第一个隐藏层,L1正则化
layers.Dense(3,activation="relu",kernel_initializer="he_normal",kernel_regularizer=regularizers.l1(0.001),input_shape=(3,)),
# 第二个隐藏层,L2正则化
layers.Dense(2,activation="relu",kernel_initializer="he_normal",kernel_regularizer=regularizers.l2(0.001)),
# 输出层,L1L2正则化
layers.Dense(2,activation="sigmoid",kernel_initializer="he_normal",kernel_regularizer=regularizers.L1L2(0.001,0.001))
],
name = "my_Sequential"
)
4.2 Droupout正则化
dropout是在深度学习领域最常用的正则化技术。Dropout 技术通过随机“丢弃”一部分神经元(即设置其权重为零),从而暂时从网络中移除这些神经元及其连接。在每个训练阶段(epoch),随机选择不同比例的神经元进行丢弃。这样做的效果是,在每次迭代过程中,神经网络都会形成不同的“稀疏网络”,这些稀疏网络实际上是相互独立训练的。
import numpy as np
# 定义dropout层,每个神经元有0.2的概率被丢弃
layer = layers.Dropout(0.2,input_shape=(2,))
# 定义输入数据
data = np.arange(1,11).reshape(5,2).astype(np.float32)
# 对输入数据进行失活
outputs = layer(data,training=True)
outputs
4.3 提前停止
提前停止是将一部分训练集作为验证集。当验证集的性能越来越差时或者性能不再提升,则立即停止对该模型的训练。
# 定义回调函数
# 损失值在连续的三个训练周期(patience=3)内没有改善,则训练将提前停止。
callback = keras.callbacks.EarlyStopping(monitor="loss",patience=3)
# 定义一层的网络
model = keras.Sequential([layers.Dense(10)])
# 模型编译
model.compile(keras.optimizers.SGD(),loss="mse")
# 模型训练
# epoch表示模型遍历整个数据集次数
# batch_size 指定每次更新模型参数时使用的样本数量
history = model.fit(np.arange(100).reshape(5,20),np.array([0,1,0,1,0]),epochs=10,callbacks=callback,batch_size=1,verbose=1)
# 打印运行的epochs
len(history.history["loss"])
4.4 批标准化
批标准化(BN层)是针对单个神经元进行,利用网络训练时一个mini-batch的数据来计算该神经元的均值和方差归一化后并重构。因而称为Batch Normalization。
# 直接将其放⼊构建神经⽹络的结构中即可
# center 添加一个可学习的偏移量beta到归一化的激活上
# scale 乘以一个可学习的缩放因子gamma到归一化的激活上
# beta_initializer、gamma_initializer 初始化
tf.keras.layers.BatchNormalization(epsilon=0.001,center=True,scale=True,
beta_initializer="zeros",gamma_initializer="ones")
五、神经网络案例
使用手写数字的MNIST数据集进行神经网络训练,该数据集包含60,000个用于训练的样本和10,000个用于测试的样本,其值为0到255。
# 导入相应的包
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (7,7)
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Dropout,Activation,BatchNormalization
from tensorflow.keras import utils
from tensorflow.keras import regularizers
- 加载数据
# 加载数据集
(x_train,y_train),(x_test,y_test) = mnist.load_data()
x_train.shape,y_train.shape
# 显示数据
for i in range(9):
plt.subplot(3,3,i + 1)
plt.imshow(x_train[i],cmap="gray")
plt.title(y_train[i])
- 数据处理
神经网络中的每个训练样本是一个向量,因此需要对输入进行重塑,使每个28x28的图像成为一个的784维向量。另外,需将输入数据进行归一化处理,从0-255调整到0-1
# 调整数据维度
x_train = x_train.reshape(60000,784)
x_test = x_test.reshape(10000,784)
# 格式转换
x_train = x_train.astype("float32")
x_test = x_test.astype("float32")
# 归一化(将数据范围调整到[0,1]之间)
x_train= x_train/255
x_test = x_test/255
x_train.shape,x_test.shape
# 将目标值转换成热编码
y_train = utils.to_categorical(y_train,10)
y_test = utils.to_categorical(y_test,10)
y_train
- 模型构建
# 使用序列模型进行构建
model = Sequential([
# 全连接层:2个隐层,1个输出层
# 第一个隐层:512个神经元,先激活后BN,随机失活
Dense(512,activation="relu",input_shape=(784,)),
BatchNormalization(),
Dropout(0.2),
# 第二个隐层:512个神经元,先BN后激活,随机失活
Dense(512,kernel_regularizer=regularizers.l2(0.01)),
BatchNormalization(),
Activation("relu"),
Dropout(0.2),
# 输出层
Dense(10,activation="softmax")
])
model.summary()
- 模型编译
# 模型编译,指明损失函数、优化器、评估指标
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
- 模型训练
# batch_size每次送入模型中样本个数,epochs所有样本的迭代次数,并指明验证数据集
history = model.fit(x_train,y_train,batch_size=128,epochs=4,verbose=1,validation_data=(x_test,y_test))
# 绘制损失函数变化曲线
plt.figure(figsize=(14,8))
# 训练集损失函数变化
plt.plot(history.history['loss'],label='train_loss')
# 验证集损失函数变化
plt.plot(history.history['val_loss'],label='val_loss')
plt.legend()
plt.grid()
# 绘制准确率的变化曲线
plt.figure(figsize=(14,8))
# 训练集准确率
plt.plot(history.history["accuracy"], label="train_accuracy")
# 验证集准确率
plt.plot(history.history["val_accuracy"], label="val_accuracy")
plt.legend()
plt.grid()
- 模型测试
# 模型测试
score = model.evaluate(x_test,y_test,verbose=1)
score
- 模型保存
# 保存模型架构与权重在h5⽂件中
model.save('my_model.h5')
# 加载模型
model = tf.keras.models.load_model("my_model.h5")
六、卷积神经网络CNN
6.1 CNN网络的构成
CNN网络主要构成:卷积层、池化层、全连接层构成,其中卷积层负责提取图像特征;池化层回来降维,防止过拟合;全连接层用来输出结果。
6.2 卷积层
卷积运算本质上就是在滤波器和输入数据的局部区域间做点积。
卷积有多通道卷积和多卷积核卷积。
卷积核的实现:
tf.keras.layers.Conv2D(filters,kernel_size,strides=(1,1),padding='valid',activation=None)
# fileters 卷积过滤器的数量,对应输出特征图的通道数
# kernel_size 过滤器filter的大小
# stride 步长
# padding valid在输入周围不进行填充,same在输入周围进行填充
6.3 池化层
- 最大池化,取窗口内的最大值作为输出。
tf.keras.layers.MaxPool2D(pool_size=(2,2),strides=2,padding='valid')
- 平均池化,取窗口内的所有值的均值作为输出
tf.keras.layers.AveragePooling2D(pool_size=(2,2),strides=2,padding='valid')
6.4 全连接层
全连接层位于CNN网络的末端,经过卷积层的特征提取和池化层的降维,将特征图转化为一维向量送入到全连接层中进行分类和回归操作。
6.5 卷积神经网络的构建
# 导包
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Conv2D,MaxPooling2D
- 数据加载
(train_image,train_label),(test_image,test_label) = mnist.load_data()
train_image.shape,train_label.shape
- 数据处理
# 维度调整
train_image = train_image.reshape(60000,28,28,1)
train_image.shape
test_image = test_image.reshape(10000,28,28,1)
- 模型搭建
model = Sequential([
# 卷积层,6个5*5的卷积
layers.Conv2D(filters=6,kernel_size=5,activation="relu",input_shape=(28,28,1)),
# 池化层
layers.MaxPooling2D(pool_size=2,strides=2),
# 卷积层,16个5*5的卷积
layers.Conv2D(filters=16,kernel_size=5,activation="relu"),
# 池化层
layers.MaxPooling2D(pool_size=2,strides=2),
# 将卷积层的输出展平为一维向量
layers.Flatten(),
# 全卷积层
layers.Dense(120,activation="relu"),
layers.Dense(84,activation="relu"),
layers.Dense(10,activation="softmax")
])
model.summary()
- 模型编译
# 优化器
optimizer = tf.keras.optimizers.SGD(learning_rate=0.1)
# 模型编译
model.compile(optimizer=optimizer,loss='sparse_categorical_crossentropy',metrics=["accuracy"])
- 模型训练
model.fit(train_image,train_label,epochs=5,batch_size=128,verbose=1)
- 模型评估
model.evaluate(test_image,test_label,verbose=1)