一. 神经网络(NN)的复杂度
NN复杂度:多个NN层数和NN参数的个数表示。
1.空间复杂度
层数=隐藏层层数+一个输出层
总参数=总w+总b
2.时间复杂度
乘加运算次数
二.指数衰减学习率
在神经网络的参数更新过程中,学习率不能太大也不能太小,太大可能会导致参数在最优值两侧来回移动,太小会大大降低优化速度,为了解决学习率的问题,TensorFlow 提供了一种灵活的学习率设置方法,即指数衰减法。
可以用较大的学习率,快速得到较优解,然后逐步减小学习率,使模型在训练后期稳定。
指数衰减学习率=初始学习率*学习率衰减率**(当前轮数/多少论=轮衰减一次)
import tensorflow as tf
import matplotlib.pyplot as plt
epoch=1000#定义迭代轮数
LR_BASE=0.2#当前学习率
LR_DECAY=0.99#学习率衰减率
LR_STEP=1#一轮更新一次学习率
w=tf.Variable(tf.constant(5,dtype=tf.float32))
lr_epoch=[]
for epoch in range(epoch):
lr=LR_BASE * LR_DECAY ** (epoch/LR_STEP)
lr_epoch.append(lr)
with tf.GradientTape() as tape:
loss=tf.square(w+1)
grads=tape.gradient(loss,w)
w.assign_sub(lr*grads)
print("After %s epoch w is %f,loss is %f,lr is %f" %(epoch,w,loss,lr))
#将学习率用二维图像表示
plt.figure(num=1)
plt.plot(range(epoch+1),lr_epoch)
plt.xlabel('lr')
plt.ylabel('epoch')
plt.show()
运行结果:随着迭代次数,学习率在指数衰减
三.激活函数
优秀的激活函数:
非线性:激活函数非线性时,多层神经网络可逼近所有函数
可微性:优化器大多用梯度下降更新参数
单调性:当激活函数是单调的,能保证单层网络的损失函数是凸函数
近似恒等性:F(X)~x当参数初始化为随机小值时,神经网络更稳定
激活函数输出值的范围:
激活函数输出为有限值时,基于梯度的优化方法更稳定
激活函数输出为无限值时,建议调小学习率
1.sigmoid函数的导数的输出使是0到0.25之间的小数,链式求导需要层导数连续相乘,会出现多个0到0.25之间的连续相乘,结果将趋于0产生梯度消失,使得参数无法继续更新。
2.输出的非零均值,sigmoid激活函数后的数据都是正数,回事收敛变慢
3.存在幂运算计算复杂度大,训练时间长。
对于初学者的建议:
首选relu激活函数;
R学习率设置较小值;
输入特征标准化,即让输入特征满足以0为均值,
1为标准差的正态分布;
初始参数中心化,即让随机生成的参数满足以0 为均值,sqrt{(2/当前输入的特征值)} 为标准差的正态分布。
四.损失函数
损失函数就是与测试y与已知答案y_的差距
NN优化目标有三种使loss最小----1.mse(平均均方误差),2.自定义损失函数,3.ce((Cross Entropy) (交叉熵))
1.mse(平均均方误差)
tensorflow表示:
loss=tf.reduce_mean(tf.square(y_-y))
举个例子:
预测酸奶日销量y, x1、 x2是影响日销量的因素。建模前,应预先采集的数据有:每日x1、 x2和销量y_ (即已知答案, 最佳情况:产量=销量)拟造数据集X,Y_ : y_ =x1 + x2
噪声: -0.05~ +0.05拟合 可以预测销量的函数。
其实就是通过梯度下降,使w1的两个数与制造数据集的公式y=x1+x2+-0.05一致,也就是使两个数接近于1.
代码:
numpy.random.RandomState(seed=?)是一个伪随机数生成器, 此命令将会产生一个随机状态种子,在该状态下生成的随机序列(正态分布)一定会有相同的模式。伪随机数是用确定性的算法计算出来的似来自[0,1]均匀分布的随机数序列,作用同np.random.seed(n)
import tensorflow as tf
import numpy as np
#设置随机种子
seed=23455
#numpy.random.RandomState(seed=?)是一个伪随机数生成器, 此命令将会产生一个随机状态种子,在该状态下生成的随机序列(正态分布)一定会有相同的模式。
#伪随机数是用确定性的算法计算出来的似来自[0,1]均匀分布的随机数序列
#起作用同np.random.seed(n)
rdm = np.random.RandomState(seed=seed)
x=rdm.rand(32,2)#生成x1,x2
#rdm.rand() 生成0-1之间的随机数
#取出x中的两列数,输入前面再加上噪声+-0.05
y_=[[x1+x2+(rdm.rand()/10.0-0.05)] for (x1,x2) in x]
x=tf.cast(x,dtype=tf.float32)
#生成两行一列的生态分布随机数
w1=tf.Variable(tf.random.normal([2,1],stddev=1,seed=1))
epoch=15000
lr=0.002
for epoch in range(epoch):
with tf.GradientTape() as type:
y=tf.matmul(x,w1)
loss=tf.reduce_mean(tf.square(y_-y))
grads=type.gradient(loss,w1)
w1.assign_sub(lr*grads)
if epoch %500==0:
print("After %d training steps ,w1 is" %(epoch))
print(w1.numpy(),"\n")
print("Final w1 is ",w1.numpy())
最终结果接近于1,说明拟合成功。
2.自定义损失函数
如上述的商品销量中,预测多了,损失的是成本,少了损失的是利润。用mse的loss无法实现利益的最大化,所以可以自定义loss
如酸奶销量,成本COST为1元,利润PROFIT为99元,如何实现利益的最大化呢
代码如下:
tf.greater(y,y_)比较大小
tf.where(condition,a,b)相当于三目运算,如果condition成立输出a,否则输出b
import tensorflow as tf
import numpy as np
#设置随机种子
seed=23455
rdm = np.random.RandomState(seed=seed)
x=rdm.rand(32,2)#生成x1,x2
#rdm.rand() 生成0-1之间的随机数
#取出x中的两列数,输入前面再加上噪声+-0.05
y_=[[x1+x2+(rdm.rand()/10.0-0.05)] for (x1,x2) in x]
x=tf.cast(x,dtype=tf.float32)
#生成两行一列的生态分布随机数
w1=tf.Variable(tf.random.normal([2,1],stddev=1,seed=1))
Cost=1
Profit=99
epoch=15000
lr=0.002
for epoch in range(epoch):
with tf.GradientTape() as type:
y=tf.matmul(x,w1)
#tf.greater(y,y_)比较大小
#tf.where(condition,a,b)相当于三目运算,如果condition成立输出a,否则输出b
loss=tf.reduce_sum(tf.where(tf.greater(y,y_),(y-y_)*Cost,(y_-y)*Profit))
grads=type.gradient(loss,w1)
w1.assign_sub(lr*grads)
if epoch %500==0:
print("After %d training steps ,w1 is" %(epoch))
print(w1.numpy(),"\n")
print("Final w1 is ",w1.numpy())
结果是如果cost小,profit大,那么w1的值就会比1大,因为利润大,尽可能多预测。
3.交叉熵
Softmax于交叉熵结合
五.正则化缓解过拟合
六.优化器
1.SGD
代码表示:
# w1 = w1 - lr * w1_grad
w1.assign_sub(lr * grads[0])
2.SGDM
m_w,m_b=0,0
beta=0.9
grads = tape.gradient(loss, [w1, b1])
m_w=beta*m_w+(1-beta)*grads[0]
m_b=beta*m_b+(1-beta)*grads[1]
# 实现梯度更新 w1 = w1 - lr * w1_grad b = b - lr * b_grad
w1.assign_sub(lr * m_w) # 参数w1自更新
b1.assign_sub(lr * m_b) # 参数b自更新
3.Adagrad
代码:
v_w,v_b=0,0
grads = tape.gradient(loss, [w1, b1])
v_w+=tf.square(grads[0])
v_b+=tf.square(grads[1])
# 实现梯度更新 w1 = w1 - lr * w1_grad b = b - lr * b_grad
w1.assign_sub(lr * grads[0]/tf.sqrt(v_w)) # 参数w1自更新
b1.assign_sub(lr * grads[1]/tf.sqrt(v_b)) # 参数b自更新
4.RMSProp
v_w,v_b=0,0
beta=0.9
grads = tape.gradient(loss, [w1, b1])
v_w=beta*v_w+(1-beta)*tf.square(grads[0])
v_b=beta*v_b+(1-beta)*tf.square(grads[1])
# 实现梯度更新 w1 = w1 - lr * w1_grad b = b - lr * b_grad
w1.assign_sub(lr * grads[0] / tf.sqrt(v_w)) # 参数w1自更新
b1.assign_sub(lr * grads[1] / tf.sqrt(v_b)) # 参数b自更新
5.Adam
code:
m_w,m_b=0,0
v_w,v_b=0,0
beta1,beta2=0.9,0.999
delta_w,delta_b=0,0
global_step=0#存放总batch数
# 训练部分
for epoch in range(epoch): #数据集级别的循环,每个epoch循环一次数据集
for step, (x_train, y_train) in enumerate(train_db): #batch级别的循环 ,每个step循环一个batch
global_step+=1
................
................
grads = tape.gradient(loss, [w1, b1])
m_w = beta1 * m_w + (1 - beta1) * grads[0]
m_b = beta1*m_b + (1 - beta1)*grads[1]
v_w = beta2 * v_w + (1 - beta2) * tf.square(grads[0])
v_b = beta2 * v_b + (1 - beta2) * tf.square(grads[1])
m_w_correction = m_w / (1 - tf.pow(beta1, int(global_step)))
m_b_correction = m_b / (1 - tf.pow(beta1, int(global_step)))
v_w_correction = v_w / (1 - tf.pow(beta2, int(global_step)))
v_b_correction = v_b / (1 - tf.pow(beta2, int(global_step)))
w1.assign_sub(lr * m_w_correction / tf.sqrt(v_w_correction))
b1.assign_sub(lr * m_b_correction / tf.sqrt(v_b_correction))