美文网首页
预备知识及优化器

预备知识及优化器

作者: 原上的小木屋 | 来源:发表于2020-09-22 12:12 被阅读0次

    神经网络预备知识

    目标:学会神经网络优化过程,使用正则化减少过拟合,使用优化器更新网络参数

    1. 预备知识
    2. 神经网络复杂度
    3. 指数衰减学习率
    4. 激活函数
    5. 损失函数
    6. 欠拟合与过拟合
    7. 正则化减少过拟合
    8. 优化器更新网络参数

    几个重要的函数

    1. tf.where()
      条件语句真返回A,条件语句假返回B 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对应位置的元素
    print("c:",c)
    
    c: tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
    
    1. 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]]
    
    1. 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:",c)
    
    c: [[1 2 3]
     [4 5 6]]
    
    1. np.mgrid[].ravelnp.c_[]这三个函数经常一起使用,可以生成坐标点

    np.mgrid[起始值:结束值:步长,起始值:结束值:步长,...]

    x.ravel()将x变为一维数组,将x变量拉直

    np.c_[]使返回的间隔数值点配对,np.c_[数组1,数组2,...]

    import  numpy as np
    x,y=np.mgrid[1:3:1,2:4:0.5]
    grid=np.c_[x.ravel(),y.ravel()]
    print("x:",x)
    print("y:",y)
    print("grid:",grid)
    
    x: [[1. 1. 1. 1.]
     [2. 2. 2. 2.]]
    y: [[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]]
    
    x.shape,y.shape,grid.shape
    
    ((2, 4), (2, 4), (8, 2))
    

    神经网络复杂度

    1. NN复杂度:多用NN层数和NN参数的个数表示

    空间复杂度:

    • 层数=隐藏层的层数+1个输出层
    • 总参数=总w+总b

    时间复杂度:

    • 乘加运算次数

    指数衰减学习率

    可以先用较大的学习率,快速得到较优解,然后逐步减小学习率,使模型在训练后期稳定

    指数衰减学习率=初始学习率*学习率衰减率^{当前轮数/多少轮衰减一次}

    激活函数

    简化模型:y=x*w+b

    MP模型:y=f(x*w+b),f即为激活函数

    优秀的激活函数需要具备的特点

    1. 非线性:激活函数非线性时,多层神经网络可逼近所有函数
    2. 可微性:优化器大多采用梯度下降更新参数
    3. 单调性:当激活函数是单调的,能保证单层网络的损失函数是凸函数
    4. 近似恒等性:f(x)\approx x当参数初始化为随即小值时,神经网络更稳定

    激活函数输出值的范围

    1. 激活函数输出为有限值时,基于梯度的优化方法更稳定
    2. 激活函数输出为无限值时,建议调小学习率

    几个常见的激活函数

    1. Sigmoid函数,tf.nn.sigmoid(x)
      f(x)=\frac {1} {1+e^{-x}}

    特点:一是易造成梯度消失,二是输出非0均值,收敛慢,三是幂运算复杂,训练时间长

    1. Tanh函数,tf,math.tanh(x)f(x)=\frac {1-e^{-2x}} {1+e^{-2x}}

    特点:一是输出是0均值,二是易造成梯度消失,三是幂运算复杂,训练时间长

    1. Relu函数,tf.nn.reluf(x)=max(x,0)=\begin{cases} 0& \text{x<0}\\ x& \text{x>=0} \end{cases}

    优点:一是解决了梯度消失问题(在正区间),二是只需判断输入是否大于0,计算速度快,三是收敛速度远快于sigmoid和tanh

    缺点:一是输出非0均值,收敛慢,二是Dead Relu问题:也就是送入激活函数的输入特征是负数时,激活函数输出是0,反向传播得到的梯度也是0,导致参数无法更新,造成神经元死亡。造成神经元死亡的根本原因是经过relu函数的负数特征过多导致,我们可以改进随机初始化,避免过多的负数特征送入relu函数,可以通过设置更小的学习率,减少参数分布的巨大变化,避免训练中产生过多负数特征进入relu函数

    1. Leaky Relu函数tf.nn.leaky_relu(x),是为解决relu函数负区间为0造成神经元死亡而设计的 f(x)=max(\alpha x,x)=\begin{cases} \alpha x& \text{x<0}\\ x& \text{x>=0} \end{cases}

    对于初学者的建议

    1. 首选relu激活函数
    2. 学习率设置较小值
    3. 输入特征标准化,即让输入特征满足以0为均值,1为标准差的正态分布
    4. 初始参数中心化,即让随机生成的参数满足以0为均值,\sqrt{\frac {2}{当前层输入特征个数}}为标准差的正态分布

    损失函数(loss):预测值(y)与已知答案(y_)的差距

    NN优化目标:loss最小\Longrightarrow\begin{cases} mse均方差\\ 自定义\\ ce(Cross Entropy)交叉熵 \end{cases}

    import tensorflow as tf
    import numpy as np
    
    SEED = 23455
    
    rdm = np.random.RandomState(seed=SEED)  # 生成[0,1)之间的随机数
    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
    
    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.assign_sub(lr * grads)
    
        if epoch % 1000 == 0:
            print("After %d training steps,w1 is " % (epoch))
            print(w1.numpy(), "\n")
    print("Final w1 is: ", w1.numpy())
    
    After 0 training steps,w1 is 
    [[1.3587625 ]
     [0.54206395]] 
    
    After 1000 training steps,w1 is 
    [[1.2821434 ]
     [0.73504746]] 
    
    After 2000 training steps,w1 is 
    [[1.1956878]
     [0.8251068]] 
    
    After 3000 training steps,w1 is 
    [[1.1335756 ]
     [0.88120794]] 
    
    After 4000 training steps,w1 is 
    [[1.0913397 ]
     [0.91844666]] 
    
    After 5000 training steps,w1 is 
    [[1.0628718]
     [0.9434442]] 
    
    After 6000 training steps,w1 is 
    [[1.0437124]
     [0.9602566]] 
    
    After 7000 training steps,w1 is 
    [[1.0308208]
     [0.9715677]] 
    
    After 8000 training steps,w1 is 
    [[1.0221469]
     [0.9791779]] 
    
    After 9000 training steps,w1 is 
    [[1.0163113 ]
     [0.98429817]] 
    
    After 10000 training steps,w1 is 
    [[1.012385 ]
     [0.9877431]] 
    
    After 11000 training steps,w1 is 
    [[1.0097439]
     [0.9900609]] 
    
    After 12000 training steps,w1 is 
    [[1.0079672]
     [0.99162  ]] 
    
    After 13000 training steps,w1 is 
    [[1.0067729 ]
     [0.99266857]] 
    
    After 14000 training steps,w1 is 
    [[1.0059646]
     [0.9933737]] 
    
    Final w1 is:  [[1.0054297 ]
     [0.99384755]]
    
    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_ce1:",loss_ce2)
    
    loss_ce1: tf.Tensor(0.5108256, shape=(), dtype=float32)
    loss_ce1: tf.Tensor(0.22314353, shape=(), dtype=float32)
    

    我们在分类时,通常先使用softmax函数,让输出结果符合概率分布,再求交叉熵损失函数,tensorflow给出了一个可同时计算概率分布和交叉熵的函数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)
    

    欠拟合和过拟合

    欠拟合的解决方法:

    1. 增加输入特征项
    2. 增加网络参数
    3. 减少正则化参数

    过拟合的解决方法

    1. 数据清洗
    2. 增大训练集
    3. 采用正则化
    4. 增大正则化参数

    正则化缓解过拟合

    正则化在损失函数中引入模型复杂度指标,利用给w加权值,弱化训练数据的噪声(一般不正则化b)loss=loss(y与y\_)+REGULARIZER*loss(w)

    REGULARIZER指用超参数REGULARIZER给出参数w在总loss中的比例,即正则化的权重,w指需要正则化的参数loss_{L1}(w)=\sum_{i}|w_i|,loss_{L2}(w)=\sum_{i}|w_i^2|

    正则化的选择

    1. L1正则化大概率会使很多参数变为零,因此该方法可通过稀疏参数,即减少参数的数量,降低复杂度
    2. L2正则化会使参数很接近零但不为零,因此该方法可通过减小参数值的大小降低复杂度
    # 导入所需模块
    import tensorflow as tf
    from matplotlib import pyplot as plt
    import numpy as np
    import pandas as pd
    
    # 读入数据/标签 生成x_train y_train
    df = pd.read_csv('D:\\学习\\tensorflow2.0\\class2\\dot.csv')
    x_data = np.array(df[['x1', 'x2']])
    y_data = np.array(df['y_c'])
    
    x_train = np.vstack(x_data).reshape(-1,2)
    y_train = np.vstack(y_data).reshape(-1,1)
    
    Y_c = [['red' if y else 'blue'] for y in y_train]
    
    # 转换x的数据类型,否则后面矩阵相乘时会因数据类型问题报错
    x_train = tf.cast(x_train, tf.float32)
    y_train = tf.cast(y_train, tf.float32)
    
    # from_tensor_slices函数切分传入的张量的第一个维度,生成相应的数据集,使输入特征和标签值一一对应
    train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
    
    # 生成神经网络的参数,输入层为2个神经元,隐藏层为11个神经元,1层隐藏层,输出层为1个神经元
    # 用tf.Variable()保证参数可训练
    w1 = tf.Variable(tf.random.normal([2, 11]), dtype=tf.float32)
    b1 = tf.Variable(tf.constant(0.01, shape=[11]))
    
    w2 = tf.Variable(tf.random.normal([11, 1]), dtype=tf.float32)
    b2 = tf.Variable(tf.constant(0.01, shape=[1]))
    
    lr = 0.01  # 学习率
    epoch = 400  # 循环轮数
    
    # 训练部分
    for epoch in range(epoch):
        for step, (x_train, y_train) in enumerate(train_db):
            with tf.GradientTape() as tape:  # 记录梯度信息
    
                h1 = tf.matmul(x_train, w1) + b1  # 记录神经网络乘加运算
                h1 = tf.nn.relu(h1)
                y = tf.matmul(h1, w2) + b2
    
                # 采用均方误差损失函数mse = mean(sum(y-out)^2)
                loss = tf.reduce_mean(tf.square(y_train - y))
    
            # 计算loss对各个参数的梯度
            variables = [w1, b1, w2, b2]
            grads = tape.gradient(loss, variables)
    
            # 实现梯度更新
            # w1 = w1 - lr * w1_grad tape.gradient是自动求导结果与[w1, b1, w2, b2] 索引为0,1,2,3 
            w1.assign_sub(lr * grads[0])
            b1.assign_sub(lr * grads[1])
            w2.assign_sub(lr * grads[2])
            b2.assign_sub(lr * grads[3])
    
        # 每20个epoch,打印loss信息
        if epoch % 20 == 0:
            print('epoch:', epoch, 'loss:', float(loss))
    
    # 预测部分
    print("*******predict*******")
    # xx在-3到3之间以步长为0.01,yy在-3到3之间以步长0.01,生成间隔数值点
    xx, yy = np.mgrid[-3:3:.1, -3:3:.1]
    # 将xx , yy拉直,并合并配对为二维张量,生成二维坐标点
    grid = np.c_[xx.ravel(), yy.ravel()]
    grid = tf.cast(grid, tf.float32)
    # 将网格坐标点喂入神经网络,进行预测,probs为输出
    probs = []
    for x_test in grid:
        # 使用训练好的参数进行预测
        h1 = tf.matmul([x_test], w1) + b1
        h1 = tf.nn.relu(h1)
        y = tf.matmul(h1, w2) + b2  # y为预测结果
        probs.append(y)
    
    # 取第0列给x1,取第1列给x2
    x1 = x_data[:, 0]
    x2 = x_data[:, 1]
    # probs的shape调整成xx的样子
    probs = np.array(probs).reshape(xx.shape)
    plt.scatter(x1, x2, color=np.squeeze(Y_c)) #squeeze去掉纬度是1的纬度,相当于去掉[['red'],[''blue]],内层括号变为['red','blue']
    # 把坐标xx yy和对应的值probs放入contour<[‘kɑntʊr]>函数,给probs值为0.5的所有点上色  plt点show后 显示的是红蓝点的分界线
    plt.contour(xx, yy, probs, levels=[.5])
    plt.show()
    
    # 读入红蓝点,画出分割线,不包含正则化
    # 不清楚的数据,建议print出来查看 
    
    epoch: 0 loss: 0.5556482672691345
    epoch: 20 loss: 0.07622403651475906
    epoch: 40 loss: 0.04548833146691322
    epoch: 60 loss: 0.03665737062692642
    epoch: 80 loss: 0.03306785225868225
    epoch: 100 loss: 0.031198138371109962
    epoch: 120 loss: 0.030087297782301903
    epoch: 140 loss: 0.02931663580238819
    epoch: 160 loss: 0.02883256785571575
    epoch: 180 loss: 0.02845843695104122
    epoch: 200 loss: 0.028249353170394897
    epoch: 220 loss: 0.02800014056265354
    epoch: 240 loss: 0.027716094627976418
    epoch: 260 loss: 0.027561360970139503
    epoch: 280 loss: 0.027493348345160484
    epoch: 300 loss: 0.027485592290759087
    epoch: 320 loss: 0.027506008744239807
    epoch: 340 loss: 0.0275419894605875
    epoch: 360 loss: 0.02760850451886654
    epoch: 380 loss: 0.02770797163248062
    *******predict*******
    
    output_15_1.png
    # 导入所需模块
    import tensorflow as tf
    from matplotlib import pyplot as plt
    import numpy as np
    import pandas as pd
    
    # 读入数据/标签 生成x_train y_train
    df = pd.read_csv('D:\\学习\\tensorflow2.0\\class2\\dot.csv')
    x_data = np.array(df[['x1', 'x2']])
    y_data = np.array(df['y_c'])
    
    x_train = x_data
    y_train = y_data.reshape(-1, 1)
    
    Y_c = [['red' if y else 'blue'] for y in y_train]
    
    # 转换x的数据类型,否则后面矩阵相乘时会因数据类型问题报错
    x_train = tf.cast(x_train, tf.float32)
    y_train = tf.cast(y_train, tf.float32)
    
    # from_tensor_slices函数切分传入的张量的第一个维度,生成相应的数据集,使输入特征和标签值一一对应
    train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
    
    # 生成神经网络的参数,输入层为4个神经元,隐藏层为32个神经元,2层隐藏层,输出层为3个神经元
    # 用tf.Variable()保证参数可训练
    w1 = tf.Variable(tf.random.normal([2, 11]), dtype=tf.float32)
    b1 = tf.Variable(tf.constant(0.01, shape=[11]))
    
    w2 = tf.Variable(tf.random.normal([11, 1]), dtype=tf.float32)
    b2 = tf.Variable(tf.constant(0.01, shape=[1]))
    
    lr = 0.01  # 学习率为
    epoch = 400  # 循环轮数
    
    # 训练部分
    for epoch in range(epoch):
        for step, (x_train, y_train) in enumerate(train_db):
            with tf.GradientTape() as tape:  # 记录梯度信息
    
                h1 = tf.matmul(x_train, w1) + b1  # 记录神经网络乘加运算
                h1 = tf.nn.relu(h1)
                y = tf.matmul(h1, w2) + b2
    
                # 采用均方误差损失函数mse = mean(sum(y-out)^2)
                loss_mse = tf.reduce_mean(tf.square(y_train - y))
                # 添加l2正则化
                loss_regularization = []
                # tf.nn.l2_loss(w)=sum(w ** 2) / 2
                loss_regularization.append(tf.nn.l2_loss(w1))
                loss_regularization.append(tf.nn.l2_loss(w2))
                # 求和
                # 例:x=tf.constant(([1,1,1],[1,1,1]))
                #   tf.reduce_sum(x)
                # >>>6
                # loss_regularization = tf.reduce_sum(tf.stack(loss_regularization))
                loss_regularization = tf.reduce_sum(loss_regularization)
                loss = loss_mse + 0.03 * loss_regularization #REGULARIZER = 0.03
    
            # 计算loss对各个参数的梯度
            variables = [w1, b1, w2, b2]
            grads = tape.gradient(loss, variables)
    
            # 实现梯度更新
            # w1 = w1 - lr * w1_grad
            w1.assign_sub(lr * grads[0])
            b1.assign_sub(lr * grads[1])
            w2.assign_sub(lr * grads[2])
            b2.assign_sub(lr * grads[3])
    
        # 每200个epoch,打印loss信息
        if epoch % 20 == 0:
            print('epoch:', epoch, 'loss:', float(loss))
    
    # 预测部分
    print("*******predict*******")
    # xx在-3到3之间以步长为0.01,yy在-3到3之间以步长0.01,生成间隔数值点
    xx, yy = np.mgrid[-3:3:.1, -3:3:.1]
    # 将xx, yy拉直,并合并配对为二维张量,生成二维坐标点
    grid = np.c_[xx.ravel(), yy.ravel()]
    grid = tf.cast(grid, tf.float32)
    # 将网格坐标点喂入神经网络,进行预测,probs为输出
    probs = []
    for x_predict in grid:
        # 使用训练好的参数进行预测
        h1 = tf.matmul([x_predict], w1) + b1
        h1 = tf.nn.relu(h1)
        y = tf.matmul(h1, w2) + b2  # y为预测结果
        probs.append(y)
    
    # 取第0列给x1,取第1列给x2
    x1 = x_data[:, 0]
    x2 = x_data[:, 1]
    # probs的shape调整成xx的样子
    probs = np.array(probs).reshape(xx.shape)
    plt.scatter(x1, x2, color=np.squeeze(Y_c))
    # 把坐标xx yy和对应的值probs放入contour<[‘kɑntʊr]>函数,给probs值为0.5的所有点上色  plt点show后 显示的是红蓝点的分界线
    plt.contour(xx, yy, probs, levels=[.5])
    plt.show()
    
    # 读入红蓝点,画出分割线,包含正则化
    # 不清楚的数据,建议print出来查看 
    
    epoch: 0 loss: 2.0484209060668945
    epoch: 20 loss: 0.4872018098831177
    epoch: 40 loss: 0.40399590134620667
    epoch: 60 loss: 0.35554954409599304
    epoch: 80 loss: 0.315273255109787
    epoch: 100 loss: 0.28194287419319153
    epoch: 120 loss: 0.25264081358909607
    epoch: 140 loss: 0.22514598071575165
    epoch: 160 loss: 0.20261871814727783
    epoch: 180 loss: 0.18463000655174255
    epoch: 200 loss: 0.16953137516975403
    epoch: 220 loss: 0.1566954106092453
    epoch: 240 loss: 0.1458052396774292
    epoch: 260 loss: 0.13634726405143738
    epoch: 280 loss: 0.12818694114685059
    epoch: 300 loss: 0.12089003622531891
    epoch: 320 loss: 0.11462999880313873
    epoch: 340 loss: 0.10939529538154602
    epoch: 360 loss: 0.10491353273391724
    epoch: 380 loss: 0.10086023807525635
    *******predict*******
    
    output_16_1.png

    神经网络参数优化器

    待优化参数w,损失函数Loss,学习率Ir,每次迭代一个batch,t表示当前batch迭代的总次数

    1. 计算t时刻损失函数关于当前参数的梯度g_t= \triangledown loss= \frac {\partial loss} {\partial (w_t)}
    2. 计算t时刻一阶动量m_t和二阶动量V_t
    3. 计算t时刻下降梯度: \eta_t=lr \cdot m_t / \sqrt{V_t}
    4. 计算t+1时刻参数:w_{t+1}=w_t-\eta_t=w_t-lr \cdot m_t / \sqrt{V_t}

    一阶动量:与梯度相关的函数

    二阶动量:与梯度平方相关的函数

    不同的优化器实质上只是定义了不同的一阶动量和二阶动量公式

    随机梯度下降

    • SGD,常用的梯度下降法
      m_t=g_t,V_t=1,\eta=lr \cdot m_t / \sqrt{V_t}=lr\cdot g_t
      w_{t+1}=w_t-\eta_t=w_t-lr \cdot m_t / \sqrt{V_t}=w_t-lr\cdot g_t
      w_{t+1}=w_t-lr * \frac{\partial loss}{\partial w_t}
    # 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线
    
    # 导入所需模块
    import tensorflow as tf
    from sklearn import datasets
    from matplotlib import pyplot as plt
    import numpy as np
    import time  ##1##
    
    # 导入数据,分别为输入特征和标签
    x_data = datasets.load_iris().data
    y_data = datasets.load_iris().target
    
    # 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)
    # seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)
    np.random.seed(116)  # 使用相同的seed,保证输入特征和标签一一对应
    np.random.shuffle(x_data)
    np.random.seed(116)
    np.random.shuffle(y_data)
    tf.random.set_seed(116)
    
    # 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行
    x_train = x_data[:-30]
    y_train = y_data[:-30]
    x_test = x_data[-30:]
    y_test = y_data[-30:]
    
    # 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
    x_train = tf.cast(x_train, tf.float32)
    x_test = tf.cast(x_test, tf.float32)
    
    # from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次batch组数据)
    train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
    test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
    
    # 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
    # 用tf.Variable()标记参数可训练
    # 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)
    w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
    b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
    
    lr = 0.1  # 学习率为0.1
    train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
    test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
    epoch = 500  # 循环500轮
    loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和
    
    # 训练部分
    now_time = time.time()  ##2##
    for epoch in range(epoch):  # 数据集级别的循环,每个epoch循环一次数据集
        for step, (x_train, y_train) in enumerate(train_db):  # batch级别的循环 ,每个step循环一个batch
            with tf.GradientTape() as tape:  # with结构记录梯度信息
                y = tf.matmul(x_train, w1) + b1  # 神经网络乘加运算
                y = tf.nn.softmax(y)  # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)
                y_ = tf.one_hot(y_train, depth=3)  # 将标签值转换为独热码格式,方便计算loss和accuracy
                loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方误差损失函数mse = mean(sum(y-out)^2)
                loss_all += loss.numpy()  # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
            # 计算loss对各个参数的梯度
            grads = tape.gradient(loss, [w1, b1])
    
            # 实现梯度更新 w1 = w1 - lr * w1_grad    b = b - lr * b_grad
            w1.assign_sub(lr * grads[0])  # 参数w1自更新
            b1.assign_sub(lr * grads[1])  # 参数b自更新
    
        # 每个epoch,打印loss信息
        print("Epoch {}, loss: {}".format(epoch, loss_all / 4))
        train_loss_results.append(loss_all / 4)  # 将4个step的loss求平均记录在此变量中
        loss_all = 0  # loss_all归零,为记录下一个epoch的loss做准备
    
        # 测试部分
        # total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0
        total_correct, total_number = 0, 0
        for x_test, y_test in test_db:
            # 使用更新后的参数进行预测
            y = tf.matmul(x_test, w1) + b1
            y = tf.nn.softmax(y)
            pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即预测的分类
            # 将pred转换为y_test的数据类型
            pred = tf.cast(pred, dtype=y_test.dtype)
            # 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
            correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
            # 将每个batch的correct数加起来
            correct = tf.reduce_sum(correct)
            # 将所有batch中的correct数加起来
            total_correct += int(correct)
            # total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数
            total_number += x_test.shape[0]
        # 总的准确率等于total_correct/total_number
        acc = total_correct / total_number
        test_acc.append(acc)
        print("Test_acc:", acc)
        print("--------------------------")
    total_time = time.time() - now_time  ##3##
    print("total_time", total_time)  ##4##
    
    # 绘制 loss 曲线
    plt.title('Loss Function Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Loss')  # y轴变量名称
    plt.plot(train_loss_results, label="$Loss$")  # 逐点画出trian_loss_results值并连线,连线图标是Loss
    plt.legend()  # 画出曲线图标
    plt.show()  # 画出图像
    
    # 绘制 Accuracy 曲线
    plt.title('Acc Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Acc')  # y轴变量名称
    plt.plot(test_acc, label="$Accuracy$")  # 逐点画出test_acc值并连线,连线图标是Accuracy
    plt.legend()
    plt.show()
    
    # 本文件较 class1\p45_iris.py 仅添加四处时间记录  用 ##n## 标识
    # 请将loss曲线、ACC曲线、total_time记录到 class2\优化器对比.docx  对比各优化器收敛情况
    
    Epoch 0, loss: 0.2821310982108116
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 1, loss: 0.25459614396095276
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 2, loss: 0.22570250555872917
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 3, loss: 0.21028399839997292
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 4, loss: 0.19942265003919601
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 5, loss: 0.18873637542128563
    Test_acc: 0.5
    
    --------------------------
    Epoch 496, loss: 0.03237855713814497
    Test_acc: 1.0
    --------------------------
    Epoch 497, loss: 0.03235237207263708
    Test_acc: 1.0
    --------------------------
    Epoch 498, loss: 0.03232626663520932
    Test_acc: 1.0
    --------------------------
    Epoch 499, loss: 0.032300274819135666
    Test_acc: 1.0
    --------------------------
    total_time 13.266215801239014
    
    output_18_1.png output_18_2.png

    SGDM(含momentum的SGD),在SGD基础上增加一阶动量

    • m_t=\beta \cdot m_{t-1} + (1-\beta)\cdot g_t, V_t=1
    • \eta_t=lr\cdot m_t / \sqrt{V_t}=lr\cdot m_t=lr\cdot (\beta\cdot m_{t-1}+(1-\beta)\cdot g_t)
    • w_{t+1}=w_t-\eta_t=w_t-lr\cdot (\beta\cdot m_{t-1}+(1-\beta)\cdot g_t)
    # 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线
    
    # 导入所需模块
    import tensorflow as tf
    from sklearn import datasets
    from matplotlib import pyplot as plt
    import numpy as np
    import time  ##1##
    
    # 导入数据,分别为输入特征和标签
    x_data = datasets.load_iris().data
    y_data = datasets.load_iris().target
    
    # 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)
    # seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)
    np.random.seed(116)  # 使用相同的seed,保证输入特征和标签一一对应
    np.random.shuffle(x_data)
    np.random.seed(116)
    np.random.shuffle(y_data)
    tf.random.set_seed(116)
    
    # 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行
    x_train = x_data[:-30]
    y_train = y_data[:-30]
    x_test = x_data[-30:]
    y_test = y_data[-30:]
    
    # 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
    x_train = tf.cast(x_train, tf.float32)
    x_test = tf.cast(x_test, tf.float32)
    
    # from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次batch组数据)
    train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
    test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
    
    # 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
    # 用tf.Variable()标记参数可训练
    # 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)
    w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
    b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
    
    lr = 0.1  # 学习率为0.1
    train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
    test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
    epoch = 500  # 循环500轮
    loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和
    
    ##########################################################################
    m_w, m_b = 0, 0
    beta = 0.9
    ##########################################################################
    
    # 训练部分
    now_time = time.time()  ##2##
    for epoch in range(epoch):  # 数据集级别的循环,每个epoch循环一次数据集
        for step, (x_train, y_train) in enumerate(train_db):  # batch级别的循环 ,每个step循环一个batch
            with tf.GradientTape() as tape:  # with结构记录梯度信息
                y = tf.matmul(x_train, w1) + b1  # 神经网络乘加运算
                y = tf.nn.softmax(y)  # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)
                y_ = tf.one_hot(y_train, depth=3)  # 将标签值转换为独热码格式,方便计算loss和accuracy
                loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方误差损失函数mse = mean(sum(y-out)^2)
                loss_all += loss.numpy()  # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
            # 计算loss对各个参数的梯度
            grads = tape.gradient(loss, [w1, b1])
    
            ##########################################################################
            # sgd-momentun  
            m_w = beta * m_w + (1 - beta) * grads[0]
            m_b = beta * m_b + (1 - beta) * grads[1]
            w1.assign_sub(lr * m_w)
            b1.assign_sub(lr * m_b)
        ##########################################################################
    
        # 每个epoch,打印loss信息
        print("Epoch {}, loss: {}".format(epoch, loss_all / 4))
        train_loss_results.append(loss_all / 4)  # 将4个step的loss求平均记录在此变量中
        loss_all = 0  # loss_all归零,为记录下一个epoch的loss做准备
    
        # 测试部分
        # total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0
        total_correct, total_number = 0, 0
        for x_test, y_test in test_db:
            # 使用更新后的参数进行预测
            y = tf.matmul(x_test, w1) + b1
            y = tf.nn.softmax(y)
            pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即预测的分类
            # 将pred转换为y_test的数据类型
            pred = tf.cast(pred, dtype=y_test.dtype)
            # 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
            correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
            # 将每个batch的correct数加起来
            correct = tf.reduce_sum(correct)
            # 将所有batch中的correct数加起来
            total_correct += int(correct)
            # total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数
            total_number += x_test.shape[0]
        # 总的准确率等于total_correct/total_number
        acc = total_correct / total_number
        test_acc.append(acc)
        print("Test_acc:", acc)
        print("--------------------------")
    total_time = time.time() - now_time  ##3##
    print("total_time", total_time)  ##4##
    
    # 绘制 loss 曲线
    plt.title('Loss Function Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Loss')  # y轴变量名称
    plt.plot(train_loss_results, label="$Loss$")  # 逐点画出trian_loss_results值并连线,连线图标是Loss
    plt.legend()  # 画出曲线图标
    plt.show()  # 画出图像
    
    # 绘制 Accuracy 曲线
    plt.title('Acc Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Acc')  # y轴变量名称
    plt.plot(test_acc, label="$Accuracy$")  # 逐点画出test_acc值并连线,连线图标是Accuracy
    plt.legend()
    plt.show()
    
    # 请将loss曲线、ACC曲线、total_time记录到 class2\优化器对比.docx  对比各优化器收敛情况
    
    Epoch 0, loss: 0.2961867228150368
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 1, loss: 0.28081151843070984
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 2, loss: 0.26392311602830887
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 3, loss: 0.2419254034757614
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 4, loss: 0.2185598649084568
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 5, loss: 0.20465285703539848
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 496, loss: 0.030705393757671118
    Test_acc: 1.0
    --------------------------
    Epoch 497, loss: 0.03068035701289773
    Test_acc: 1.0
    --------------------------
    Epoch 498, loss: 0.03065542411059141
    Test_acc: 1.0
    --------------------------
    Epoch 499, loss: 0.03063057456165552
    Test_acc: 1.0
    --------------------------
    total_time 13.602926254272461
    
    output_20_1.png output_20_2.png

    Adagrad,在SGD基础上增加二阶动量

    • m_t=g_t,V_t=\sum^{t}_{\tau=1}g^{2}_{\tau}
    • \eta_t=lr\cdot m_t /(\sqrt{V_t})=lr\cdot g_t /(\sqrt{\sum^{t}_{\tau=1}g^{2}_{\tau}})
    • w_{t+1}=w_t-\eta_t=w_t-lt\cdot g_t /(\sqrt{\sum^{t}_{\tau=1}g^{2}_{\tau}})
    # 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线
    
    # 导入所需模块
    import tensorflow as tf
    from sklearn import datasets
    from matplotlib import pyplot as plt
    import numpy as np
    import time  ##1##
    
    # 导入数据,分别为输入特征和标签
    x_data = datasets.load_iris().data
    y_data = datasets.load_iris().target
    
    # 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)
    # seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)
    np.random.seed(116)  # 使用相同的seed,保证输入特征和标签一一对应
    np.random.shuffle(x_data)
    np.random.seed(116)
    np.random.shuffle(y_data)
    tf.random.set_seed(116)
    
    # 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行
    x_train = x_data[:-30]
    y_train = y_data[:-30]
    x_test = x_data[-30:]
    y_test = y_data[-30:]
    
    # 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
    x_train = tf.cast(x_train, tf.float32)
    x_test = tf.cast(x_test, tf.float32)
    
    # from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次batch组数据)
    train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
    test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
    
    # 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
    # 用tf.Variable()标记参数可训练
    # 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)
    w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
    b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
    
    lr = 0.1  # 学习率为0.1
    train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
    test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
    epoch = 500  # 循环500轮
    loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和
    
    ##########################################################################
    v_w, v_b = 0, 0
    ##########################################################################
    
    # 训练部分
    now_time = time.time()  ##2##
    for epoch in range(epoch):  # 数据集级别的循环,每个epoch循环一次数据集
        for step, (x_train, y_train) in enumerate(train_db):  # batch级别的循环 ,每个step循环一个batch
            with tf.GradientTape() as tape:  # with结构记录梯度信息
                y = tf.matmul(x_train, w1) + b1  # 神经网络乘加运算
                y = tf.nn.softmax(y)  # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)
                y_ = tf.one_hot(y_train, depth=3)  # 将标签值转换为独热码格式,方便计算loss和accuracy
                loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方误差损失函数mse = mean(sum(y-out)^2)
                loss_all += loss.numpy()  # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
            # 计算loss对各个参数的梯度
            grads = tape.gradient(loss, [w1, b1])
    
            ##########################################################################
            # adagrad
            v_w += tf.square(grads[0])
            v_b += tf.square(grads[1])
            w1.assign_sub(lr * grads[0] / tf.sqrt(v_w))
            b1.assign_sub(lr * grads[1] / tf.sqrt(v_b))
        ##########################################################################
    
        # 每个epoch,打印loss信息
        print("Epoch {}, loss: {}".format(epoch, loss_all / 4))
        train_loss_results.append(loss_all / 4)  # 将4个step的loss求平均记录在此变量中
        loss_all = 0  # loss_all归零,为记录下一个epoch的loss做准备
    
        # 测试部分
        # total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0
        total_correct, total_number = 0, 0
        for x_test, y_test in test_db:
            # 使用更新后的参数进行预测
            y = tf.matmul(x_test, w1) + b1
            y = tf.nn.softmax(y)
            pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即预测的分类
            # 将pred转换为y_test的数据类型
            pred = tf.cast(pred, dtype=y_test.dtype)
            # 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
            correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
            # 将每个batch的correct数加起来
            correct = tf.reduce_sum(correct)
            # 将所有batch中的correct数加起来
            total_correct += int(correct)
            # total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数
            total_number += x_test.shape[0]
        # 总的准确率等于total_correct/total_number
        acc = total_correct / total_number
        test_acc.append(acc)
        print("Test_acc:", acc)
        print("--------------------------")
    total_time = time.time() - now_time  ##3##
    print("total_time", total_time)  ##4##
    
    # 绘制 loss 曲线
    plt.title('Loss Function Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Loss')  # y轴变量名称
    plt.plot(train_loss_results, label="$Loss$")  # 逐点画出trian_loss_results值并连线,连线图标是Loss
    plt.legend()  # 画出曲线图标
    plt.show()  # 画出图像
    
    # 绘制 Accuracy 曲线
    plt.title('Acc Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Acc')  # y轴变量名称
    plt.plot(test_acc, label="$Accuracy$")  # 逐点画出test_acc值并连线,连线图标是Accuracy
    plt.legend()
    plt.show()
    
    # 请将loss曲线、ACC曲线、total_time记录到 class2\优化器对比.docx  对比各优化器收敛情况
    
    Epoch 0, loss: 0.2528788596391678
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 1, loss: 0.17821526527404785
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 2, loss: 0.15621442720294
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 3, loss: 0.1420477293431759
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 4, loss: 0.13362514600157738
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 5, loss: 0.12754761427640915
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 496, loss: 0.031801939476281404
    Test_acc: 1.0
    --------------------------
    Epoch 497, loss: 0.03178095258772373
    Test_acc: 1.0
    --------------------------
    Epoch 498, loss: 0.03176002623513341
    Test_acc: 1.0
    --------------------------
    Epoch 499, loss: 0.03173917252570391
    Test_acc: 1.0
    --------------------------
    total_time 13.258378505706787
    
    output_22_1.png output_22_2.png

    RMSProp,SGD基础上增加二阶动量

    • m_t=g_t,V_t=\beta\cdot V_{t-1}+(1-\beta)\cdot g^{2}_{t}
    • \eta_t=lr\cdot m_t /(\sqrt{V_t})=lr\cdot g_t /(\sqrt{\beta\cdot V_{t-1}+(1-\beta)g^{2}_{t}})
    • w_{t+1}=w_t-\eta_t=w_t-lr\cdot g_t /(\sqrt{\beta\cdot V_{t-1}+(1-\beta)g^{2}_{t}})
    # 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线
    
    # 导入所需模块
    import tensorflow as tf
    from sklearn import datasets
    from matplotlib import pyplot as plt
    import numpy as np
    import time  ##1##
    
    # 导入数据,分别为输入特征和标签
    x_data = datasets.load_iris().data
    y_data = datasets.load_iris().target
    
    # 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)
    # seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)
    np.random.seed(116)  # 使用相同的seed,保证输入特征和标签一一对应
    np.random.shuffle(x_data)
    np.random.seed(116)
    np.random.shuffle(y_data)
    tf.random.set_seed(116)
    
    # 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行
    x_train = x_data[:-30]
    y_train = y_data[:-30]
    x_test = x_data[-30:]
    y_test = y_data[-30:]
    
    # 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
    x_train = tf.cast(x_train, tf.float32)
    x_test = tf.cast(x_test, tf.float32)
    
    # from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次batch组数据)
    train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
    test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
    
    # 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
    # 用tf.Variable()标记参数可训练
    # 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)
    w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
    b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
    
    lr = 0.01  # 学习率为0.1
    train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
    test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
    epoch = 500  # 循环500轮
    loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和
    
    ##########################################################################
    v_w, v_b = 0, 0
    beta = 0.9
    ##########################################################################
    
    # 训练部分
    now_time = time.time()  ##2##
    for epoch in range(epoch):  # 数据集级别的循环,每个epoch循环一次数据集
        for step, (x_train, y_train) in enumerate(train_db):  # batch级别的循环 ,每个step循环一个batch
            with tf.GradientTape() as tape:  # with结构记录梯度信息
                y = tf.matmul(x_train, w1) + b1  # 神经网络乘加运算
                y = tf.nn.softmax(y)  # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)
                y_ = tf.one_hot(y_train, depth=3)  # 将标签值转换为独热码格式,方便计算loss和accuracy
                loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方误差损失函数mse = mean(sum(y-out)^2)
                loss_all += loss.numpy()  # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
            # 计算loss对各个参数的梯度
            grads = tape.gradient(loss, [w1, b1])
    
            ##########################################################################
            # rmsprop
            v_w = beta * v_w + (1 - beta) * tf.square(grads[0])
            v_b = beta * v_b + (1 - beta) * tf.square(grads[1])
            w1.assign_sub(lr * grads[0] / tf.sqrt(v_w))
            b1.assign_sub(lr * grads[1] / tf.sqrt(v_b))
        ##########################################################################
    
        # 每个epoch,打印loss信息
        print("Epoch {}, loss: {}".format(epoch, loss_all / 4))
        train_loss_results.append(loss_all / 4)  # 将4个step的loss求平均记录在此变量中
        loss_all = 0  # loss_all归零,为记录下一个epoch的loss做准备
    
        # 测试部分
        # total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0
        total_correct, total_number = 0, 0
        for x_test, y_test in test_db:
            # 使用更新后的参数进行预测
            y = tf.matmul(x_test, w1) + b1
            y = tf.nn.softmax(y)
            pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即预测的分类
            # 将pred转换为y_test的数据类型
            pred = tf.cast(pred, dtype=y_test.dtype)
            # 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
            correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
            # 将每个batch的correct数加起来
            correct = tf.reduce_sum(correct)
            # 将所有batch中的correct数加起来
            total_correct += int(correct)
            # total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数
            total_number += x_test.shape[0]
        # 总的准确率等于total_correct/total_number
        acc = total_correct / total_number
        test_acc.append(acc)
        print("Test_acc:", acc)
        print("--------------------------")
    total_time = time.time() - now_time  ##3##
    print("total_time", total_time)  ##4##
    
    # 绘制 loss 曲线
    plt.title('Loss Function Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Loss')  # y轴变量名称
    plt.plot(train_loss_results, label="$Loss$")  # 逐点画出trian_loss_results值并连线,连线图标是Loss
    plt.legend()  # 画出曲线图标
    plt.show()  # 画出图像
    
    # 绘制 Accuracy 曲线
    plt.title('Acc Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Acc')  # y轴变量名称
    plt.plot(test_acc, label="$Accuracy$")  # 逐点画出test_acc值并连线,连线图标是Accuracy
    plt.legend()
    plt.show()
    
    # 请将loss曲线、ACC曲线、total_time记录到 class2\优化器对比.docx  对比各优化器收敛情况
    
    Epoch 0, loss: 0.2614312767982483
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 1, loss: 0.2232961319386959
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 2, loss: 0.20800380781292915
    Test_acc: 0.16666666666666666
    --------------------------
    Epoch 3, loss: 0.1955469585955143
    Test_acc: 0.2
    --------------------------
    Epoch 4, loss: 0.183865737169981
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 5, loss: 0.17326249554753304
    Test_acc: 0.5333333333333333
    
    --------------------------
    Epoch 496, loss: 0.02189745125360787
    Test_acc: 1.0
    --------------------------
    Epoch 497, loss: 0.02188444253988564
    Test_acc: 1.0
    --------------------------
    Epoch 498, loss: 0.021871474804356694
    Test_acc: 1.0
    --------------------------
    Epoch 499, loss: 0.02185854851268232
    Test_acc: 1.0
    --------------------------
    total_time 13.637284278869629
    
    output_24_1.png output_24_2.png

    Adam,同时结合SGDM一阶动量和RMSProp二阶动量

    • m_t=\beta_1\cdot m_{t-1}+(1-\beta_1)\cdot g_t
    • 修正一阶动量的偏差:\hat{m_t}=\frac {m_1}{1-\beta^{t}_{1}}
    • V_t=\beta_2\cdot V_{step-1}+(1-\beta_2)\cdot g^{2}_{t}
    • 修正二阶动量的偏差:\hat{V_t}=\frac {V_t}{1-\beta^{t}_{2}}
    • \eta_t=lr\cdot \hat{m_t}/sqrt{\hat{V_t}}=lr\cdot \frac{m_t}{1-\beta^{t}_{1}}/ \sqrt{\frac {V_t}{1-\beta^{t}_{2}}}
    • w_{t+1}=w_t-\eta_t=w_t-lr\cdot \frac{m_t}{1-\beta^{t}_{1}}/ \sqrt{\frac {V_t}{1-\beta^{t}_{2}}}
    # 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线
    
    # 导入所需模块
    import tensorflow as tf
    from sklearn import datasets
    from matplotlib import pyplot as plt
    import numpy as np
    import time  ##1##
    
    # 导入数据,分别为输入特征和标签
    x_data = datasets.load_iris().data
    y_data = datasets.load_iris().target
    
    # 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)
    # seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)
    np.random.seed(116)  # 使用相同的seed,保证输入特征和标签一一对应
    np.random.shuffle(x_data)
    np.random.seed(116)
    np.random.shuffle(y_data)
    tf.random.set_seed(116)
    
    # 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行
    x_train = x_data[:-30]
    y_train = y_data[:-30]
    x_test = x_data[-30:]
    y_test = y_data[-30:]
    
    # 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
    x_train = tf.cast(x_train, tf.float32)
    x_test = tf.cast(x_test, tf.float32)
    
    # from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次batch组数据)
    train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
    test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
    
    # 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
    # 用tf.Variable()标记参数可训练
    # 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)
    w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
    b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
    
    lr = 0.1  # 学习率为0.1
    train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
    test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
    epoch = 500  # 循环500轮
    loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和
    
    ##########################################################################
    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
    ##########################################################################
    
    # 训练部分
    now_time = time.time()  ##2##
    for epoch in range(epoch):  # 数据集级别的循环,每个epoch循环一次数据集
        for step, (x_train, y_train) in enumerate(train_db):  # batch级别的循环 ,每个step循环一个batch
     ##########################################################################       
            global_step += 1
     ##########################################################################       
            with tf.GradientTape() as tape:  # with结构记录梯度信息
                y = tf.matmul(x_train, w1) + b1  # 神经网络乘加运算
                y = tf.nn.softmax(y)  # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)
                y_ = tf.one_hot(y_train, depth=3)  # 将标签值转换为独热码格式,方便计算loss和accuracy
                loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方误差损失函数mse = mean(sum(y-out)^2)
                loss_all += loss.numpy()  # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
            # 计算loss对各个参数的梯度
            grads = tape.gradient(loss, [w1, b1])
    
    ##########################################################################
     # adam
            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))
    ##########################################################################
    
        # 每个epoch,打印loss信息
        print("Epoch {}, loss: {}".format(epoch, loss_all / 4))
        train_loss_results.append(loss_all / 4)  # 将4个step的loss求平均记录在此变量中
        loss_all = 0  # loss_all归零,为记录下一个epoch的loss做准备
    
        # 测试部分
        # total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0
        total_correct, total_number = 0, 0
        for x_test, y_test in test_db:
            # 使用更新后的参数进行预测
            y = tf.matmul(x_test, w1) + b1
            y = tf.nn.softmax(y)
            pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即预测的分类
            # 将pred转换为y_test的数据类型
            pred = tf.cast(pred, dtype=y_test.dtype)
            # 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
            correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
            # 将每个batch的correct数加起来
            correct = tf.reduce_sum(correct)
            # 将所有batch中的correct数加起来
            total_correct += int(correct)
            # total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数
            total_number += x_test.shape[0]
        # 总的准确率等于total_correct/total_number
        acc = total_correct / total_number
        test_acc.append(acc)
        print("Test_acc:", acc)
        print("--------------------------")
    total_time = time.time() - now_time  ##3##
    print("total_time", total_time)  ##4##
    
    # 绘制 loss 曲线
    plt.title('Loss Function Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Loss')  # y轴变量名称
    plt.plot(train_loss_results, label="$Loss$")  # 逐点画出trian_loss_results值并连线,连线图标是Loss
    plt.legend()  # 画出曲线图标
    plt.show()  # 画出图像
    
    # 绘制 Accuracy 曲线
    plt.title('Acc Curve')  # 图片标题
    plt.xlabel('Epoch')  # x轴变量名称
    plt.ylabel('Acc')  # y轴变量名称
    plt.plot(test_acc, label="$Accuracy$")  # 逐点画出test_acc值并连线,连线图标是Accuracy
    plt.legend()
    plt.show()
    
    # 请将loss曲线、ACC曲线、total_time记录到 class2\优化器对比.docx  对比各优化器收敛情况
    
    Epoch 0, loss: 0.2198411151766777
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 1, loss: 0.14480535127222538
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 2, loss: 0.10274325869977474
    Test_acc: 0.6666666666666666
    --------------------------
    Epoch 3, loss: 0.08922175876796246
    Test_acc: 0.5333333333333333
    --------------------------
    Epoch 4, loss: 0.08600811939686537
    Test_acc: 0.9
    --------------------------
    Epoch 5, loss: 0.06994965020567179
    Test_acc: 0.8
    --------------------------
    Epoch 6, loss: 0.06724501121789217
    Test_acc: 0.8
    --------------------------
    Epoch 7, loss: 0.06104531418532133
    Test_acc: 1.0
    --------------------------
    Epoch 8, loss: 0.05573826748877764
    Test_acc: 0.9333333333333333
    --------------------------
    Epoch 9, loss: 0.05405295733362436
    Test_acc: 1.0
    --------------------------
    Epoch 489, loss: 0.013960019452497363
    Test_acc: 1.0
    --------------------------
    Epoch 490, loss: 0.01395658147521317
    Test_acc: 1.0
    --------------------------
    
    Epoch 496, loss: 0.01393622939940542
    Test_acc: 1.0
    --------------------------
    Epoch 497, loss: 0.013932880363427103
    Test_acc: 1.0
    --------------------------
    Epoch 498, loss: 0.013929550303146243
    Test_acc: 1.0
    --------------------------
    Epoch 499, loss: 0.013926220242865384
    Test_acc: 1.0
    --------------------------
    total_time 16.333426475524902
    
    output_26_2.png output_26_1.png

    相关文章

      网友评论

          本文标题:预备知识及优化器

          本文链接:https://www.haomeiwen.com/subject/oucwyktx.html