美文网首页
人人都能懂的机器学习——用Keras搭建人工神经网络06

人人都能懂的机器学习——用Keras搭建人工神经网络06

作者: 苏小菁在编程 | 来源:发表于2020-03-05 21:48 被阅读0次

    编译模型

    在创建了模型之后,我们必须要使用compile()方法来指定损失方程和优化器。另外,你还可以指定在训练和评估过程中计算出一系列其他的指标(作为一个列表输入):

    model.compile(loss="sparse_categorical_crossentropy",
                  optimizer="sgd",
                  metrics=["accuracy"])
    

    这里使用下面的代码也能达到完全一样的效果:

    model.compile(loss=keras.losses.sparse_categorical_crossentropy,
                  optimizer=keras.optimizers.SGD(),
                  metrics=[keras.metrics.sparse_categorical_accuracy])
    

    未来我们还将使用更多其他的损失,优化器和指标,如果想看一下完整的列表,可以访问下面的网址:

    https://keras.io/losses/

    https://keras.io/optimizers/

    https://keras.io/metrics/

    我们来简单解释一下上面的代码,首先,使用sparse_categorical_crossentropy损失函数是因为我们有离散的标签(对于每个实例,只有一个目标类别,在这个模型中就是0到9),并且类别与类别之间是互斥的。如果我们对每个实例对每个类别都输出一个概率(就像一位有效编码one-hot vector一样,[0., 0., 0., 1., 0., 0.]表示3类别),那我们就用categorical_crossentropy损失就可以了。如果需要做二元分类(可能只有一个或者多个二元标签),那就使用sigmoid函数,而不是softmax激活函数,同时我们使用binary_crossentropy损失。

    如果你想将离散的标签转换成one-hot vector使用keras.utils.to_categorical()函数。

    至于优化器,sgd代表我们使用随机梯度下降法(Stochastic Gradient Descent)来训练模型。换句话说,Keras会执行之前的文章中所描述的反向传播算法。我们未来还会介绍更多高效的优化器(它们优化了梯度下降的方法,而并不是自动微分)。使用SGD优化器的时候,调整学习速度是十分重要的。所以一般会使用:

    optimizer=keras.optimizers.SGD(lr=XXXXXX)
    

    因为sgd的默认学习速度为0.01。

    最后,既然这是一个分类器,那么衡量它的准确度accuracy是很有用的。

    训练与评估模型

    现在我们的模型已经准备好训练了,我们只需调用fit()方法就可以了:

    >>> history = model.fit(X_train, y_train, epochs=30,
    ... validation_data=(X_valid, y_valid))
    ...
    Train on 55000 samples, validate on 5000 samples
    Epoch 1/30
    55000/55000 [======] - 3s 49us/sample - loss: 0.7218 - accuracy: 0.7660
    - val_loss: 0.4973 - val_accuracy: 0.8366
    Epoch 2/30
    55000/55000 [======] - 2s 45us/sample - loss: 0.4840 - accuracy: 0.8327
    - val_loss: 0.4456 - val_accuracy: 0.8480
    [...]
    Epoch 30/30
    55000/55000 [======] - 3s 53us/sample - loss: 0.2252 - accuracy: 0.9192
    - val_loss: 0.2999 - val_accuracy: 0.8926
    

    我们输入了特性(X_train)和目标类别(y_train),以及要训练的epoch次数(不然的话epoch默认为1,这肯定无法收敛至一个好的结果)。所谓epoch就是使用训练集中的全部数据对模型进行一次完整的训练,称为'一代训练'。不过这个中文叫法实在拗口,不如还是叫一个epoch。我们还输入了一个验证集(这是可选的)。Keras将在每个epoch结束时度量这些损失和其他指标,这对于查看模型的实际执行情况非常有用。如果模型在训练集上的表现比验证集上的表现好得多,那么模型可能对训练集进行了过拟合(或者可能存在bug,比如说训练集和验证集之间的数据不匹配)。

    这样就可以了,我们模型已经训练好了!如果训练集和验证集的数据不匹配预期的输入形状的话,就会得到一个异常。这可能是最常见的错误,我们应该熟悉这个异常提示,比如:如果尝试使用一个包含扁平图像的向量,X_train.reshape(-1,784)。那么就会得到这样的异常提醒:“ValueError: Error when checking input: expected flatten_input to have 3 dimensions, but got array with shape (60000, 784)”。

    在训练过程中的每一个epoch,Keras都会展示已经处理的实例数,以及一个处理进度条,每个实例训练的平均时间以及训练集和验证集的损失和准确度(或者还有其他设置好的指标)。你会看到训练损失下降了(这是个好现象),在30个epoch之后,验证集的准确度达到了89.26%,与训练集的准确度没有差距太大,所以看起来没有太大的过拟合发生。

    上面的代码使用validation_data参数输入验证集,我们还可以使用validation_split的方式,设置一定比例的训练集用于验证集。比如,validation_split=0.1那么Keras将会使用最后10%的数据(在打乱顺序之前)用于验证。

    如果训练集很不平衡,有些类别的实例过多而有些过少,那么应该在fit()方法中设置class_weight参数,这个参数会给实例较少的类别更大的权重,给实例过多的类别较小的权重。Keras将会在计算损失时考虑这些权重。如果需要设置每个实例的权重,可以设置sample_weight参数(它会取代class_weight)。如果有些实例是由专家标记的,而其他实例是使用公共数据平台标记的,那么每个实例的权重可能会很有用:你可能希望给专家标记的数据更多的权重。你也可以在validation_data的tuple中添加第三项,来给验证集设置实例权重。

    fit()方法会返回一个History对象,这个对象包含训练参数(history.params),训练经过的epoch(history.history)以及最重要的,在每个epoch结束的时候对训练集和验证集度量的损失和其他指标的结果字典(history.history)。如果你用字典创建了Pandas的DataFrame,并且调用plot()方法,那么就会得到图1.12:

    import pandas as pd
    import matplotlib.pyplot as plt
    pd.DataFrame(history.history).plot(figsize=(8, 5))
    plt.grid(True)
    plt.gca().set_ylim(0, 1) # set the vertical range to [0-1]
    plt.show()
    
    图1.12 学习曲线:每个epoch的平均训练损失和准确度,每个epoch结束时的平均验证损失和准确度.jpg

    可以看到,在训练过程中,训练准确度和验证准确度都在稳步提高,而训练损失和验证损失都在减少。此外,验证曲线与训练曲线接近,这意味着没有太多的过拟合。在这个例子当中,在训练刚开始时模型在验证集上的表现似乎比训练集上要好。但事实并非如此:实际上,验证错误是在每个epoch结束时计算的,而训练错误是在每个epoch期间使用平均值计算的。所以训练曲线应该向左移动半个epoch。这样,你就可以发现训练和验证曲线在训练开始时几乎完全重合。

    其实在绘制训练曲线时,也应当将其向左移动半个epoch。

    通常情况下,如果训练时间足够长,训练集的表现最终会超过验证集的表现。可以看出这个例子当中,模型还没有完全收敛,验证损失仍然在下降,所以你应该继续训练。这与再次调用fit()方法一样简单,因为Keras会在它停止的地方继续进行训练(最终应该能够达到接近89%的验证准确度)。

    如果对模型的表现不满意,那么应该重新调整超参数。首先要检查的是学习率。如果没什么用,尝试换一个优化器(并总是在更改任何超参数后重新调整学习率)。如果表现仍然不是很好,那么可以尝试调整模型的超参数,例如层的数量、每层神经元的数量以及每个隐藏层使用的激活函数。你还可以尝试调整其他超参数,比如批处理大小(可以在fit()方法中使用batch_size参数进行设置,默认值为32)。我们将在未来的文章当中再次讲到超参数调优。一旦您对模型的验证准确度感到满意,就应该在测试集上对其进行评估,以在将模型部署到生产环境之前估计泛化误差。使用evaluate()方法就可以评估测试集的准确度了(它还支持其他参数,比如batch_size以及sample_weight等):

    >>> model.evaluate(X_test, y_test)
    10000/10000 [==========] - 0s 29us/sample - loss: 0.3340 - accuracy: 0.8851
    [0.3339798209667206, 0.8851]
    

    通常模型在测试集中的表现要比验证集中略差一些,这是很正常的。因为超参数是根据验证集的表现来调整的(不过在这个例子里我们没有做任何超参的调整,所以准确度低只是运气不好)。记住,一定要抵制在测试集中调超参的诱惑,否则你会对泛化误差的估计过于乐观。

    使用模型进行预测

    接下来,我们可以使用模型的predict()方法对新的实例进行预测了。这里我们用测试集的前三个实例进行预测:

    >>> X_new = X_test[:3]
    >>> y_proba = model.predict(X_new)
    >>> y_proba.round(2)
    array([[0. , 0. , 0. , 0. , 0. , 0.03, 0. , 0.01, 0. , 0.96],
    [0. , 0. , 0.98, 0. , 0.02, 0. , 0. , 0. , 0. , 0. ],
    [0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ]],
    dtype=float32)
    

    对于每个实例,模型对每个类别都生成了一个概率。例如,对于第一个图像,它估计类别9(短靴)的概率是96%,类别5(凉鞋)的概率是3%,类别7(运动鞋)是1%,其他类别的概率忽略不计。

    换句话说,模型“相信”第一个图片是鞋类,最有可能是短靴,但也不完全确定,可能是凉鞋或运动鞋。不过如果你只关心估计概率最高的类别(即使这个概率并不高),那么可以使用predict_classes()方法:

    >>> y_pred = model.predict_classes(X_new)
    >>> y_pred
    array([9, 2, 1])
    >>> np.array(class_names)[y_pred]
    array(['Ankle boot', 'Pullover', 'Trouser'], dtype='<U11')
    

    那么模型实际上对这三个图像都预测正确了(见图1.13)。

    图1.13 测试集前三张图像.jpg

    好的,那么现在我们已经学习了如何用Sequential API来搭建、训练、评估和使用分类MLP,那么接下来的文章我们将讲述如何搭建回归MLP以及搭建更加复杂的模型。

    敬请期待啦!

    相关文章

      网友评论

          本文标题:人人都能懂的机器学习——用Keras搭建人工神经网络06

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