神经网络优化
1、预备知识
tf.where()
tf.where(条件语句, 真返回A, 假返回B)
a = tf.constant([1, 2, 3, 1, 1])
b = tf.constant([0, 1, 3, 4, 5])
c = tf.where(tf.greater(a, b), a, b) # 若a>b,返回a对应的元素,否则返回b对应的元素
# greater()判断a是否大于b,对应元素比较,例如a中的1>b中的0则返回a中的1,a中第四个1不大于b中第四个4则返回b中的4
print("c:", c)
运行结果:
c: tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
np.random.RandomState.rand()
# 返回一个[0, 1)之间的随机数
np.random.RandomState.rand(维度) # 维度为空,返回标量
import numpy as np
rdm = np.random.RandomState(seed=1) # seed=常数,每次生成的随机数相同
a = rdm.rand() # 返回一个标量
b = rdm.rand(2, 3) # 返回维度为2行3列随机数矩阵
print("a:", a)
print("b:", b)
运行结果:
a: 0.417022004702574
b: [[7.20324493e-01 1.14374817e-04 3.02332573e-01]
[1.46755891e-01 9.23385948e-02 1.86260211e-01]]
np.vstack()
# 将两个数组按垂直方向叠加
np.vstack(数组1, 数组2)
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.vstack((a, b)) # 垂直叠加,变为二维数组
print("c:\n", c)
运行结果:
c:
[[1 2 3]
[4 5 6]]
np.mgrid[ ] , .ravel() , np.c_[ ]
这三个函数经常一起使用,可以构成网格坐标点
np.mgrid[] # 返回若干组维度相同的等差数组,[起始值,结束值)
np.mgrid[起始值:结束值:步长,起始值:结束值:步长,...] # 不同组用逗号隔开
x.ravel() 将多维数组x变为一维数组
np.c_[] 使返回的间隔数值点配对
np.c_[数组1, 数组2, ...] 把数组配对后输出
# 生成等间隔数值点
x, y = np.mgrid[1:3:1, 2:4:0.5]
# 将x, y拉直,并合并配对为二维张量,生成二维坐标点
grid = np.c_[x.ravel(), y.ravel()]
print("x:\n", x)
print("y:\n", y)
print("x.ravel():\n", x.ravel())
print("y.ravel():\n", y.ravel())
print('grid:\n', grid)
运行结果:
x:
[[1. 1. 1. 1.]
[2. 2. 2. 2.]]
y:
[[2. 2.5 3. 3.5]
[2. 2.5 3. 3.5]]
x.ravel():
[1. 1. 1. 1. 2. 2. 2. 2.]
y.ravel():
[2. 2.5 3. 3.5 2. 2.5 3. 3.5]
grid:
[[1. 2. ]
[1. 2.5]
[1. 3. ]
[1. 3.5]
[2. 2. ]
[2. 2.5]
[2. 3. ]
[2. 3.5]]
2、复杂度学习率
神经网络(NN)复杂度
NN复杂度:分为空间复杂度和时间复杂度
空间复杂度多用神经网络层数和神经网络中待优化的参数的个数表示
在计算神经网络层数时,只统计具有运算能力的层
输入层仅传输数据,不参加运算,输入层和输出层之间的所有层都叫隐藏层
神经网络层数=n个隐藏层+1个输出层
总参数=总w+总b
在上图中,总参数=3×4+4 + 4×2+2 = 26
时间复杂度可以用神经网络中乘加运算次数表示
有几条权重线,就有几次乘加运算
上图中:3×4 + 4×2 = 20
学习率
为了找到合适的学习率,可以使用指数衰减学习率的方法
先用较大的学习率,快速得到较优解,然后逐步减小学习率,使模型在训练后期稳定。
指 数 学 习 率 = 初 始 学 习 率 ∗ 学 习 率 衰 减 率 ( 当 前 轮 数 / 多 少 轮 衰 减 一 次 ) 指数学习率 = 初始学习率 * 学习率衰减率^{(当前轮数/多少轮衰减一次)} 指数学习率=初始学习率∗学习率衰减率(当前轮数/多少轮衰减一次)
import tensorflow as tf
w = tf.Variable(tf.constant(5, dtype=tf.float32))
epoch = 40
LR_BASE = 0.2 # 最初学习率
LR_DECAY = 0.99 # 学习率衰减率
LR_STEP = 1 # 喂入多少轮BATCH_SIZE后,更新一次学习率
for epoch in range(epoch): # for epoch 定义顶层循环,表示对数据集循环epoch次,此例数据集数据仅有1个w,初始化时候constant赋值为5,循环100次迭代。
lr = LR_BASE * LR_DECAY ** (epoch / LR_STEP)
with tf.GradientTape() as tape: # with结构到grads框起了梯度的计算过程。
loss = tf.square(w + 1)
grads = tape.gradient(loss, w) # .gradient函数告知谁对谁求导
w.assign_sub(lr * grads) # .assign_sub 对变量做自减 即:w -= lr*grads 即 w = w - lr*grads
print("After %s epoch,w is %f,loss is %f,lr is %f" % (epoch, w.numpy(), loss, lr))
3、激活函数
优秀的激活函数
- 非线性:激活函数非线性时,多层神经网络可逼近所有函数
- 可微性:优化器大多用梯度下降更新参数
- 单调性:当激活函数是单调的,能保证单层网络的损失函数是凸函数
- 近似恒等性:f(x)≈x,即激活函数的输出值约等于输入值,当参数初始化为随机小值时,神经网络更稳定
激活函数输出值的范围
- 激活函数输出为有限值时,权重对特征的影响会更显著,基于梯度的优化方法更稳定
- 激活函数输出为无限值时,参数的初始值对模型的影响非常大,建议调小学习率
Sigmoid函数
tf.nn.sigmoid(x)
缺点:
- 深层神经网络更新参数时需要从输出层到输入层逐层进行链式求导,而sigmoid函数的导数输出是0到0.25之间的小数,链式求导需要多层导数连续相乘,会出现多个0到0.25之间的连续相乘,结果将趋于0,产生梯度消失。
- 我们希望输入每层神经网络的特征是以0为均值的小数值,但是过sigmoid激活函数后的数据都是正数,会使收敛变慢。
- sigmoid函数存在幂运算计算复杂度大,训练时间长。
Tanh函数
tf.math.tanh(x)
特点:
- 输出是0均值
- 仍易造成梯度消失
- 仍存在幂运算,训练时间长
Relu函数
tf.nn.relu(x)
f ( x ) = m a x ( x , 0 ) = { 0 x < 0 x x > = 0 f(x)=max(x,0)=\{\begin{matrix} 0&x<0\\ x&x>=0\end{matrix} f(x)=max(x,0)={ 0