预备知识
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt import random import pandas as pd plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签 plt.rcParams['axes.unicode_minus']=False # 正常显示负号
tf.where()
tf.where(条件语句,真返回值,假返回值)
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对应位置元素 print(c)
tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
np.random.RandomState.rand()
返回一个[0,1)之间的随机数
np.random.RandomState.rand(维度)
若参数 维度 为空,则返回一个0~1之间的标量
rdm = np.random.RandomState(seed = 1) # seed=常数 每次产生的随机数相同 a = rdm.rand() # 返回一个随机标量 b = rdm.rand(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)
a = np.array([1,2,3]) b = np.array([3,3,3]) c = np.vstack((a,b)) print(c)
[[1 2 3] [3 3 3]]
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]]
神经网络(NN)复杂度
NN复杂度:多用NN层数和NN参数的个数表示
- 空间复杂度
- 层数 = 隐藏层的层数 + 1个输出层
- 总参数 = 总w + 总b
- 时间复杂度
- 乘加运算次数
上图 第一层参数个数是 三行四列w+4个偏置b;第二层参数个数是四行二列w+2个偏置b。总共26个参数
学习率
学习率lr = 0.001时,参数w更新过慢;lr = 0.999时,,参数w不收敛。那么学习率设置多少合适呢?
在实际应用中,可以先用较大的学习率,快速找到较优值,然后逐步减小学习率,使模型找到最优解使模型在训练后期稳定。——指数衰减学习率
指数衰减学习率
指数衰减学习率 = 初始学习率 * 学习率衰减率 (当前轮数/多少轮衰减一次)
备注: (当前轮数/多少轮衰减一次) 是上标
w = tf.Variable(tf.constant(5, dtype=tf.float32)) # lr = 0.2 epoch = 40 LR_BASE = 0.2 LR_DECAY = 0.99 LR_STEP = 1 # 决定更新频率 epoch_all=[] lr_all = [] w_numpy_all=[] loss_all=[] for epoch in range(epoch): # for epoch 定义顶层循环,表示对数据集循环epoch次,此例数据集数据仅有1个w,初始化时候constant赋值为5,循环40次迭代。 lr = LR_BASE * LR_DECAY **(epoch/LR_STEP) # 根据当前迭代次数,动态改变学习率的值 lr_all.append(lr) 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" % (epoch, w.numpy(), loss)) epoch_all.append(epoch) w_numpy_all.append(w.numpy()) loss_all.append(loss) fig,axes = plt.subplots(nrows=1,ncols=3,figsize=(10,5),dpi=300) axes[0].plot(epoch_all,lr_all,color="g",linestyle="-",label="学习率") # 绘画 axes[0].plot(epoch_all,w_numpy_all,color="k",linestyle="-.",label="参数") # 绘画 axes[0].plot(epoch_all,loss_all,color="b",linestyle="--",label="损失率") # 绘画 axes[0].set_title("指数衰减学习率") axes[0].set_xlabel("epoch") axes[0].set_label("data") axes[0].legend(loc="upper right")# 显示图例必须在绘制时设置好 axes[1].plot(epoch_all,lr_all,color="g",linestyle="-",label="学习率") # 绘画 axes[1].plot(epoch_all,w_numpy_all,color="k",linestyle="-.",label="参数") # 绘画 axes[1].set_title("指数衰减学习率") axes[1].set_xlabel("epoch") axes[1].set_ylabel("data") axes[1].legend(loc="upper right")# 显示图例必须在绘制时设置好 axes[2].plot(epoch_all,lr_all,color="g",linestyle="-",label="学习率") # 绘画 axes[2].set_title("指数衰减学习率") axes[2].set_xlabel("epoch") axes[2].set_ylabel("data") axes[2].legend(loc="upper right")# 显示图例必须在绘制时设置好 plt.show() # lr初始值:0.2 请自改学习率 0.001 0.999 看收敛过程 # 最终目的:找到 loss 最小 即 w = -1 的最优参数w
After 0 epoch,w is 2.600000,loss is 36.000000 After 1 epoch,w is 1.174400,loss is 12.959999 After 2 epoch,w is 0.321948,loss is 4.728015 After 3 epoch,w is -0.191126,loss is 1.747547 After 4 epoch,w is -0.501926,loss is 0.654277 After 5 epoch,w is -0.691392,loss is 0.248077 After 6 epoch,w is -0.807611,loss is 0.095239 After 7 epoch,w is -0.879339,loss is 0.037014 After 8 epoch,w is -0.923874,loss is 0.014559 After 9 epoch,w is -0.951691,loss is 0.005795 After 10 epoch,w is -0.969167,loss is 0.002334 After 11 epoch,w is -0.980209,loss is 0.000951 After 12 epoch,w is -0.987226,loss is 0.000392 After 13 epoch,w is -0.991710,loss is 0.000163 After 14 epoch,w is -0.994591,loss is 0.000069 After 15 epoch,w is -0.996452,loss is 0.000029 After 16 epoch,w is -0.997660,loss is 0.000013 After 17 epoch,w is -0.998449,loss is 0.000005 After 18 epoch,w is -0.998967,loss is 0.000002 After 19 epoch,w is -0.999308,loss is 0.000001 After 20 epoch,w is -0.999535,loss is 0.000000 After 21 epoch,w is -0.999685,loss is 0.000000 After 22 epoch,w is -0.999786,loss is 0.000000 After 23 epoch,w is -0.999854,loss is 0.000000 After 24 epoch,w is -0.999900,loss is 0.000000 After 25 epoch,w is -0.999931,loss is 0.000000 After 26 epoch,w is -0.999952,loss is 0.000000 After 27 epoch,w is -0.999967,loss is 0.000000 After 28 epoch,w is -0.999977,loss is 0.000000 After 29 epoch,w is -0.999984,loss is 0.000000 After 30 epoch,w is -0.999989,loss is 0.000000 After 31 epoch,w is -0.999992,loss is 0.000000 After 32 epoch,w is -0.999994,loss is 0.000000 After 33 epoch,w is -0.999996,loss is 0.000000 After 34 epoch,w is -0.999997,loss is 0.000000 After 35 epoch,w is -0.999998,loss is 0.000000 After 36 epoch,w is -0.999999,loss is 0.000000 After 37 epoch,w is -0.999999,loss is 0.000000 After 38 epoch,w is -0.999999,loss is 0.000000 After 39 epoch,w is -0.999999,loss is 0.000000
激活函数
- 线性函数
- y = x * w + b
- 非线性函数(MP模型)
- y = f(x * w + b )
其中f便是激活函数
优秀的激活函数具有以下特点
- 非线性:激活函数非线性时,多层神经网络可逼近所有函数
- 可微性:优化器大多用梯度下降更新参数(如果激活函数不可微,就无法更新参数了)
- 单调性:当激活函数是单调的,能保证单层网络的损失函数是凸函数
- 近似恒等性:f(x)≈x当参数初始化为随机小值时,神经网络更稳定
激活函数输出值的范围
- 激活函数输出为有限值时,基于梯度的优化方法更稳定
- 激活函数输出为无限值时,建议调小学习率
常用激活函数 Sigmoid函数
tf.nn.sigmoid(x)
特点:
(1)已造成梯度消失
(2)输出非0均值,收敛慢
(3)幂运算复杂,训练时间长
常用激活函数 Tanh函数
常用激活函数 Relu函数
注意要避免过多的负数特征送入relu函数,避免神经元死亡
- 改进随机初始化
- 设置更小的学习率,减少参数分布的巨大变化
常用激活函数 Leaky Relu函数
Leaky Relu函数是为了解决relu负区间为0引起神经元死亡问题而设计
损失函数loss
损失函数显示出预测值(y)与已知答案y_的差距
主流loss的三种计算方法
- 【均方误差】mse(Mean Squared Error)
- 前向传播计算出结果y与已知标准答案y_之差的平方_
- 自定义
- 【交叉熵】ce (Cross Entropy)
通过均方误差预测酸奶日销量
已知 酸奶日销量y。其中x1,x2是影响日销量的元素。
建模前,需要采集的数据有
- 每日的x1,x2
- 销量y_
拟造数据集 X,Y:y_=x1+x2;噪声:-0.05~+0.05。拟合可以预测销量的函数
SEED = 23455 rdm = np.random.RandomState(seed=SEED) # 生成[0,1)之间的随机数 此时为了方便调试因此写固定值,实际应用不写seed的 x = rdm.rand(32, 2) y_ = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in x] # 生成噪声[0,1)/10=[0,0.1); [0,0.1)-0.05=[-0.05,0.05) x = tf.cast(x, dtype=tf.float32) w1 = tf.Variable(tf.random.normal([2, 1], stddev=1, seed=1)) # 两行一列 epoch = 15000 # 迭代次数 lr = 0.002 # 学习率 epoch_all=[] w0_numpy_all = [] w1_numpy_all = [] for epoch in range(epoch): with tf.GradientTape() as tape: y = tf.matmul(x, w1) loss_mse = tf.reduce_mean(tf.square(y_ - y)) grads = tape.gradient(loss_mse, w1) # 损失函数对待训练参数w1求偏导 w1.assign_sub(lr * grads) if epoch % 500 == 0: # 每迭代500次记录一次数据 # print("After %d training steps,w1 is " % (epoch)) epoch_all.append(epoch) w0_numpy_all.append(w1.numpy()[0]) w1_numpy_all.append(w1.numpy()[1]) # print(w1.numpy(), "\n") plt.figure(figsize=(10,5),dpi=360) plt.plot(epoch_all,w0_numpy_all,color="g",linestyle="-",label="x1 标准答案") # 绘画 plt.plot(epoch_all,w1_numpy_all,color="k",linestyle="-.",label="x2 预测答案") # 绘画 plt.title("预测酸奶日销量") plt.xlabel("epoch") plt.ylabel("data") plt.legend(loc="upper right")# 显示图例必须在绘制时设置好 plt.show() # print("Final w1 is: ", w1.numpy()) # 最后得到的结果是趋近于1的
上图结果 y=1.00 * x1 + 1.00 * x2符合制造数据集的公式 y = 1 * x1 + 1 * x2,说明预测酸奶日销量的公式拟合正确
上述方法,如果预测多了,损失成本;如果预测少了,损失利润。
若 利润 ≠ 成本 ,则mes产生的loss无法利益最大化
为了实现利益最大化,在此引入自定义损失函数
自定义函数
上图中,PROFIT
代表利润,COST
代表成本
如果 预测结果y > 标准答案 y_ 则预测的多了,损失成本,则应该 ( y - y_ ) * COST
如果 预测结果y < 标准答案 y_ 则预测的少了,损失利润,则应该 ( y_ - y ) * COST
通过自定义函数预测商品销量
loss_zdy = tf.reduce_sum(tf.where( tf.greater(y,y_) , COST*(y-y_) , PROFIT*(y_-y) ))
import tensorflow as tf import numpy as np SEED = 23455 COST = 1 # 成本 PROFIT = 99 rdm = np.random.RandomState(SEED) x = rdm.rand(32, 2) y_ = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in x] # 生成噪声[0,1)/10=[0,0.1); [0,0.1)-0.05=[-0.05,0.05) x = tf.cast(x, dtype=tf.float32) w1 = tf.Variable(tf.random.normal([2, 1], stddev=1, seed=1)) epoch = 10000 lr = 0.002 w0_numpy_all = [] w1_numpy_all = [] epoch_all = [] for epoch in range(epoch): with tf.GradientTape() as tape: y = tf.matmul(x, w1) loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * COST, (y_ - y) * PROFIT)) grads = tape.gradient(loss, w1) w1.assign_sub(lr * grads) if epoch % 500 == 0: # print("After %d training steps,w1 is " % (epoch)) # print(w1.numpy()[1], "\n") epoch_all.append(epoch) w0_numpy_all.append(w1.numpy()[0]) w1_numpy_all.append(w1.numpy()[1]) # print("Final w1 is: ", w1.numpy()) plt.figure(figsize=(10,5),dpi=360) plt.plot(epoch_all,w0_numpy_all,color="g",linestyle="-",label="x1 标准答案") # 绘画 plt.plot(epoch_all,w1_numpy_all,color="k",linestyle="-.",label="x2 预测答案") # 绘画 plt.title("预测酸奶日销量") plt.xlabel("epoch") plt.ylabel("data") plt.legend(loc="upper right")# 显示图例必须在绘制时设置好 plt.show() # 自定义损失函数 # 酸奶成本1元, 酸奶利润99元 # 成本很低,利润很高,人们希望多预测些,生成模型系数大于1,往多了预测
看上图可以发现,自定义损失函数的系数都大于均方误差做损失函数时的系数,即模型在尽量往大了预测,(大损成本小损利润)
接下来把成本改为99,利润改为1,如下:
import tensorflow as tf import numpy as np SEED = 23455 COST = 99 # 成本 PROFIT = 1 rdm = np.random.RandomState(SEED) x = rdm.rand(32, 2) y_ = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in x] # 生成噪声[0,1)/10=[0,0.1); [0,0.1)-0.05=[-0.05,0.05) x = tf.cast(x, dtype=tf.float32) w1 = tf.Variable(tf.random.normal([2, 1], stddev=1, seed=1)) epoch = 10000 lr = 0.002 w0_numpy_all = [] w1_numpy_all = [] epoch_all = [] for epoch in range(epoch): with tf.GradientTape() as tape: y = tf.matmul(x, w1) loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * COST, (y_ - y) * PROFIT)) grads = tape.gradient(loss, w1) w1.assign_sub(lr * grads) if epoch % 500 == 0: # print("After %d training steps,w1 is " % (epoch)) # print(w1.numpy()[1], "\n") epoch_all.append(epoch) w0_numpy_all.append(w1.numpy()[0]) w1_numpy_all.append(w1.numpy()[1]) # print("Final w1 is: ", w1.numpy()) plt.figure(figsize=(10,5),dpi=360) plt.plot(epoch_all,w0_numpy_all,color="g",linestyle="-",label="x1 标准答案") # 绘画 plt.plot(epoch_all,w1_numpy_all,color="k",linestyle="-.",label="x2 预测答案") # 绘画 plt.title("预测酸奶日销量") plt.xlabel("epoch") plt.ylabel("data") plt.legend(loc="upper right")# 显示图例必须在绘制时设置好 plt.show() # 自定义损失函数 # 酸奶成本1元, 酸奶利润99元 # 成本很低,利润很高,人们希望多预测些,生成模型系数大于1,往多了预测
看上图可以发现,模型在尽量往少了预测
交叉熵损失函数(Cross Entropy)
tensorflow交叉熵计算公式 tf.losses.categorical_crossentropy(标准答案y_,输出结果)
loss_ce1 = tf.losses.categorical_crossentropy([1, 0], [0.6, 0.4]) loss_ce2 = tf.losses.categorical_crossentropy([1, 0], [0.8, 0.2]) print("loss_ce1:", loss_ce1) print("loss_ce2:", loss_ce2) # 交叉熵损失函数
loss_ce1: tf.Tensor(0.5108256, shape=(), dtype=float32) loss_ce2: tf.Tensor(0.22314353, shape=(), dtype=float32)
同时计算概率分布和交叉熵的函数
tf.nn.softmax_cross_entropy_with_logits(y_,y)
|
y_ = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 0, 0], [0, 1, 0]]) y = np.array([[12, 3, 2], [3, 10, 1], [1, 2, 5], [4, 6.5, 1.2], [3, 6, 1]]) ## 分步完成 y_pro = tf.nn.softmax(y) loss_ce1 = tf.losses.categorical_crossentropy(y_,y_pro) ## 一步完成 loss_ce2 = tf.nn.softmax_cross_entropy_with_logits(y_, y) print('分步计算的结果:\n', loss_ce1) print('结合计算的结果:\n', loss_ce2)
分步计算的结果: tf.Tensor( [1.68795487e-04 1.03475622e-03 6.58839038e-02 2.58349207e+00 5.49852354e-02], shape=(5,), dtype=float64) 结合计算的结果: tf.Tensor( [1.68795487e-04 1.03475622e-03 6.58839038e-02 2.58349207e+00 5.49852354e-02], shape=(5,), dtype=float64)