第一讲:神经网络的计算过程,搭建出你的第一个神经网络模型。
准备数据:采集大量“特征/标签”数据
搭建网络:搭建神经网络结构(前传)
优化参数:训练网络获取最佳参数(反传)
应用网络:将网络封装为模型,输入未曾见过的新数据
输出分类或预测结果(前传)
第二讲:神经网络的优化方法
掌握学习率、激活函数、损失函数和正则化的使用
用Python语言写出SGD、Momentum、Adagrad、RMSProp、Adam五种反向传播优化器。
第三讲:神经网络搭建八股
用“六步法”,写出手写数字识别训练模型。
第四讲,神经网络八股扩展
增加自制数据集、数据增强,断点续训、参数提取和acc/loss可视化,实现给图识物的应用程序。
第五讲:卷积神经网络
用基础CNN、LeNet、AlexNet、VGGNet、InceptionNet和ResNet实现图像识别。
第六讲:循环神经网络
用基础RNN、LSTM、GRU实现股票预测。
人工智能:让机器具备人的思维和意识。
人工智能三学派:
准备数据:采集大量的 “特征/标签”对 数据
搭建网络:搭建神经网络的网络结构
优化参数:通过反向传播,训练网络,直到模型的识别准确率达到要求,获取最佳参数(权重矩阵)
应用网络:将网络保存为模型,输入新数据,通过前向传播,输出分类或预测结果(概率值最大)
以鸢尾花为例
损失函数(loss function)
表征了网络前向传播推理(结果)和标准答案之间的差距。即预测值y与标准答案y_的差距。
损失函数可以定量判断W、b的优劣,当损失函数输出最小时,参数w、b会出现最优值。
均方误差(MSE)就是一种常用的损失函数
梯度下降
目的:想找到一组参数w和b,使得损失函数最小。
梯度:函数对各参数求偏导后的向量。函数梯度下降方向是函数减小方向。
梯度下降法:沿损失函数梯度下降的方向,寻找损失函数的最小值,得到最优参数的方法。
学习率(learning rate,Ir):梯度下降的速度。
当学习率设置的过小时,收敛过程将变得十分缓慢。
当学习率设置的过大时,梯度可能会在最小值附近来回震荡,甚至可能无法收敛。
反向传播:从后向前,逐层求损失函数对每层神经元参数的偏导数,迭代更新,至损失函数最小的最优w值。
wt+1 = wt-lr * (dloss/dwt)
张量(Tensor):多维数组(列表)
张量可以表示0阶到n阶数组(列表)
阶:张量的维数
[[[,就是3阶张量
数据类型
√tf.int,tf.float ..
tf.int 32,tf.float 32,tf.float 64
tf.bool
tf.constant([True,False])
√tf.string
tf.constant("Hello, world!")
创建一个张量
import tensorflow as tf
a = tf.constant([1, 5], dtype=tf.int64) # [[[ 是3阶张量
print("a:", a)
print("a.dtype:", a.dtype)
print("a.shape:", a.shape, #逗号隔开了几个数字,就是几维张量;数字大小值代表有几个元素')
#本机默认 tf.int32 可去掉dtype试一下 查看默认值
将numpy的数据类型转换为Tensor数据类型
import tensorflow as tf
import numpy as np
a = np.arange(0, 5)
b = tf.convert_to_tensor(a, dtype=tf.int64)
print("a:", a)
print("b:", b)
创建一个张量2
import tensorflow as tf
#创建全为0的张量
a = tf.zeros([2, 3])
#创建全为1的张量
b = tf.ones(4) #(维度)
#创建全为指定值的张量
c = tf.fill([2, 2], 9) #(维度,指定值)
print("a:", a)
print("b:", b)
print("c:", c)
√生成正态分布的随机数,默认均值为0,标准差为1
tf.random.normal(维度,mean=均值,stddev=标准差)
√生成截断式正态分布的随机数(更集中)
tf.random.truncated normal(维度,mean=均值,stddev=标准差)
import tensorflow as tf
d = tf.random.normal([2, 2], mean=0.5, stddev=1)
print("d:", d)
e = tf.random.truncated_normal([2, 2], mean=0.5, stddev=1)
print("e:", e)
如何创建一个Tensor
√将numpy的数据类型转换为Tensor数据类型
tf.convert to tensor(数据名,dtype=数据类型(可选))
√创建一个张量
tf.constant(张量内容,dtype=数据类型(可选))
√创建全为0的张量
tf.zeros(维度)
√创建全为1的张量
tf.ones(维度)
√创建全为指定值的张量
tf.fill(维度,指定值)
√生成正态分布的随机数,默认均值为0,标准差为1
tf.random.normal(维度,mean=均值,stddev=标准差)
√生成截断式正态分布的随机数(更集中)
tf.random.truncated normal(维度,mean=均值,stddev=标准差)
√生成均匀分布随机数[minval,maxval)
tf.random.uniform(维度,minval=最小值,maxval=最大值)
5
√强制tensor转换为该数据类型
tf.cast(张量名,dtype=数据类型)
√计算张量维度上元素的最小值
tf.reduce_min(张量名)
√计算张量维度上元素的最大值
tf.reduce_max(张量名)
√计算张量沿着指定维度的平均值
tf.reduce_mean(张量名,axis=操作轴)
√计算张量沿着指定维度的和
tf.reduce_sum(张量名,axis=操作轴)
√tf.Variable()将变量标记为“可训练”,被标记的变量会在反向传播中记录梯度信息。神经网络训练中,常用该函数标记待训练参数
√数学运算:
tf.add加; tf.subtract减;tf.multiply乘;tf.divide除;
tf.square平方;tf.pow次方; tf.sqrt开方; tf.matmul矩阵乘
√tf.add(张量1,张量2)
只有维度相同的张量才可以做四则运算
√tf.data.Dataset.from_tensor_slices (特征,标签)配对
p33
切分传入张量的第一维度,生成输入特征/标签对,构建数据集
Numpy和Tensor格式都可用该语句读入数据
√tf.GradientTape: 实现某个函数对指定参数的求导运算
p34
with结构记录计算过程,gradient求出张量的梯度
√enumerate:枚举。是python的内建函数
p35
可遍历每个元素(如列表、元组或字符串),{组合为:索引 元素],常在for循环中使用
√tf.one_hot,独热编码(one-hot encoding)
在分类问题中,常用独热码做标签,
标记类别:1表示是,0表示非
tf.one_hot()函数将待转换数据(必须是整数),转换为one-hot形式的数据输出。
tf.one_hot(待转换数据, depth=几分类)
tf.nn.softmax()
使输出符合概率分布,且概率和为1。
assign_sub
赋值操作,更新参数的值并返回。自减操作!
调用assign_sub前,先用tf.Variable定义变量w为可训练(可自更新)
tf.argmax
返回张量沿指定维度最大值的索引
tf.argmax(张量名,axis=操作轴)
数据集读入
数据集乱序
生成训练集和测试集(即×train /y train,x_test/y_test)
配成(输入特征,标签)对,每次读入一小撮(batch)
定义神经网路中所有可训练参数
嵌套循环迭代,with结构更新参数,显示当前loss
计算当前参数前向传播后的准确率,显示当前acc
√acc / loss可视化
本讲目标:学会神经网络优化过程,使用正则化减少过拟合,使用优化器更新网络参数
·预备知识
·神经网络复杂度
·指数衰减学习率
·激活函数
·损失函数
·欠拟合与过拟合
·正则化减少过拟合
·优化器更新网络参数
tf.where(条件语句,真返回A,假返回B)
条件语句真返回A,条件语句假返回B
np.random.RandomState.rand(维度)
返回一个[0,1)之间的随机数
维度为空,返回标量
np.vstack;np.hstack
垂直方向vstack连接
沿水平方向hstack连接
a,b = np.mgrid[起始值a:结束值a:步长a,起始值b:结束值b:步长b]
a、b矩阵的维度都是(a_num, b_num)
即a、b两个数组都是a行b列的
np.ravel(数组) or 数组.ravel()
将数组拉直成一维数组
np.c_[数组a,数组b,...]
使返回的间隔数值点配对,得到len(a)*len(b)对
(1)神经网络(NN)复杂度
NN复杂度:多用NN层数和NN参数的个数表示
*空间复杂度:
层数=隐藏层的层数+ 1个输出层
左图为2层NN
总参数=总w+总b
左图3x4+4 + 4x2+2 = 26
第1层 第2层
*时间复杂度:
乘加运算次数
左图3x4 + 4x2 = 20
第1层 第2层
图为输入层3,隐藏层4,输出层2
(2)学习率
Wt+1 = Wt - lr * d·loss/d·Wt
更新后的参数 当前参数 学习率 损失函数的梯度(偏导数)
eg: 损失函数loss = (W+ 1)^2
d·loss / d·w= 2w+2
【指数衰减学习率】
可以先用较大的学习率,快速得到较优解,然后逐步减小学习率,使模型在训练后期稳定。
指数衰减学习率 = 初始学习率 * 学习率衰减率^(当前轮数/多少轮衰减一次)
lr = LR_BASE * LR_DECAY ^ (epoch / LR_STEP)
循环迭代总次数:epoch=40
3个超参数:LR_BASE(0.2);LR_DECAY(0.99);LR_STEP(1)
https://blog.csdn.net/tyhj_sf/article/details/79932893 #常用激活函数(激励函数)理解与总结
如果不用激励函数(其实相当于激励函数是f(x) = x),在这种情况下你每一层节点的输入都是上层输出的
线性函数,很容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种
情况就是最原始的感知机(Perceptron)了,那么网络的逼近能力就相当有限。正因为上面的原因,我们决定
引入非线性函数作为激励函数,这样深层神经网络表达能力就更加强大(不再是输入的线性组合,而是几乎
可以逼近任意函数)。
√优秀的激活函数:
·非线性:激活函数非线性时,多层神经网络可逼近所有函数
·可微性:优化器大多用梯度下降更新参数
·单调性:当激活函数是单调的,能保证单层网络的损失函数是凸函数
·近似恒等性:f(x)x当参数初始化为随机小值时,神经网络更稳定
√激活函数输出值的范围:
·激活函数输出为有限值时,基于梯度的优化方法更稳定
·激活函数输出为无限值时,建议调小学习率
Sigmoid函数
f(z) = 1/(1+e**(-z)), 取值范围[0, 1]
目前很少用这个激活函数,因为在深度神经网络中更新参数时需要从输出层到输入层逐层进行链式求导,
而sigmoid函数导数值为0-0.25之间,链式求导需要多层导数连续相乘,会出现多个0-0.25之间的连续
相乘,结果趋于零,产生梯度消失,无法进行参数更新
特点
(1)易造成梯度消失
(2)输出非0均值,收敛慢
(3)幂运算复杂,训练时间长
Tanh函数
f(z) = (e**z - e**(-z))/(e**z+e**(-z)),取值范围[-1, 1]
特点
(1)输出是0均值
(2)易造成梯度消失
(3)幂运算复杂,训练时间长
Relu函数
Relu = max(0,x), 使用中应避免负数特征进入RELU函数,否则会导致神经元死亡
优点:
(1)解决了梯度消失问题(在正区间)
(2)只需判断输入是否大于0,计算速度快
(3)收敛速度远快于sigmoid和tanh
缺点:
(1)输出非0均值,收敛慢
(2)Dead RelU问题:某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。
Leaky Relu函数
f(x)=max(αx,x), 引入α避免神经元死亡
理论上来讲,Leaky Relu有Relu的所有优点,外加不会有Dead Relu问题,但是在实际操作当中,并没有完全
证明Leaky Relu总是好于Relu。
ELU (Exponential Linear Units) 函数
ELU也是为解决ReLU存在的问题而提出,显然,ELU有ReLU的基本所有优点,以及:(1)不会有Dead ReLU问题;
(2)输出的均值接近0,zero-centered。它的一个小问题在于计算量稍大。类似于Leaky ReLU,理论上虽然好于
ReLU,但在实际使用中目前并没有好的证据ELU总是优于ReLU。
应用中如何选择合适的激活函数?
这个问题目前没有确定的方法,凭一些经验吧。
1)深度学习需要大量时间处理数据,模型收敛速度尤为重要。所以总体讲,训练深度学习网络尽量使用
zero-centered数据 (可以经过数据预处理实现) 和zero-centered输出。要尽量选择输出具有zero-centered
特点的激活函数以加快模型的收敛速度。
2)如果使用 ReLU,那么一定要小心设置 learning rate,而且要注意不要让网络出现很多 “dead” 神经元,
如果这个问题不好解决,那么可以试试 Leaky ReLU、PReLU 或者 Maxout.
3)最好不要用 sigmoid,你可以试试 tanh,不过可以预期它的效果会比不上 ReLU 和 Maxout.
★对于初学者的建议:
首选relu激活函数;
学习率设置较小值;
输入特征标准化,即让输入特征满足以0为均值,1为标准差的正态分布;
初始参数中心化,即让随机生成的参数满足以0为均值,以square(2/当前层输入特征个数)为标准差的正态分布。
损失函数loss,衡量模型预测好坏,是前向传播计算出的预测结果y与已知标准答案y_的差距。
优化目标:loss最小
(1)均方误差mse. (Mean Squared Error), 即误差的平方再求平均
MSE(y_, y) = ∑(y - y_)^2 / n
loss_mse = tf.reduce_mean(tf.square(y_ - y))
举例:p19_mse.py
预测酸奶日销量y,x1、x2是影响日销量的因素。
建模前,应预先采集的数据有:每日x1、 x2和每日销量y_ (即已知答案,最佳情况:产量=销量)
拟造数据集X, Y_ : y_ = x1 + x2
噪声: -0.05 ~ +0.05
拟合可以预测销量的函数
(2)自定义损失函数
此例,MSE作为Loss函数,默认认为销量预测的多了或者少了,损失是一样的。
然而,实际上,销量预测多了,损失成本;预测少了,损失利润。
若利润≠成本,则mse产生的loss无法利益最大化。
因此,使用自定义损失函数举例p20_custom.py
自定义损失函数loss(y_, y) = ∑f(y_, y)
y_:标准答案数据集的
y:预测答案计算出的
f(y_, y) = PROFIT* (y_ - y), y < y_, 预测的y少了,损失利润(PROFIT)
= COST * (y - y_), y >=y_, 预测的y多了,损失成本(COST)
loss_ zdy= tf.reduce.sum(tf.where(tf.greater(y,y_), COST(y - y_), PROFIT(y_ - y))
y>y_ ?进行真假判别
如:预测酸奶销量, 酸奶成本(COST) 1元,酸奶利润(PROFIT) 99元。
预测少了损失利润99元,大于预测多了损失成本1元。
预测少了损失大,希望生成的预测函数往多了预测。
(3)CE (Cross Entropy), 交叉熵损失函数
*交叉熵能够衡量同一个随机变量中的两个不同概率分布的差异程度,在机器学习中就表示为真实
概率分布与预测概率分布之间的差异。
*交叉熵在分类问题中常常与softmax是标配,softmax将输出的结果进行处理,使其多个分类的预测值和为1,
再通过交叉熵来计算损失。
https://blog.csdn.net/b1055077005/article/details/100152102
*表征两个概率分布之间的距离:熵越大,距离越远;越小越近。交叉熵的值越小,模型预测效果就越好。
H(y_, y) = -∑ y_ * lny
eg.二分类己知答案y_=(1,0),预测y1=(0.6,0.4) y2=(0.8,0.2),哪个更接近标准答案?
H1((1,0),(0.6,0.4)) = -(1*ln0.6+O*ln0.4) ≈ (-0.511+0) = 0.511
H2((1,0).(0.8,0.2)) = -(1*ln0.8+O*ln0.2) ≈ (-0.223+0) = 0.223
因为H1>H2,所以y2预测更准。p22_ce.py
tf.losses.categorical_crossentropy(y_, y)
ce作为损失函数
tf.nn.softmax_cross_entropy_with_logits(y_, y)
先过softmax,再算ce
是softmax与交叉熵损失函数的结合
【欠拟合与过拟合】
欠拟合的解决方法:
增加输入特征项
增加网络参数
减少正则化参数
过拟合的解决方法:
数据清洗
增大训练集
采用正则化
增大正则化参数
*正则化缓解过拟合
正则化在损失函数中引入模型复杂度指标,利用给W加权值,弱化了训练数据的噪声(一般不正则化b)
loss = loss( y与y_ ) + REGULARIZER * loss(w)
loss( y与y_ ):模型中所有参数的损失函数。如:交叉熵、均方误差
REGULARIZER:用超参数REGULARIZER给出参数w在总loss中的比例,即正则化的权重
w:需要正则化的参数
正则化的选择
lossL1(w) = ∑|w;|
L1正则化大概率会使很多参数变为零,因此该方法可通过稀疏参数,即减少参数的数量,降低复杂度。
lossl2(w)= ∑|wi**2|
L2正则化会使参数很接近零但不为零,因此该方法可通过减小参数值的大小降低复杂度。
优化器就是引导神经网络更新参数的工具。
(1)待优化参数w,损失函数loss, 学习率lr, 每次迭代一个batch(每个batch通常包含2^n组数据),t表示当前batch迭代的总次数。
(2)步骤:
1.计算t时刻损失函数关于当前参数的梯度gt=▽loss= d·loss/d(wt)
2.计算时刻一阶动量mt和二阶动量Vt。(不同的优化器实质上只是定义了不同的一阶动量和二阶动量公式)
一阶动量:与梯度相关的函数
二阶动量:与梯度平方相关的函数
3.计算t时刻下降梯度: nt= lr.mt/square(Vt)
4.计算t+1时刻参数: Wt+1 =wt-nt=Wt- lr. mt/square(Vt)
5种常用优化器Optimizers:
https://keras.io/zh/optimizers/ #Keras集成了不止5种。
(1)SGD
不含动量momentum
#class2_p32.py 比 class1/p45.py只增加了程序运行的计时功能,全部相同
(2)SGDM
含momentum的SGD
#class2/p34_sgdm.py
(3)Adagrad
在SGD基础上增加二阶动量
二阶动量是从开始到现在梯度平方的累计和:Vt = ∑gt^2
#class2/p36_adagrad.py
可以理解成梯度更新的时候是有惯性的,动量就表示了前面时刻的梯度对当前时刻梯度的影响
(4)RMSProp
在SGD基础上增加二阶动量
二阶动量v使用指数滑动平均值计算:Vt=β·Vt-1+(1-β)·gt^2
#class2/p38_rmsprop.py
(5)Adam
同时结合SGDM一阶动量和RMSProp二阶动量,并增加了2个修正项
#class2/p40_adam.py
本讲目标
使用八股搭建神经网络
●神经网络搭建八股
●iris代码复现
●MNIST数据集
●Fashion数据集
六步法:
(1)import
(2)train, test
告知要喂入网络的训练集和测试集是什么:
指定训练集的输入特征x_train和训练集的标签y_ train
指定测试集的输入特征x_test和测试集的标签y_test
(3)model = tf.keras.models. Sequential
在Sequential()中搭建网络结构,逐层描述每层网络,相当于走了一遍前向传播
(4)model.compile
在compile()中配置训练方法,告知训练时选择哪种优化器,哪个损失函数,哪种评测指标
(5)model.fit
在fit()中执行训练过程,告知训练集和测试集的输入特征和标签,每个batch是多少,要迭代多少次数据集
(6)model.summary
用summary()打印出网络的结构和参数统计
可以认为是个容器,这个容器里封装了一个神经网络结构。
在Sequential()中,要描述从输入层到输出层每一层的网络结构,每一层的网络结构可以是拉直层、全连接层。
model = tf.keras .models Sequential ([网络结构]) #描述各层网络
网络结构举例:
拉直层: tf.keras.layers.Flatten( )
这一层不含计算,只是形状转换,把输入特征拉直变成一维数组
全连接层: tf.keras.layers.Dense(神经元个数,activation='哪种激活函数', kernel_regularizer=哪种正则化)
activation (字符串给出)可选: relu、softmax、 sigmoid 、tanh
kernel_regularizer 可选: tf.keras.regularizers.l1()、tf.keras.regularizers.12()
卷积层: tf.keras.layers.Conv2D(filters =卷积核个数,kernel size=卷积核尺寸
, strides=卷积步长, padding =”valid" or "same")
见class5
LSTM层: tf.keras.layers.LSTM()
见class6
model.compile(optimizer =优化器,loss =损失函数,metrics= [“准确率/评测指标"] )
Optimizer可选:
傻瓜式 or 函数式(可设定超参数)
'sgd' or tf.keras.optimizers.SGD(lr=学习率,momentum=动量参数)
'adagrad' or tf.keras.optimizers.Adagrad (lr=学习率)
'adadelta' or tf.keras.optimizers.Adadelta (lr=学习率)
'adam' or tf.keras.optimizers.Adam(lr=学习率,beta_ 1=0.9, beta_ 2=0.999)
loss可选:
'mse' or tf.keras.losses.MeanSquaredError()
'sparse_categorical_crossentropy or ★tf.keras.losses.SparseCategoricalCrossentropy(from_logits= False)
★有些神经网络的输出是经过了softmax等函数的概率分布,有些则不经概率分布直接输出。
from_logits参数是询问是否是原始输出,也就是没有经概率分布的输出。如果神经网络预测结果输出前经过了概率分布False,否则为True。
Metrics可选:
'accuracy' : y_和y都是以数值形式给出,如y_=[1] y=[1]
'categorical accuracy : y_ 和y都是以独热码(或概率分布)形式给出
如y_=[0,1,0]是独热码, y=[0.256,0.695,0.048]是概率分布
'★sparse_categorical_accuracy : y_是数值,y是独热码(概率分布),如鸢尾花分类y_=[1] y=[0.256,0.695,0.048]
★后续的例子都是以数值形式给出标签y_,以概率分布形式表示输出y,所以选择sparse_categorical_accuracy!
model.fit (训练集的输入特征,训练集的标签,
batch_size=每次喂入神经网络多少组数据(样本数), epochs=数据集迭代循环多少次,
validation_data=(测试集的输入特征,测试集的标签) #2选1,需要在前面先给出测试集
validation_split= 从训练集划分多少比例给测试集, #2选1,不需要,直接分个比例数字即可
validation_freq =每多少次epoch迭代使用测试集验证一次准确率等评测结果)
下例class3/p8_iris_sequential.py
import tensorflow as tf
from sklearn import datasets
import numpy as np
x_train = datasets.load_iris().data
y_train = datasets.load_iris().target
'''测试集的输入特征x_test和测试集的标签y_test,可以像×_train和y_train一样直接在这里给定;
也可以在fit中按比例从训练集中划分validation_split, 此例为按比例给出'''
np.random.seed(116)
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())
])
#由于神经网络末端使用了softmax函数,使得输出是概率分布而不是原始输出,所以from_logits=False
model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
#由于鸢尾花数据集给的标签是数值0 1 2,神经网络前向传播的输出y是概率分布所以选sparse_categorical_accuracy
metrics=['sparse_categorical_accuracy'])
model.fit(x_train, y_train, batch_size=32, epochs=500, validation_split=0.2, validation_freq=20)
model.summary()
*用Sequential可以搭建出上层输出就是下层输入的顺序网络结构,但是无法写出一些带有跳连的非顺序网络结构,此时可选用
类class搭建神经网络结构。
import tensorflow as tf
from tensorflow.keras.layers import Dense #new
from tensorflow.keras import Model #new
from sklearn import datasets
import numpy as np
x_train = datasets.load_iris().data
y_train = datasets.load_iris().target
np.random.seed(116)
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)
class IrisModel(Model): #这里的Model表示继承了TensorFlow的Model类
#__init__()定义了具有3个神经元的全连接网络结构块
def __init__(self):
super(IrisModel, self).__init__()
#定义网络结构块,d1是给这一层起的名字
self.d1 = Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())
#call()调用__init__中搭建好的积木self.d1,实现前向传播
def call(self, x):
y = self.d1(x)
return y
#实例化
model = IrisModel()
model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
model.fit(x_train, y_train, batch_size=32, epochs=500, validation_split=0.2, validation_freq=20)
model.summary()
提供6万张28*28像素点的0~9手写数字图片和标签,用于训练。
提供1万张28*28像素点的0~9手写数字图片和标签,用于测试。
p13_mnist_datasets.py
p14_mnist_sequential.py #用Sequential实现手写数字识别训练
p15_mnist_class.py #用类实现手写数字识别模型训练
提供6万张28*28像素点的衣裤等图片和标签,用于训练。
提供1万张28*28像素点的衣裤等图片和标签,用于测试。
p16_fashion_class.py
p16_fashion_sequential.py
本讲目标:神经网络八股功能扩展
(1)自制数据集,解决本领域应用
(2)数据增强,扩充数据集
(3)断点续训,存取模型
(4)参数提取,把参数存入文本
(5)acc/loss可视化,查看训练效果
(6)应用程序,给图识物
基于class3_p13/14的手写数字识别训练八股代码,进行扩展
*在class4 MNIST_FC\mnist_image_label文件夹中存放了两个文件夹和两个txt文本mnist_train_jpg_60000、mnist_test_jpg_10000。
0_5.jpg就是第一张图片的图片名,空格后的5是这张图片所对应的标签。要自己写个函数把load_data替换掉
在上一讲 p13_mnist_datasets.py代码中,看到了x_train、y_train、 x_test、y_test的数据类型和形状。
x_train.shape:(60000,28,28)三维数组;y_train.shape:(60000,)一维数组
x_test.shape: (10000,28,28)三维数组;y_test.shape: (10000,)一维数组
例子见class4/p8_mnist_train_ex1.py
数据增强可以帮助扩展数据集。对图像的增强,就是对图像的简单形变,用来应对因拍照角度不同引起的图片变形。
数据增强(增大数据量)
image_gen_train = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=所有数据将乘以该数值
rotation_range=随机旋转角度数范围
width_shift_range=随机宽度偏移量
height_shift range=随机高度偏移量
水平翻转:horizontal_flip=是否随机水平翻转
随机缩放:zoom_range=随机缩放的范围[1-n,1+n])
image_gen_train.fit(x_train)
fit()需要输入一个4维数据,需要对x_train进行reshape,如下:
x_train = x_train.reshape(x_train.shape[0],28,28,1)
(60000,28,28) → (60008,28,28,1) #最后一个1,是增加的一个维度,是RGB的通道,因为是灰度值所以一维
model.fit(x_train, y_train,batch_size=32,......)
↓
model.fit(image_gen_train.flow(x_train, y_train, batch_size=32),...)
★数据增强,在小数据量上,可以增强模型泛化性。
源码:p11_show_augmented_images.py
p13_mnist_train_ex2.py
在进行神经网络训练过程中由于一些因素导致训练无法进行,需要保存当前的训练结果下次接着训练
读取保存模型
读取模型:
load_weights(路径文件名)
保存模型:
tf.keras.callbacks.ModelCheckpoint(
filepath=路径文件名,
save_weights_only = True / False, 是否只保留模型参数
save_best_only=True/False 是否只保留最优结果
)
history = model.fit ( callbacks=[cp_callback] ) 加入callbacks选项,记录到history。
源码:p16_mnist_train_ex3.py
import tensorflow as tf
import os
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
#读取模型:先定义出存放模型的路径和文件名,命名为ckpt文件,因为生成ckpt文件会同步生成索引表,
#所以通过判断是否已有索引表,就知道是不是已经保存过模型参数了,然后调用load_weights
checkpoint_save_path = "./checkpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)
#保存模型
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
model.summary()
提取可训练参数
model.trainable_variables 返回模型中可训练的参数
设置print输出格式
np.set_printoptions(threshold=超过多少省略显示)
np.set_printoptions(threshold=np.inf) # ★np.inf表示不使用省略号,所有内容都打印
print(model.trainable_variables)
存入weights.txt文本文件
file = open('./weights.txt','w')
for v in model.trainable_variables:
file.write(str(v.name) +'ln')
file.write(str(v.shape) +'ln')
file.write(str(v.numpy())+ '\n')
file.close()
多次运行本段代码,可以实现断点续寻,并把最新最有参数存入weights.txt。
对于计算能力较弱的CPU,可以减少训练次数,多次运行,实现续训和最优参数保存
源码:p19_mnist_train_ex4.py
#由于断点续训很有用,这页的代码是在断点续训的基础上,添加了参数提取功能
import tensorflow as tf
import os
import numpy as np #新增
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
checkpoint_save_path = "./checkpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
model.summary()
np.set_printoptions(threshold=np.inf) #新增
print(model.trainable_variables) #打印模型中可训练的参数
#存入weights.txt文本文件
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
file.write(str(v.name) + '\n')
file.write(str(v.shape) + '\n')
file.write(str(v.numpy()) + '\n')
file.close()
history=model.fit(训练集数据,训练集标签, batch_size=, epochs=,
validation_split=用作测试数据的比例, validation_data=测试集,
validation_freq=测试频率)
history:
训练集loss: loss
测试集loss: val_loss
训练集准确率: sparse_categorical_accuracy
测试集准确率: val_sparse_categorical_accuracy
用history.history提取结果
源码:p23_mnist_train_ex5.py
import tensorflow as tf
import os
import numpy as np
from matplotlib import pyplot as plt
np.set_printoptions(threshold=np.inf)
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
checkpoint_save_path = "./checkpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
history = model.fit(x_train, y_train, batch_size=32, epochs=50, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
model.summary()
print(model.trainable_variables)
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
file.write(str(v.name) + '\n')
file.write(str(v.shape) + '\n')
file.write(str(v.numpy()) + '\n')
file.close()
############################################### show ###############################################
# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
前向传播执行应用
predict(输入特征,batch_size=整数)
返回前向传播计算结果
实现前向传播执行识图应用仅需三步:
(1)复现模型(前向传播)
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten().
tf.keras.layers.Dense(128,activation='relu').
tf.keras.layers.Dense(10,activation= 'softmax’)])
(2)加载参数
model.load_weights(model_save_path)
(3)预测结果
result = model.predict(x_predict)
由于神经网络训练时都是按照batch送入网络的,
img_arr:(28,28)
x__predict:(1,28,28)
所以进入predict函数前先要把img_arr前面添加一个维度
x_predict = img_arr[tf.newaxis, ...]
送入预测,并把最大的概率值输出
result = model.predict(x_predict)
pred = tf.argmax(result, axis=1)
p27_mnist_app.py #预处理为灰度图片
p28_mnist_app.py #预处理为黑白高对比度图片
全连接NN:
每个神经元与前后相邻层的每一个神经元都有连接关系,输入是特征,输出为预测的结果。
参数个数: ∑( 前层 × 后层 + 后层)
各层 w b
【本讲目标】:用CNN实现离散数据的分类(以图像分类为例)
5.1 卷积计算过程
5.2 感受野
5.3 全零填充(Padding)
5.4 TF描述卷积计算层
5.5 批标准化(Batch Normalization, BN)
5.6 池化(Pooling
5.7 舍弃(Dropout)
5.8 卷积神经网络
5.9 cifar10数据集
5.10 卷积神经网络搭建示例
实现LeNet、AlexNet、VGGNet、InceptionNet、ResNet五个经典卷积网络
上一讲中输入全连接网络的是—幅28行28列的784个像素点的灰度值,仅两层全连接网络就有十万多个待训练参数。
*实际项目中,图片多是高分辨率彩色图,相对于灰度图单通道,彩色图有红绿蓝三通道,它送入全连接网络的输入特征数过多,随着隐藏层
层数的增加,网络规模过大,待优化的参数过多,容易导致模型过拟合。
*实际应用时,会先对原始图像进行若干层的特征提取,再把提取到的特征送给全连接网络。
卷积Convolutional计算可认为是一种有效提取图像特征的方法。
*一般会用一个正方形的卷积核,按指定步长,在输入特征图上滑动,遍历输入特征图中的每个像素点。每一个步长,卷积核会与输入特征图
部分像素重合,重合区域对应元素相乘、求和再加上偏置项,得到输出特征的一个像素点。
*如果输入特征是单通道灰度图,使用深度为1的单通道卷积核;如果输入特征是三通道彩色图,使用3*3*3或5*5*3的卷积核。总之要使
卷积核的通道数与输入特征图的通道数一致。因为要想让卷积核与输入特征图对应点匹配上,必须让卷积核的深度与输特征图的深度一致。
**所以,输入特征图的深度(channel数),决定了当前层卷积核的深度;
由于每个卷积核在卷积计算后,会得到一张输出特征图。所以,当前层使用了几个卷积核,就有几张输出特征图。
当前层卷积核的个数,决定了当前层输出特征图的深度。
如果你觉得某层模型的特征提取能力不足,可以在这一层多用几个卷积核,提高这一层的特征提取能力。
*卷积就是利用立体卷积核实现了参数的空间共享。
感受野(Receptive Field):卷积神经网络各输出特征图中的每个像素点,在原始输入图片上映射区域的大小。
同样一个5*5的原始输入图片,经过两层3*3的卷积核作用,和经过一层5*5的卷积核作用,都得到一个感受野是5的1*1输出特征(图),
所以这两种特征提取能力是一样的,具体选择需要考虑他们所承载的待训练参数量和计算量。
当输入特征图高宽为x,大于10时,两层3*3卷积核优于一层5*5卷积核
卷积输出特征图维度的计算公式:
(1)当SAME(使用全0填充), 入长/步长, (向上取整)
(2)当VALID(不使用全0填充), (入长一核长+1)/步长,(向上取整)
TF描述全零填充,用以下参数表示
padding = 'SAME’ ,(使用全0填充)
padding = 'VALID' ,(不使用全0填充)
tf.keras.layers.Conv2D( filters=卷积核个数, #filters一般比kernel_size大1
kernel_size=卷积核尺寸, #正方形写核长整数,或元组形式(核高h,核宽w)
strides=滑动步长, #横纵向相同写步长整数,或(纵向步长h,横向步长w),默认1
padding= “same" or "valid", #使用全零填充是"same",不使用是"valid”(默认不使用)
activation=" relu " or " sigmoid " or " tanh "or " softmax"等,#如卷积后有BN操作,此处不写激活
input_shape=(高,宽,通道数) #输入特征图维度,可省略
)
model = tf.keras.models.Sequential([
#形式1:
Conv2D(6,5, padding='valid', activation='sigmoid'),
MaxPoo12D(2,2),
#形式2:
Conv2D(6,(5,5),padding='valid', activation='sigmoid '),
MaxPool2D(2,(2,2)),
#★形式3:代码可读性强
Conv2D(filters=6, kernel_size=(5,5), padding='valid', activation='sigmoid
MaxPool2D(pool_size=(2,2),strides=2),
Flatten(),
Dense(10,activation='softmax ')
])
神经网络对0附近的数据更敏感,但是随着网络层数的增加,特征数据会出现偏离0均值的情况。由标准化解决。
标准化:使数据符合0为均值,1为标准差的分布。
**批标准化:对一小批数据(batch),做标准化处理。常用在卷积操作和激活操作之间。
批标准化操作,会让每个像素点进行减均值除以标准差的自更新计算。
*BN操作,将原本偏移的特征数据重新拉回到0均值,使进入激活函数的数据分布在激活函数线性区,使得输入数据的微小变化,
更明显的体现到激活函数的输出。提升了激活函数对输入数据的区分力。但是这种简单的特征数据标准化,使特征数据完全满足
标准正态分布,集中在激活函数中心的线性区域,使激活函数丧失了非线性特性。
*因此,在BN操作中为每个卷积核引入了两个可训练参数:缩放因子γ和偏移因子β,调整批归一化的力度。反向传播时,γ和β会与
其他待训练参数一同被训练优化。使标准正态分布后的特征数据,优化了特征数据分布的宽窄和偏移量,保证了网络的非线性表达力。
批标准化后,第k个卷积核的输出特征图(feature map)中第i个像素点。
TF描述批标准化
tf.keras.layers.BatchNormalization()
把BN层加到卷积层和激活层之间
*公示图像复杂,参见视频。
池化操作用于减少神经网络中的特征数据量。
最大值池化法可提取图片纹理,
均值池化法可保留背景特征。
TF描述池化
tf.keras.layers.MaxPool2D( # 最大值池化法
pool_size=池化核尺寸, #正方形写核长整数,或(核高h,核宽w)
strides=池化步长, #步长整数,或(纵向步长h,横向步长w),默认为pool size
padding='valid'or'same' #使用全零填充是"same”,不使用是"valid”(默认)
)
tf.keras.layers.AveragePooling2D( # 均值池化法
pool_size=池化核尺寸, #正方形写核长整数,或(核高h,核宽w)
strides=池化步长, #步长整数,或(纵向步长h,横向步长w),默认为pool_size
padding='valid'or'same' #使用全零填充是“same",不使用是"valid”(默认)
)
*Dropout是由Hinton提出的一个网络正则化技巧,Dropout在训练阶段和推理阶段的工作是不一样的,就训练阶段而言,Dropout
允许神经元具有p的概率与下一层神经元相连;而在推理阶段,该层神经元始终是与下一层神经元相连的,只不过在原有权重因子
的基础上要乘以Dropout比率p。
*为了缓解神经网络过拟合,在神经网络训练时,将一部分神经元按照一定概率从神经网络中暂时舍弃。神经网络使用时,被舍弃
的神经元恢复链接。
TF描述池化
tf.keras.layers.Dropout(舍弃的概率)
常把隐藏层的部分神经元按照一定比例从神经网络中临时舍弃,在使用神经网络时再把所有神经元恢复到神经网络中
Dropout(0.2),# dropout层,0.2表示随机舍弃掉20%的神经元
卷积神经网络就是借助卷积核对输入特征进行特征提取后,再把提取到的特征,送入全连接网络进行识别预测。
卷积神经网络网络的主要模块[]:
[卷积Conv2D → 批标准化BN → 激活Activation → 池化Pooling → 舍弃Dropout] → 全连接FC
对输入特征进行特征提取 →
★卷积是什么?卷积就是特征提取器,就是CBAPD
★特征提取器CBAPD,卷积/批标/激活/池化/舍弃
model = tf.keras.models.Sequential([
Conv2D(filters=6,kernel_size=(5,5),padding=‘same’),#C.卷积层
BatchNormalization(), #B.批标BN层
Activation( ‘relu’), #A.激活层
MaxPool2D(pool_size=(2,2),strides=2,paaaing=same), #P.池化层
Dropout(0.2), #D.舍弃dropout层
])
例:★p27_cifar10_baseline.py
提供5万张32*32像素点的十分类彩色图片和标签,用于训练。
提供1万张32*32像素点的十分类彩色图片和标签,用于测试。
飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车,分别对应标签0、1、2、3...、9
用卷积神经网络训练cifar10数据集, 搭建一个一层卷积、两层全连接的网络
5x5 conv,filters=6 C(核:6*5*5,步长:1,全零填充:same)
2x2 pool,strides=2 B(Yes)
A (relu)
↓ P(max,核:2*2,步长:2,全零填充:same)
D(0.2) #20%的神经元休眠
Dense 128 Flatten
↓ Dense(神经元:128,激活:relu,Dropout:0.2)
Dense 10 Dense(数据集是10分类,所以神经元:10,激活:softmax)softmax使输出符合概率分布
源码:★p27_cifar10_baseline.py
'基础代码,后续改动基本在class中有差别'
import tensorflow as tf
import os
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten, Dense
from tensorflow.keras import Model
np.set_printoptions(threshold=np.inf)
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
class Baseline(Model):
def __init__(self):
'在此准备出搭建神经网络要用的每一层结构,即CBAPD'
super(Baseline, self).__init__()
self.c1 = Conv2D(filters=6, kernel_size=(5, 5), padding='same') # 卷积层
'filters指的是卷积模板的数量,对于同一幅图片你可以使用多个不同的卷积模板来提取图片不同方向的特征'
self.b1 = BatchNormalization() # BN层
self.a1 = Activation('relu') # 激活层
self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same') # 池化层
self.d1 = Dropout(0.2) # dropout层
self.flatten = Flatten()
self.f1 = Dense(128, activation='relu')
self.d2 = Dropout(0.2)
self.f2 = Dense(10, activation='softmax')
'在call函数中调用_init__函数里搭建好的每层网络结构,从输入到输出过一次前向传播,返回推理结果y'
def call(self, x):
x = self.c1(x)
x = self.b1(x)
x = self.a1(x)
x = self.p1(x)
x = self.d1(x)
x = self.flatten(x)
x = self.f1(x)
x = self.d2(x)
y = self.f2(x)
return y
model = Baseline()
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
checkpoint_save_path = "./checkpoint/Baseline.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
history = model.fit(x_train, y_train, batch_size=32, epochs=20, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
model.summary()
# print(model.trainable_variables)
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
file.write(str(v.name) + '\n')
file.write(str(v.shape) + '\n')
file.write(str(v.numpy()) + '\n')
file.close()
############################################### show ###############################################
# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
LeNet_1998 - AlexNet_2012 - VGGNet_2014 - InceptionNet_2014 - ResNet_2015
p31 p34 p36 p40 p46
LeNet由Yann LeCun于1998年提出,Recognition.Proceedings of the IEEE,1998.
*卷积网络开篇之作。通过共享卷积核减少了网络的参数。
Yaun Lecmn, Leon Bottou,Y. Bengio, Patrick Haffner: Gradient-Based Learning Applied to Document
AlexNet网络诞生于2012年,当年ImageNet竞赛的冠军,Top5错误率为16.4%
*使用relu激活函数,提升训练速度;使用Dropout,缓解过拟合
Alex Krishevsky, Ilya Sutskever, Geoffrey E.Hinton.
ImageNet Classification with Deep Convolution Netvorks. In NIPS,2012.
VGGNet诞生于2014年,当年ImageNet竞赛的亚军,Top5错误率减小到7.3%
*使用小尼寸卷积核,在减少参数的同时提高了识别准确率,适合并行(硬件)加速
K.Simoryam,A. Zisserma.Very Deep Convolutional Networks for Large-Scale Image Recognition.In CVPR,2015.
InceptionNet诞生于2014年,当年ImageNet竞赛冠军,Top5错误率为6.67%
*一层内使用不同尺寸卷积核,提升感知力使用批标准化,缓解梯度消失
Szegedy C, LiuW, Jia Y,et al.Going Deeper with Comvolutions.In CVPR,2015.
视频查看Inception结构块
ResNet诞生于2015年,当年ImageNet竞赛冠军,Top5错误率为3.57%
Kaiming He何凯明,Xiamgyu Zhang, Shaoqing Ren.Deep Residual Learning for Image Recognition.In CPVR,2016.
*提出了层间残差跳连,引入了前方信息,缓解模型退化(梯度消失),使神经网络层数增加成为可能
单纯堆叠神经网络层数,会使神经网络模型退化,以至于后边的特征丢失了前边特征的原本模样。用一根跳连线将前边的特征直接接到了后边,使这里的输出结果H(x),包含了堆叠卷积的非线性输出F(x),和跳过这两层堆叠卷积,直接连接过来的恒等映射x,让它们的对应元素相加,这一操作有效缓解了神经网络模型堆叠导致的退化。使得神经网络可以向着更深层级发展。
Inception块中的“+”是沿深度方向叠加(千层蛋糕层数叠加)
ResNet块中的“+”是特征图对应元素值相加(矩阵值相加)
本讲目标:用RNN实现连续数据的预测(以股票预测为例)
CNN和RNN的对比:
CNN: 借助卷积核(kernel)提取特征后,送入后续网络(如全连接网络 Dense) 进行分类、目标检测等操作。CNN 借助卷积核从空间维度提取信息,卷积核参数空间共享。
RNN: 借助循环核(cell)提取特征后,送入后续网络(如全连接网络 Dense) 进行预测等操作。RNN 借助循环核从时间维度提取信息,循环核参数时间共享。
循环神经网络
循环核
循环核按时间步展开
循环计算层
TF描述循环计算层
循环计算过程
实践1: ABCDE字母预测
One-hot
Embedding
实践2:股票预测
RNN
LSTM
GRU
前向传播时:记忆体内存储的状态信息ht,在每个时刻都被刷新,三个参数矩阵Wxh Whh Why自始至终都是固定不变的。
反向传播时:三个参数矩阵Wxh Whh Why被梯度下降法更新。
我们可以认为 ht 储存了网络中的记忆(memory),RNN学习的目标是使得 ht 记录了在 t 时刻之前(含)的输入信息 x1,x2,…xt 。在新词 xt 输入到网络之后,之前的隐状态向量 ht-1 就转换为和当前输入 xt 有关的 ht。
yt = softmax(ht·why +by)
ht = tanh(xt·Wxh+ht-1·Whh +bh)
yt,当前时刻循环核的输出特征
ht,记忆体内存储的当前时刻的状态信息
xt,当前时刻循环核的输入特征
by,偏置项
tanh/sotfmax,激活函数
循环计算层:向输出方向生长
每个循环核中记忆体的个数可以根据我们的需求任意指定。
tf.keras.layers.SimpleRNN(记忆体个数,activation=‘激活函数’,
return_sequences=是否每个时刻输出ht到下一层)
activation=“激活函数’(不写,默认使用tanh)
return_sequences=True 各时间步输出ht,推送到下一层
return_sequences=False 仅最后时间步输出ht(默认)
例:SimpleRNN(3,return_sequences=True)
TF描述循环计算层
入RNN时,x_train维度:
[送入样本数,循不核时间展开步数,每个时间步输入特征个数]
字母预测:输入a预测出b,输入b预测出,输入c预测出d,输入d预测出e,输入e预测出a
*神经网络的输入都是数字,所以我们先要把用到的abcde这五个字母,用数字表示出来。最简单直接的方法就是用独热码对这五个字母编码,每个
字母用一个独热码表示,随机生成了Wxh、Whh和Why三个参数矩阵。
p15_rnn_onehot_1pre1.py
以“连续输入四个字母,预测下一个字母”为例,讲解循环核按时间展开后的循环计算过程
独热码:数据量大过于稀疏,映射之间是独立的,没有表现出关联性
Embedding:是一种单词编码方法,用低维向量实现了编码,这种编码通过神经网络训练优化,能表达出单词间的相关性。
tf.keras.layers.Embedding(词汇表大小,编码维度)
词汇表大小,是编码一共要表示多少个单词
编码维度,是用几个数字表达一个单词
例:对1-100进行编码,[4]编码为[0.25,0.1,0.11]
所以,tf.keras.layers.Embedding(100,3 )
入Embedding时,x_train维度: [送入样本数,循环核时间展开步数]
用RNN实现输入一个字母,预测下一个字母(Embedding编码)
p27_rnn_embedding_1pre1.py
用RNN实现输入连续四个字母,预测下一个字母(Embedding编码)
p32_rnn_embedding_4pre1.py
长短记忆网络中引入了三个门限
输入门(门限) :it = σ(Wi.[ht-1,xt]+bi)
遗忘门(门限) :ft = σ(Wf.[ht-1,xt]+bf)
输出门(门限) :ot = σ(Wo.[ht-1,xt]+bo)
当前时刻的输入特征xt;上个时刻的短期记忆ht-1;Wi、WF和Wo是待训练参数矩阵;bi、bf和bo是待训练偏置项。他们都经过sigmoid激活函数归一化,
细胞态(长期记忆) :Ct = ft * Ct-1+i*C_t
遗忘门*上个时刻的长期记忆 + 当前时刻归纳出的新知识乘以输入门
记忆体(短期记忆) :ht = ot*tanh(Ct)
属于长期记忆的一部分,是输出门乘以细胞态过tanh激活函数的结果
候选态(归纳出的新知识) :C_t= tanh(Wc.[ht-1, xt]+bc)
tf.keras.layers.LSTM(记忆体个数,return_sequences=是否返回输出)
return_sequences=True各时间步输出ht
return_sequences=False仅最后时间步输出ht(默认)
一般中间层用True,最后一层用False
动画展示RNN、LSTM和GRU计算过程 https://zhuanlan.zhihu.com/p/115823190
LSTM与GRU数学推导 https://www.jianshu.com/p/1dc21b622cf9