Deep learning with Python 学习笔记(9)

神经网络模型的优化

使用 Keras 回调函数

使用 model.fit()或 model.fit_generator() 在一个大型数据集上启动数十轮的训练,有点相似于扔一架纸飞机,一开始给它一点推力,以后你便再也没法控制其飞行轨迹或着陆点。若是想要避免很差的结果(并避免浪费纸飞机),更聪明的作法是不用纸飞机,而是用一架无人机,它能够感知其环境,将数据发回给操纵者,而且可以基于当前状态自主航行。下面要介绍的技术,可让model.fit() 的调用从纸飞机变为智能的自主无人机,能够自我检讨并动态地采起行动html

训练过程当中将回调函数做用于模型python

训练模型时,不少事情一开始都没法预测。尤为是你不知道须要多少轮才能获得最佳验证损失。前面全部例子都采用这样一种策略:训练足够多的轮次,这时模型已经开始过拟合,根据这第一次运行来肯定训练所须要的正确轮数,而后使用这个最佳轮数从头开始再启动一次新的训练。固然,这种方法很浪费算法

处理这个问题的更好方法是,当观测到验证损失再也不改善时就中止训练。这可使用 Keras 回调函数来实现。回调函数(callback)是在调用 fit 时传入模型的一个对象(即实现特定方法的类实例),它在训练过程当中的不一样时间点都会被模型调用。它能够访问关于模型状态与性能的全部可用数据,还能够采起行动:中断训练、保存模型、加载一组不一样的权重或改变模型的状态后端

回调函数的一些用法示例以下所示数组

  1. 模型检查点(model checkpointing):在训练过程当中的不一样时间点保存模型的当前权重
  2. 提早终止(early stopping):若是验证损失再也不改善,则中断训练(固然,同时保存在训练过程当中获得的最佳模型)
  3. 在训练过程当中动态调节某些参数值:好比优化器的学习率
  4. 在训练过程当中记录训练指标和验证指标,或将模型学到的表示可视化(这些表示也在不断更新):Keras 进度条就是一个回调函数

keras.callbacks 模块包含许多内置的回调函数,如浏览器

keras.callbacks.ModelCheckpoint
keras.callbacks.EarlyStopping
keras.callbacks.LearningRateScheduler
keras.callbacks.ReduceLROnPlateau
keras.callbacks.CSVLogger网络

架构

ModelCheckpoint 与 EarlyStopping 回调函数框架

若是监控的目标指标在设定的轮数内再也不改善,能够用 EarlyStopping 回调函数来中断训练。好比,这个回调函数能够在刚开始过拟合的时候就中断训练,从而避免用更少的轮次从新训练模型。这个回调函数一般与ModelCheckpoint 结合使用,后者能够在训练过程当中持续不断地保存模型(你也能够选择只保存目前的最佳模型,即一轮结束后具备最佳性能的模型)机器学习

import keras


# 经过 fit 的 callbacks 参数将回调函数传入模型中,这个参数接收一个回调函数的列表。你能够传入任意个数的回调函数
# EarlyStopping: 1. 若是再也不改善,就中断训练 2. 监控模型的验证精度 3. 若是精度在多于一轮的时间(即两轮)内再也不改善,中断训练
# ModelCheckpoint: 1. 在每轮事后保存当前权重 2. 若是 val_loss 没有改善,那么不须要覆盖模型文件
callbacks_list = [ 
    keras.callbacks.EarlyStopping(
        monitor='acc', 
        patience=1, 
    ),
    keras.callbacks.ModelCheckpoint( 
        filepath='model.h5', 
        monitor='val_loss', 
        save_best_only=True,
 )
]

# 监控精度,因此" metrics=['acc'] "应该是模型指标的一部分
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc']) 
# 因为回调函数要监控验证损失和验证精度,因此在调用 fit 时须要传入 validation_data(验证数据)
model.fit(x, y, epochs=10, batch_size=32, validation_data=(x_val, y_val), callbacks=callbacks_list)

ReduceLROnPlateau 回调函数
若是验证损失再也不改善,你可使用这个回调函数来下降学习率。在训练过程当中若是出现了损失平台(loss plateau),那么增大或减少学习率都是跳出局部最小值的有效策略

# 监控模型的验证损失,触发时将学习率除以 10,若是验证损失在 10 轮内都没有改善,那么就触发这个回调函数  
callbacks_list = [
    keras.callbacks.ReduceLROnPlateau(
    monitor='val_loss' 
    factor=0.1, 
    patience=10, 
 )
]

model.fit(x, y,
    epochs=10,
    batch_size=32,
    callbacks=callbacks_list,
    validation_data=(x_val, y_val))

自定义回调函数

回调函数的实现方式是建立 keras.callbacks.Callback 类的子类。而后你能够实现下面这些方法(从名称中便可看出这些方法的做用),它们分别在训练过程当中的不一样时间点被调用

  1. on_epoch_begin -- 在每轮开始时被调用
  2. on_epoch_end -- 在每轮结束时被调用
  3. on_batch_begin -- 在处理每一个批量以前被调用
  4. on_batch_end -- 在处理每一个批量以后被调用
  5. on_train_begin -- 在训练开始时被调用
  6. on_train_end -- 在训练结束时被调用

这些方法被调用时都有一个 logs 参数,这个参数是一个字典,里面包含前一个批量、前一个轮次或前一次训练的信息,即训练指标和验证指标等。此外,回调函数还能够访问下列属性

  1. self.model:调用回调函数的模型实例
  2. self.validation_data:传入 fit 做为验证数据的值

自定义回调函数的简单示例,它能够在每轮结束后将模型每层的激活保存到硬盘(格式为 Numpy 数组),这个激活是对验证集的第一个样本计算获得的

import keras
import numpy as np


class ActivationLogger(keras.callbacks.Callback):
    def set_model(self, model):
        self.model = model 
        layer_outputs = [layer.output for layer in model.layers]
        self.activations_model = keras.models.Model(model.input, layer_outputs) 
    
    def on_epoch_end(self, epoch, logs=None):
        if self.validation_data is None:
            raise RuntimeError('Requires validation_data.')
        validation_sample = self.validation_data[0][0:1] 
        activations = self.activations_model.predict(validation_sample)
        f = open('activations_at_epoch_' + str(epoch) + '.npz', 'w') 
        np.savez(f, activations)
        f.close()

TensorBoard 简介:TensorFlow 的可视化框架

TensorBoard,一个内置于 TensorFlow 中的基于浏览器的可视化工具。只有当 Keras 使用 TensorFlow 后端时,这一方法才能用于 Keras 模型

-- 等待尝试

让模型性能发挥到极致

高级架构模式

除残差链接外,标准化和深度可分离卷积在构建高性能深度卷积神经网络时也特别重要

批标准化

标准化(normalization)是一大类方法,用于让机器学习模型看到的不一样样本彼此之间更加类似,这有助于模型的学习与对新数据的泛化。最多见的数据标准化形式就是:将数据减去其平均值使其中心为 0,而后将数据除以其标准差使其标准差为 1。实际上,这种作法假设数据服从正态分布(也叫高斯分布),并确保让该分布的中心为 0,同时缩放到方差为 1

normalized_data = (data - np.mean(data, axis=...)) / np.std(data, axis=...)

前面的示例都是在将数据输入模型以前对数据作标准化。但在网络的每一次变换以后都应该考虑数据标准化。即便输入 Dense 或 Conv2D 网络的数据均值为 0、方差为 1,也没有理由 假定网络输出的数据也是这样

批标准化(batch normalization)是在 2015 年提出的一种层的类型(在Keras 中是 BatchNormalization),即便在训练过程当中均值和方差随时间发生变化,它也能够适应性地将数据标准化。批标准化的工做原理是,训练过程当中在内部保存已读取每批数据均值和方差的指数移动平均值。批标准化的主要效果是,它有助于梯度传播(这一点和残差链接很像),所以容许更深的网络。对于有些特别深的网络,只有包含多个 BatchNormalization 层时才能进行训练

BatchNormalization 层一般在卷积层或密集链接层以后使用

conv_model.add(layers.Conv2D(32, 3, activation='relu')) 
conv_model.add(layers.BatchNormalization())

dense_model.add(layers.Dense(32, activation='relu')) 
dense_model.add(layers.BatchNormalization())

BatchNormalization 层接收一个 axis 参数,它指定应该对哪一个特征轴作标准化。这个参数的默认值是 -1,即输入张量的最后一个轴。对于 Dense 层、Conv1D 层、RNN 层和将data_format 设为 "channels_last"(通道在后)的 Conv2D 层,这个默认值都是正确的。但有少数人使用将 data_format 设为 "channels_first"(通道在前)的 Conv2D 层,这时特征轴是编号为 1 的轴,所以 BatchNormalization 的 axis 参数应该相应地设为 1

深度可分离卷积

深度可分离卷积(depthwise separable convolution)层(SeparableConv2D)能够替代 Conv2D,并可让模型更加轻量(即更少的可训练权重参数)、速度更快(即更少的浮点数运算),还可让任务性能提升几个百分点。这个层对输入的每一个通道分别执行空间卷积,而后经过逐点卷积(1×1 卷积)将输出通道混合

如图示

这至关于将空间特征学习和通道特征学习分开,若是你假设输入中的空间位置高度相关,但不一样的通道之间相对独立,那么这么作是颇有意义的。它须要的参数要少不少,计算量也更小,所以能够获得更小、更快的模型。由于它是一种执行卷积更高效的方法,因此每每可以使用更少的数据学到更好的表示,从而获得性能更好的模型

demo

from keras.models import Sequential, Model
from keras import layers


height = 64
width = 64
channels = 3
num_classes = 10
model = Sequential()
model.add(layers.SeparableConv2D(32, 3, activation='relu', input_shape=(height,                 width, channels,)))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu'))
model.add(layers.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu'))
model.add(layers.GlobalAveragePooling2D())
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(num_classes, activation='softmax'))
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')

超参数优化

构建深度学习模型时,你必须作出许多看似随意的决定:应该堆叠多少层?每层应该包含多少个单元或过滤器?激活应该使用 relu 仍是其余函数?在某一层以后是否应该使用BatchNormalization ?应该使用多大的 dropout 比率?还有不少。这些在架构层面的参数叫做超参数(hyperparameter),以便将其与模型参数区分开来,后者经过反向传播进行训练

超参数优化的过程一般以下所示:

  1. 选择一组超参数
  2. 构建相应的模型
  3. 将模型在训练数据上拟合,并衡量其在验证数据上的最终性能
  4. 选择要尝试的下一组超参数(自动选择)
  5. 重复上述过程
  6. 最后,衡量模型在测试数据上的性能

这个过程的关键在于,给定许多组超参数,使用验证性能的历史来选择下一组须要评估的超参数的算法。有多种不一样的技术可供选择:贝叶斯优化、遗传算法、简单随机搜索等

更新超参数很是具备挑战性,如

  1. 计算反馈信号(这组超参数在这个任务上是否获得了一个高性能的模型)的计算代价可能很是高,它须要在数据集上建立一个新模型并从头开始训练
  2. 超参数空间一般由许多离散的决定组成,于是既不是连续的,也不是可微的。所以,你一般不能在超参数空间中作梯度降低。相反,你必须依赖不使用梯度的优化方法,而这些方法的效率比梯度降低要低不少

一般状况下,随机搜索(随机选择须要评估的超参数,并重复这一过程)就是最好的解决方案,虽然这也是最简单的解决方案。也存在一些工具比随机搜索要好不少,如:Hyperopt。它是一个用于超参数优化的 Python 库,其内部使用 Parzen 估计器的树来预测哪组超参数可能会获得好的结果。另外一个叫做 Hyperas 的库将 Hyperopt 与 Keras 模型集成在一块儿

模型集成

集成是指将一系列不一样模型的预测结果聚集到一块儿,从而获得更好的预测结果。集成依赖于这样的假设,即对于独立训练的不一样良好模型,它们表现良好多是由于不一样的缘由:每一个模型都从略有不一样的角度观察数据来作出预测,获得了“真相”的一部分,但不是所有真相。每一个模型都获得了数据真相的一部分,但不是所有真相。将他们的观点聚集在一块儿,你能够获得对数据更加准确的描述

集成最简单的方法就是就不一样模型的结果进行平均,以平均值做为预测的结果。可是这种方法假设了所使用的分类器的性能都差很少好。若是其中一个模型性能比其余的差不少,那么最终预测结果可能不如这一组中的最佳模型好

而更加适用的方法是对各个模型的结果进行加权平均,其权重从验证数据上学习获得。一般来讲,更好的模型被赋予更大的权重,而较差的模型则被赋予较小的权重。为了找到一组好的集成权重,你可使用随机搜索或简单的优化算法(好比 Nelder-Mead 方法)

还有许多其余变体,好比你能够对预测结果先取指数再作平均。通常来讲,简单的加权平均,其权重在验证数据上进行最优化,这是一个很强大的基准方法。想要保证集成方法有效,关键在于这组分类器的多样性(diversity)。多样性可以让集成方法取得良好效果。用机器学习的术语来讲,若是全部模型的误差都在同一个方向上,那么集成也会保留一样的误差。若是各个模型的误差在不一样方向上,那么这些误差会彼此抵消,集成结果会更加稳定、更加准确

所以,集成的模型应该尽量好,同时尽量不一样。这一般意味着使用很是不一样的架构,甚至使用不一样类型的机器学习方法。有一件事情基本上是不值得作的,就是对相同的网络,使用不一样的随机初始化屡次独立训练,而后集成。若是模型之间的惟一区别是随机初始化和训练数据的读取顺序,那么集成的多样性很小,与单一模型相比只会有微小的改进。集成不在于你的最佳模型有多好,而在于候选模型集合的多样性

在进行大规模超参数自动优化时,有一个重要的问题须要牢记,那就是验证集过拟合。由于你是使用验证数据计算出一个信号,而后根据这个信号更新超参数,因此你其实是在验证数据上训练超参数,很快会对验证数据过拟合

Deep learning with Python 学习笔记(10)
Deep learning with Python 学习笔记(8)

相关文章
相关标签/搜索