端智能入门 - 模型训练

导读

以监督学习为例,了解常用的端智能模型是如何产生的,也就是如何对神经网络模型进行训练。模型训练是指利用有标签的样本数据,通过调整参数,达成目标预测的过程。神经网络在进行初始化时,其参数一般是随机的,如果直接将数据输入到这样的模型,那么它预测的结果也是随机的、是不具有参考意义的。因此我们就需要通过一系列模型参数优化的手段,将这些参数逐步调整为合适的数值,从而让模型预测的结果变得越来越可靠。总体来说,模型训练主要分为以下步骤:

基础知识

在开始介绍整体的模型训练流程之前,我们可以先了解一下关于模型参数优化的原理:也就是神经网络模型如何使用样本数据获得预测结果(前向计算 ),并将其与真实结果进行误差计算(损失函数计算 ),再把该误差逐层进行反向传播 ,从而利用梯度下降的方式来实现模型参数的调优。

前向计算

前向计算或者叫前向传播,指的是按顺序(从输入层到输出层)计算和存储神经网络中每层的结果。一个简单神经网络的结构如下图所示:

  • 假设输入模型的样本为 <math xmlns="http://www.w3.org/1998/Math/MathML"> x ∈ R d \boldsymbol{x} \in R^d </math>x ∈ Rd,将隐藏层的权重参数表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> W ( 1 ) \boldsymbol{W^{(1)}} </math>W(1),偏置表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> b ( 1 ) b^{(1)} </math>b(1),那么我们可以通过矩阵运算获得中间变量:

    <math xmlns="http://www.w3.org/1998/Math/MathML"> z i n = W ( 1 ) x + b ( 1 ) \boldsymbol{z^{in}} = \boldsymbol{W^{(1)}}\boldsymbol{x} + b^{(1)} </math>zin = W(1)x + b(1)

  • 将输出层的权重参数表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> W ( 2 ) \boldsymbol{W^{(2)}} </math>W(2),偏置表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> b ( 2 ) b^{(2)} </math>b(2),同理可获得输出为:

    <math xmlns="http://www.w3.org/1998/Math/MathML"> z o u t = W ( 2 ) z i n + b ( 2 ) \boldsymbol{z^{out}} = \boldsymbol{W^{(2)}}\boldsymbol{z^{in}} + b^{(2)} </math>zout = W(2)zin + b(2)

  • 假设还需要经过激活函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> ϕ \phi </math>ϕ才可获得最终的输出结果:

    <math xmlns="http://www.w3.org/1998/Math/MathML"> o = ϕ ( z o u t ) \boldsymbol{o} = \phi(\boldsymbol{z^{out}}) </math>o = ϕ(zout)

  • 流程图:

损失函数

损失函数(loss function)是用来计算神经网络前向计算的结果与真实结果的差距,它是一个非负实值函数,主要用来指导模型向正确的方向进行训练。

  • 假设模型的预测值为 <math xmlns="http://www.w3.org/1998/Math/MathML"> o \boldsymbol{o} </math>o ,真实值为 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y 损失函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l ,那么损失值为:

    <math xmlns="http://www.w3.org/1998/Math/MathML"> l o s s = l ( o , y ) loss = l(o,y) </math>loss = l(o,y)

  • 对于不同的场景、不同的训练任务,我们会选用不同的损失函数,以下是几个常用的损失函数实例:

    • 绝对值损失函数: <math xmlns="http://www.w3.org/1998/Math/MathML"> l o s s = ∣ y − o ∣ loss = |y-o| </math>loss = ∣y−o∣

    • 交叉熵损失函数(cross entropy error):

      <math xmlns="http://www.w3.org/1998/Math/MathML"> l o s s = − [ y ∗ l n ( o ) + ( 1 − y ) ∗ l n ( 1 − o ) ] y ∈ [ 0 , 1 ] loss = -[y*ln(o)+(1-y)*ln(1-o)] y \in [0,1] </math>loss = −[y∗ln(o)+(1−y)∗ln(1−o)] y ∈ [0,1]

    • 均方差损失函数: <math xmlns="http://www.w3.org/1998/Math/MathML"> l o s s = ( o − y ) 2 loss=(o-y)^2 </math>loss=(o−y)2

  • 均方差函数,主要用于回归;交叉熵函数,主要用于分类。

反向传播

反向传播(backward propagation)指的是计算神经网络参数梯度的方法 。该方法根据微积分中的链式法则,按相反的顺序从输出层到输入层遍历网络,它对网络中所有参数计算损失函数的梯度。这个梯度会反馈给最优化方法(如梯度下降法),用来更新网络参数以最小化损失函数,最终是希望模型的参数能够较好地拟合样本特征 <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x到标签 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y之间的映射关系。

  • 这里所要优化的参数是隐藏层的权重 <math xmlns="http://www.w3.org/1998/Math/MathML"> W ( 1 ) \boldsymbol{W^{(1)}} </math>W(1)、偏置 <math xmlns="http://www.w3.org/1998/Math/MathML"> b ( 1 ) b^{(1)} </math>b(1),以及输出层的权重 <math xmlns="http://www.w3.org/1998/Math/MathML"> W ( 2 ) \boldsymbol{W^{(2)}} </math>W(2)、偏置 <math xmlns="http://www.w3.org/1998/Math/MathML"> b ( 2 ) b^{(2)} </math>b(2),将损失函数表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> L \boldsymbol{L} </math>L,那么我们实际上是要求以下的梯度:

    <math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ L ∂ W ( 2 ) = ∂ L ∂ o ∗ ∂ o ∂ z o u t ∗ ∂ z o u t ∂ W ( 2 ) \frac{\partial{\boldsymbol{L}}} {\partial \boldsymbol{W^{(2)}}} = \frac{\partial{\boldsymbol{L}}} {\partial \boldsymbol{o}} * \frac{\partial{\boldsymbol{o}}} {\partial \boldsymbol{z^{out}}} * \frac{\partial{\boldsymbol{z^{out}}}} {\partial \boldsymbol{W^{(2)}}} </math>∂ W(2)∂L = ∂ o∂L ∗ ∂ zout∂o ∗ ∂ W(2)∂zout

    <math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ L ∂ b ( 2 ) = ∂ L ∂ o ∗ ∂ o ∂ z o u t ∗ ∂ z o u t ∂ b ( 2 ) \frac{\partial{\boldsymbol{L}}} {\partial b^{(2)}} = \frac{\partial{\boldsymbol{L}}} {\partial \boldsymbol{o}} * \frac{\partial{\boldsymbol{o}}} {\partial \boldsymbol{z^{out}}} * \frac{\partial{\boldsymbol{z^{out}}}} {\partial b^{(2)}} </math>∂ b(2)∂L = ∂ o∂L ∗ ∂ zout∂o ∗ ∂b(2)∂zout

    <math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ L ∂ W ( 1 ) = ∂ L ∂ o ∗ ∂ o ∂ z o u t ∗ ∂ z o u t ∂ z i n ∗ ∂ z i n ∂ W ( 1 ) \frac{\partial{\boldsymbol{L}}} {\partial \boldsymbol{W^{(1)}}} = \frac{\partial{\boldsymbol{L}}} {\partial \boldsymbol{o}} * \frac{\partial{\boldsymbol{o}}} {\partial \boldsymbol{z^{out}}} * \frac{\partial{\boldsymbol{z^{out}}}} {\partial \boldsymbol{z^{in}}} * \frac{\partial{\boldsymbol{z^{in}}}} {\partial \boldsymbol{W^{(1)}}} </math>∂ W(1)∂L = ∂ o∂L ∗ ∂ zout∂o ∗ ∂ zin∂zout∗ ∂ W(1)∂zin

    <math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ L ∂ b ( 1 ) = ∂ L ∂ o ∗ ∂ o ∂ z o u t ∗ ∂ z o u t ∂ z i n ∗ ∂ z i n ∂ b ( 1 ) \frac{\partial{\boldsymbol{L}}} {\partial b^{(1)}} = \frac{\partial{\boldsymbol{L}}} {\partial \boldsymbol{o}} * \frac{\partial{\boldsymbol{o}}} {\partial \boldsymbol{z^{out}}} * \frac{\partial{\boldsymbol{z^{out}}}} {\partial \boldsymbol{z^{in}}} * \frac{\partial{\boldsymbol{z^{in}}}} {\partial b^{(1)}} </math>∂ b(1)∂L = ∂ o∂L ∗ ∂ zout∂o ∗ ∂ zin∂zout∗ ∂ b(1)∂zin

  • 在训练神经网络时,前向传播和反向传播相互依赖, 对于前向传播,需要沿着依赖的方向遍历计算其路径上的所有变量。 然后将这些计算结果用于反向传播,其计算顺序与前向传播相反。一般而言,神经网络的训练比预测需要更多的计算资源。

  • 更为具体的反向传播推理过程可以参考下列链接:

梯度下降

梯度下降是一种优化算法。通过调整网络的参数,使网络的预测值与网络的实际/期望值之间的差异尽可能小,可以改善神经网络的性能。梯度下降采用参数的初始值,并使用基于演算的操作将其值调整为使网络尽可能精确的值,是优化神经网络性能的主要方法。

  • 梯度下降的数学公式:

    <math xmlns="http://www.w3.org/1998/Math/MathML"> θ n + 1 = θ n − η ∗ ∇ J ( θ ) \theta_{n+1} = \theta_{n} - \eta * \nabla J(\theta) </math>θn+1 = θn − η ∗ ∇ J(θ)

    • <math xmlns="http://www.w3.org/1998/Math/MathML"> θ n + 1 \theta_{n+1} </math>θn+1:下一个值;
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> θ n \theta_{n} </math>θn:当前值;
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> η \eta </math>η:学习率或步长,控制每一步走的距离,不能太快以免错过了最佳位置,不能太慢以免训练时间过长;
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ J ( θ ) \nabla J(\theta) </math>∇ J(θ):损失函数对于参数 <math xmlns="http://www.w3.org/1998/Math/MathML"> θ \theta </math>θ的梯度,函数当前位置的最快上升点。
  • 我们已经通过反向传播获取了各个参数相对于损失函数的梯度,那么这里就可以根据梯度的大小和方向,对参数进行调整,从而使得损失值逐渐减小。

    • 梯度:函数当前位置的最快上升点;
    • 下降:与导数相反的方向,用数学语言描述就是那个减号。
  • 假设有一个图表示神经网络产生的误差量。图的底部是误差最小的点,而图的顶部是误差最大的点。如果想要从图形的顶部向下移动到底部,梯度就是一种量化误差与神经网络权值之间关系的方法。

    • 第一步:为权重选择一个起点值(starting point,也就是公式中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> θ n \theta_{n} </math>θn);
    • 第二步:梯度始终指向损失函数中增长最快的方向。梯度下降法算法朝负梯度方向迈进一步,以尽快减少损失;
    • 第三步:为了确定损失函数曲线上的下一个点,梯度下降法算法会在起点(starting point, <math xmlns="http://www.w3.org/1998/Math/MathML"> θ n \theta_{n} </math>θn)处移动一定幅度(或者说学习率 <math xmlns="http://www.w3.org/1998/Math/MathML"> η \eta </math>η)的梯度(gradient, <math xmlns="http://www.w3.org/1998/Math/MathML"> ∇ J ( θ ) \nabla J(\theta) </math>∇ J(θ)),达到下一个点(next point, <math xmlns="http://www.w3.org/1998/Math/MathML"> θ n + 1 \theta_{n+1} </math>θn+1)并将其作为下一轮参数优化时的起点值;
    • 梯度下降法会重复此过程,逐渐接近最小值。
  • 优化器(Optimizer)

    • 优化器就是机器学习中的各种参数优化方法,比如前文提到的梯度下降法。主要用来指引损失函数(目标函数)的各个参数往正确的方向更新合适的大小,使得更新后的各个参数让损失函数(目标函数)值不断逼近全局最小。
    • 批梯度下降(Batch Gradient Descent):遍历全部数据集算一次损失函数,然后算目标函数对各个参数的梯度和更新梯度。
    • 随机梯度下降(Stochastic Gradient Descent):每看一个数据就算一下损失函数,然后求梯度更新参数。
    • 小批次梯度下降(Mini-batch Gradient Descent):把数据分成若干个批,按批来更新参数,这样一批中的一组数据共同决定了本次梯度的方向,下降起来就不容易跑偏,减少了随机性。
    • 基于动量(Momentum):借用了物理中"动量"的概念,让模型在更新时有一定的"惯性",也就是在应用当前batch梯度的同时,也沿用了一部分之前更新的方向,从而使模型的更新更加稳定、学得更快,也有一定能力脱离局部最优。
    • Adagrad算法:能够在训练中自动的对学习率 <math xmlns="http://www.w3.org/1998/Math/MathML"> η \eta </math>η进行调整,对于出现频率较低参数采用较大的 <math xmlns="http://www.w3.org/1998/Math/MathML"> η \eta </math>η更新;相反,对于出现频率较高的参数采用较小的 <math xmlns="http://www.w3.org/1998/Math/MathML"> η \eta </math>η更新。
    • RMSprop:计算之前所有梯度的平均值,缓解Adagrad算法学习率下降较快的问题。
    • Adam:利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。

其他术语

  • 参数初始化:需要对神经网络设置初始的权重,可以是随机的参数,也可以是预训练好的权重。

  • Iteration:进行训练需要的总的迭代次数。

  • Batch Size:进行一次训练或者说一次迭代(Iteration)所选取的样本数,其大小影响模型的优化程度和速度。

  • Epoch:一次 epoch 是指将所有数据训练一遍的次数,epoch 所代表的数字是指所有数据被训练的总轮数。 例如:有30000个数据,计划进行五轮训练,那么 epoch=5。

  • Dropout:我们在前向传播的时候,让某个神经元的激活值以一定的概率 p 停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征,从而防止神经网络的过拟合。

    使用 dropout 的前后示意图

  • BatchNormalization:也就是批量归一化,是一种用于改善神经网络的性能和稳定性的技术。它为神经网络中的任何层提供零均值/单位方差输入,通过调整和缩放激活来规范化输入层,使得模型在训练过程中能够让每一层神经网络的输入保持相同的分布。

  • EarlyStopping:指的是在训练过程中计算模型在验证集上的表现,当模型在验证集上的表现开始下降的时候,停止训练,这样就能避免继续训练导致过拟合的问题。

  • 激活函数:激活函数是在人工神经网络的神经元上运行的函数,负责将神经元的输入映射到输出端,旨在帮助网络学习数据中的复杂模式。类似于人类大脑中基于神经元的模型,激活函数最终决定了要发射给下一个神经元的内容。如果没有激活函数,那么每一层节点的输入都是上层输出的线性函数,网络的逼近效果有限。

  • 正则化:是结构风险最小化策略的实现,针对模型中参数过大的问题引入惩罚项,从而防止模型的过拟合。

  • 深度学习框架:是一种界面、库或者工具,它能够使我们在无需深入了解底层算法(正向计算、反向传播、梯度下降等)的细节的情况下,更加容易、更加快速地构建和训练深度学习模型。深度学习框架利用预先构建和优化好的组件集合定义模型,为模型的实现提供了一种清晰而简洁的方式。当下主流的深度学习框架有TensorFlow、Keras、Pytorch、Caffe、Theano、MXNet 等等,后续所讲的模型训练过程的例子也主要由 TensorFlow、Keras 进行实现。

认识训练过程

这一部分会通过一个"预测共享单车使用情况"的例子,介绍一下我们如何通过使用深度学习框架TensorFlow和Keras,来进行神经网络模型的构建和训练。

数据处理

  • 读取csv中的数据:

    python 复制代码
    import pandas as pd
    import numpy as np
    from sklearn.model_selection import train_test_split
    
    # 读入数据
    data = pd.read_csv("hour.csv")
    print(data.head())
  • 数据的样式如下所示,最后一列 'cnt' 表示当天该时刻所使用的共享单车的数量:

    instant dteday season yr mnth hr holiday weekday workingday weathersit temp atemp hum windspeed casual registered cnt
    1 2011/1/1 1 0 1 0 0 6 0 1 0.24 0.2879 0.81 0 3 13 16
    2 2011/1/1 1 0 1 1 0 6 0 1 0.22 0.2727 0.8 0 8 32 40
    3 2011/1/1 1 0 1 2 0 6 0 1 0.22 0.2727 0.8 0 5 27 32
    4 2011/1/1 1 0 1 3 0 6 0 1 0.24 0.2879 0.75 0 3 10 13
    5 2011/1/1 1 0 1 4 0 6 0 1 0.24 0.2879 0.75 0 0 1 1
  • 对数据集进行划分,并选取所需要的特征:

    python 复制代码
    # 以 4:1 的比率划分训练集与测试集
    train,test = train_test_split(data,test_size=0.2, random_state=0)
    
    # 挑选输入模型的特征,并对其数值进行处理,这一步可以通过"特征工程" 来完成
    # 比如将string处理为有意义的数值、将连续值变为离散值、对离散值进行onehot编码、缺失值处理、非法值处理等等
    # 为了方便,这里的例子将特征的原数据直接输入到网络中
    features_list = [
        'season', 'yr', 'mnth', 'hr', 'holiday', 'weekday', 'workingday', 
        'weathersit', 'temp', 'atemp', 'hum', 'windspeed', 'casual'
    ]
  • 根据任训练务处理数据集和标签:

    • 如果任务是预测某一天某个时刻使用的具体单车数量,那么我们直接将 'cnt' 作为我们训练样本的标签;
    • 如果任务是预测某一天某个时刻共享单车的使用数量是否大于100,那么我们需要 'cnt' 的数据进行处理,当使用数量大于100时,标签为1否则为0。
  • 这里我们根据第二种任务来对数据进行处理:

    python 复制代码
    train_x, train_y = train[features_list], train['cnt'] > 100
    test_x, test_y = test[features_list], test['cnt'] > 100

模型构建

  • 构建一个简单的MLP模型,这里使用 TensofFlow Keras 套件实现,代码参考如下:

    python 复制代码
    import tensorflow as tf
    from tensorflow.keras import Sequential, layers
    
    # input_shape的第一维需要与features_list中的特征个数对齐
    def create_nn(input_shape=(13,), print_summary=False):
        model = Sequential()
        model.add(layers.Dense(256, activation='relu', input_shape=input_shape))
        model.add(layers.Dropout(0.1))
        model.add(layers.Dense(128, activation='relu'))
        model.add(layers.Dropout(0.05))
        model.add(layers.Dense(64, activation='relu'))
        model.add(layers.Dropout(0.05))
        model.add(layers.Dense(32, activation='relu'))
        model.add(layers.Dense(1, activation='sigmoid'))
    
        return model
  • 这里是二分类任务,因此使用 sigmoid 激活函数来将输出的值转为[0, 1]的区间内。

    • 如果是多分类任务,那么可以使用 softmax 激活函数;
    • 如果是回归任务,可以直接将最后一层的神经元作为输出。
  • 模型整体结构如下:

    python 复制代码
    _________________________________________________________________
     Layer (type)                Output Shape              Param #   
    =================================================================
     dense_90 (Dense)            (None, 256)               3328      
    
     dropout_54 (Dropout)        (None, 256)               0         
    
     dense_91 (Dense)            (None, 128)               32896     
    
     dropout_55 (Dropout)        (None, 128)               0         
    
     dense_92 (Dense)            (None, 64)                8256      
    
     dropout_56 (Dropout)        (None, 64)                0         
    
     dense_93 (Dense)            (None, 32)                2080      
    
     dense_94 (Dense)            (None, 1)                 33        
    
    =================================================================
    Total params: 46,593
    Trainable params: 46,593
    Non-trainable params: 0

训练过程

  • 设置各项训练参数,包括优化器 adam,损失函数 binary_crossentropy,监控指标 accuracy、auc 等。

    python 复制代码
    model = create_nn()
    # 设置优化器、学习率
    optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, beta_1=0.9, beta_2=0.999, epsilon=1e-07, amsgrad=False)
    # 设置优化器、loss函数、衡量准确性指标 acc & auc,对于优化器也可以使用注释的语句进行简单的调用(使用默认值)
    model.compile(
        # optimizer='adam',
        optimizer=optimizer, 
        loss="binary_crossentropy", 
        metrics=['accuracy', tf.keras.metrics.AUC()])
    # 启动训练,训练集输入train_x,训练集标签train_y
    # 训练一次多少条样本batch_size=128
    # 所有样本训练一次为一个epoch,最多训练epochs=200
    history = model.fit(
        train_x, train_y, 
        batch_size=128, epochs=200, 
        validation_data=[test_x, test_y])
    # 保存训练完的模型到文件  
    model.save(model_path)
  • 对于学习率,考虑到随着模型迭代的步数越来越多,模型参数也越来越接近最优点,如果保持同样的学习率,可能会使模型优化的步长刚好"跨过"最优点,因此我们可以定义一个随着迭代次数的增加,而不断衰减的学习率,具体设置方法可以查看链接:optimizers.schedules,示例:

    python 复制代码
    initial_learning_rate = 1e-3
    decay_step = total_steps
    end_learning_rate = 1e-5
    learning_rate_fn = tf.keras.optimizers.schedules.PolynomialDecay(initial_learning_rate, decay_step, end_learning_rate)
    optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate_fn, beta_1=0.9, beta_2=0.999, epsilon=1e-07, amsgrad=False)
  • 启动训练后可以看到如下输出:

    python 复制代码
    Epoch 1/200
    123/123 [==============================] - 2s 6ms/step - loss: 0.2829 - accuracy: 0.8740 - auc_1: 0.9495 - val_loss: 0.2391 - val_accuracy: 0.8964 - val_auc_1: 0.9643
    Epoch 2/200
    123/123 [==============================] - 0s 4ms/step - loss: 0.2238 - accuracy: 0.9028 - auc_1: 0.9683 - val_loss: 0.2000 - val_accuracy: 0.9097 - val_auc_1: 0.9753
    ...
    Epoch 200/200
    123/123 [==============================] - 0s 4ms/step - loss: 0.0605 - accuracy: 0.9740 - auc_1: 0.9974 - val_loss: 0.1534 - val_accuracy: 0.9482 - val_auc_1: 0.9876

测试验证

  • 我们可以通过获取 history 中的数据,对训练过程中的各项指标进行可视化:

    python 复制代码
    import matplotlib.pyplot as plt
    
    print(history.history.keys())
    # summarize history for accuracy
    plt.plot(history.history['accuracy'])
    plt.plot(history.history['val_accuracy'])
    plt.title('model accuracy')
    plt.ylabel('accuracy')
    plt.xlabel('epoch')
    plt.legend(['train', 'test'], loc='upper left')
    plt.show()
    
    # summarize history for auc
    plt.plot(history.history['auc_1'])
    plt.plot(history.history['val_auc_1'])
    plt.title('model auc')
    plt.ylabel('auc')
    plt.xlabel('epoch')
    plt.legend(['train', 'test'], loc='upper left')
    plt.show()
    
    # summarize history for loss
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.title('model loss')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train', 'test'], loc='upper left')
    plt.show()
  • 可以看到测试集的accuracy、auc、loss等指标,在训练到一定的epoch时,便很难得到提升,甚至还有下降的趋势,因此我们可以设置一个EarlyStopping的回调,让模型在出现性能指标下降时停止训练。具体用法可参考该链接:EarlyStopping

  • 训练结果指标:

    python 复制代码
    # 自定义评估函数方式,根据在算法章节提到的分类模型评估方法,验证模型准确性
    def verify_model(model, x, y, t=0.5):
        arr = np.zeros((2, 2), int)
        y_ = model.predict(x) > t
        for i in range(len(x)):
            arr[int(y[i]), int(y_[i, 0])] += 1
        arr = arr/sum(sum(arr))
        accuracy = (arr[0, 0]+arr[1,1])/sum(sum(arr))
        print('混淆矩阵', arr) # [[0.38319908 0.02186421][0.02991945 0.56501726]]
        print('accuracy', round(accuracy, 4)) # 0.9482
        print('0 recall:', round(arr[0, 0]/sum(arr[0,:]), 4)) # 0.946
        print('0 precision:', round(arr[0, 0]/sum(arr[:,0]), 4)) # 0.9276
        print('1 recall', round(arr[1, 1]/sum(arr[1,:]), 4)) # 0.9497
        print('1 precision', round(arr[1, 1]/sum(arr[:,1]), 4)) # 0.9627
    
    # 将标签由bool值转为int值,方便后续计算
    test_y_int = []
    for index, row in test_y.iteritems():
        if test_y[index]:
            test_y_int.append(np.array([1]))
        else:
            test_y_int.append(np.array([0]))
    
    verify_model(model, test_x, test_y_int)

总结

以上便是对神经网络模型训练的一个简单介绍,大家可以实际进行操作来进一步了解模型训练的过程,希望上述内容能够帮助大家加深对于端智能的理解。

相关推荐
却尘6 天前
Server Actions 深度剖析(2):缓存管理与重新验证,如何用一行代码干掉整个客户端状态层
前端·客户端·next.js
程序员老刘8 天前
Google突然“变脸“,2026年要给全球开发者上“紧箍咒“?
android·flutter·客户端
Lei活在当下9 天前
【业务场景架构实战】1. 多模块 Hilt 使用原则和环境搭建
性能优化·架构·客户端
万少14 天前
可可图片编辑 HarmonyOS(3)应用间分享图片
前端·harmonyos·客户端
程序员老刘18 天前
Dart MCP翻车了!3.9.0版本无法运行,这个坑你踩过吗?
flutter·ai编程·客户端
程序员老刘19 天前
Cursor vs Claude Code vs AS+AI助手:谁才是客户端的编程神器?
flutter·ai编程·客户端
麦客奥德彪25 天前
React native 项目函数式编程的背后-另类的架构InversifyJS 依赖注入(DI)
react native·架构·客户端
fouryears_234171 个月前
Flutter InheritedWidget 详解:从生命周期到数据流动的完整解析
开发语言·flutter·客户端·dart
程序员老刘1 个月前
Flutter 3.35 更新要点解析
flutter·ai编程·客户端
红橙Darren1 个月前
手写操作系统 - 编译链接与运行
android·ios·客户端