深度神经网络之Keras(一)——从Keras开始
本文将探索Keras框架下的深度神经网络知识,主要介绍了利用Keras构建深度神经网络的步骤及其结构。Keras的安装在此未提及(读者可在网络上很容易找到)。
目录
一、输入数据
输入的数据可以是任意格式(存储方式)与类型(可变换为数值型),数据进入模型时被理解为“张量(tensor)”,也可以理解为向量,或是数组及矩阵。每列(每行)代表一个训练样本。如电离层TEC建模,日变化分量、季节变化分量以及与之对应的电离层TEC值均是训练样本。
为减少计算量,适应激活函数,对于输入数据,应当进行正规化、标准化或者缩放。正规化是将值落入0-1范围内,标准化是将值落入平均值为0且标准差为1的范围,缩放即将值缩放几倍,达到想要的结果。
二、神经元
看到神经元,千万不要联想到生物学神经元,想到只会误导。神经元可以说是深度学习中最基本的单位元素,几乎所有深度学习的网络都是由神经元通过不同的方式组合起来。一个完整的神经元由两部分构成,分别是“线性模型”与“激励函数”。
三、激活函数
下图的非线性函数就是激活函数。激活函数接收下图中的求和的值,对其应用激活函数,然后输出值。
在这里,使用激活函数的目的是明确定义出激活发生位置处的阈值,然而,做为激活函数,不能是常数或线性函数,导数为0,没有激活功能。在深度学习中,通常使用反向传播算法进行训练,有助于向神经网络提供有关错误分类信息的反馈,然后通过使用激活函数的导数调整输入值得权重。
四、Sigmoid与ReLU函数
Sigmoid激活函数定义为
这个函数能让它的输出值在0-1之间,在Keras中,可以使用keras.activations.sigmoid(x)的形式应用sigmoid函数。
ReLU激活函数定义为
如果输入为正,则输出为正的相同值,反之,输出为0,在Keras中,可以使用keras.activations.relu(x, alpha=0.0, max_value=None)的形式应用ReLU函数。
ReLU函数在应用时可能会导致严重问题,原因就是输入值为负时的输出值为0,相当于未施加激活函数,导致训练中的权重不容易更新。鉴于此,引入一种新的激活函数——Leaky ReLU,与ReLU函数不同的是,给负值部分的水平线增加了值非常小的斜率,用以反向传播时更新权重。
Leaky ReLU定义为
其中a(即alpha)为微小的常量,在Keras中,可以使用keras.layers. LeakyReLU(X, alpha=0.0, max_value=None)的形式应用。
五、模型
模型的构建是深度神经网络的重要步骤,在Keras中,通过model对象来构建,它是通过逐层添加新神经网络来创建层堆栈。定义模型最简单的方法是使用序列模型(sequential model),如下例创建了一个简单的序列模型,有一个层(layer),层后面是该层的激活函数。该层有10个神经元,从15个神经元处接收输入,并由ReLU函数激活。
from keras.models import Sequential
from keras.layers import Dense, Activation
model = Sequential()
model.add(Dense(10, input_dim=15))
model.add(Activation('relu'))
六、层
深度神经网络中的层定义为一组神经元或一个分层网络结构中的逻辑上分离的组。
1、核心层
全连接层(稠密层)是深度神经网络的一种常规层,它将本层中的每个神经元与前一层中的每个神经元两两互连。例如,如果第1层有5个神经元而第2层有3个神经元(第2层是全连接层),则第1层和第2层之间的连接总数为5×3=15。
Keras提供具有以下默认参数的全连接层:
keras.layers.Dense(units, activation = None,
use_bias = True,
kernel_initializer = 'glorot_uniform',
bias_initializer = 'zeros',
kernel_regularizer = None,
bias_regularizer = None,
activity_regularizer = None,
kernel_constraint = None,
bias_constraint = None)
对于任意层(即非全连接层),有以上的自定义方式。units代表单元数量(即层中的神经元数)、activation表示激活函数类型、kernel_initializer和bias_initializer分别指内核和偏差的初始化类型(初始化定义了设置Keras各层权重随机初始值的方法,具体方法见https://www.bookstack.cn/read/keras-docs-zh/sources-initializers.md )、regularizer指对学习过程进行正则化、来自constraints模块的函数允许在优化期间对网络参数设置约束(例如,非负,详见https://www.bookstack.cn/read/keras-docs-zh/sources-constraints.md )。一般只需要单元数量和激活类型等参数,其他为默认值。
另一个需要定义的是Keras层的输入形状。且只需在第一层定义输入形状,后续层只需定义神经元的数量,input_dim定义输入的维数(即特征的个数)。
2、Dropout层
Dropout层(即舍弃层)中将包含正则化(regularization)和泛化(generalization)功能,目的是减少过拟合现象。具体用法如下:
keras.layers.Dropout(rate, noise_shape=None, seed=None)
rate:在0和1之间浮动。需要丢弃的输入比例。
noise_shape:1D整数张量,表示将与输入相乘的二进制dropout掩层的形状。例如,如果你的输入尺寸为(batch_size, timesteps, features),然后你希望dropout 掩层在所有时间步都是一样的,你可以使用 noise_shape=(batch_size, 1, features)。
seed: 一个作为随机种子的 Python 整数。
示例:在常规层后增加了Dropout层。
model = Sequential()
model.add(Dense(5, input_dim=10, activation="sigmoid"))
model.add(Dropout(rate=0.1, seed=100))
model.add(Dense(1, activation="sigmoid"))
3、其它层及编写自己的层
详见https://keras.io/zh/layers/writing-your-own-keras-layers/ 。
七、损失函数
损失函数(loss function)是帮助神经网络了解它是否在正确的方向上学习的度量标准。(可联想考试的分数),损失函数主要度量相对于目标的损失。模型从数据中学习,并预测学生考试及格的概率为0.87,所以这里实际的损失是1-0.87=0.13,如果通过一些参数的更新重复练习并改进模型后,得到0.4的损失,那么它没有帮助神经网络正确的学习。或者,新的损失是0.05,则表明学习的更新或改变是正确的。
损失函数类型:
①均方差,即实际值和预测值之差的平方的均值
keras.losses.mean_squared_error(y_true=, y_pred=)
②平均绝对误差,即实际值和预测值之间绝对误差的均值
keras.losses.mean_absolute_error(y_true=, y_pred=)
③平均绝对百分比误差
keras.losses.mean_absolute_percentage_error(y_true=, y_pred=)
④均方对数误差
keras.losses.mean_squared_logarithmic_error(y_true=, y_pred=)
分类问题在此不做讨论。
八、优化器
优化器函数有助于更新权重以减少损失,是一种数学算法,使用微积分中的导数、偏导数和链式法则,通过对神经元的权重进行微小的改变,来了解神经网络的损失函数将发生变化的程度。
一个训练样本经历从输入层到输出层的计算称为“一遍(pass)”。训练会分批(batch)进行,神经网络在处理完一批中的所有样本后,更新其权重,称为一次迭代。完成一次对输入数据中提供的所有训练样本的计算加上逐批(batch)的权重更新,称为一个历元(epoch)。在每次迭代中,神经网络利用优化器函数对其权重参数(权重在开始时被随机初始化)进行小的改变,通过降低损失函数来改进最终预测结果。通过几次迭代,然后是几个历元,神经网络更新其权重并学习,对给定训练样本进行正确的预测。
1、随机梯度下降(SGD)
随机梯度下降对每个训练样本执行一次迭代。由于权重更新频繁,因此整体损失曲线会有大量噪声,为降低这种高波动,较好的方法是提供小分批(minibatch)来减少迭代次数。批尺寸(batch size)通常设置为2的幂数(32,64,128)。
权重更新公式:权重=权重-学习率×损失
假设学习率为0.01,SGD形式如下:
keras.optimizers.SGD(lr=0.01, momentum=0.0, decay=0.0, nesterov=False)
lr: float >= 0. 学习率。
momentum: float >= 0. 参数,用于加速 SGD 在相关方向上前进,并抑制震荡。
decay: float >= 0. 每次参数更新后学习率衰减值。
nesterov: boolean. 是否使用 Nesterov 动量。
为实现在每个训练样本通过网络后进行权重更新,需要在模型训练函数中设置batch_size=1。
2、Adam
Adam是自适应矩估计,是目前深度学习中最热门、应用最广泛的优化器。大多数情况下可以直接选用。该优化技术为每个参数计算一个自适应学习率。它定义了损失梯度的动量(momentum)和方差(variance),并利用组合效应来更新权重参数,动量和方差共同作用,有助于平滑学习曲线并有效改善学习过程。
权重更新公式:权重=权重-(动量和方差的组合)
Adam形式如下:
keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)
lr: float >= 0. 学习率。
beta_1: float, 0 < beta < 1. 通常接近于 1。(计算动量,一般使用默认值)
beta_2: float, 0 < beta < 1. 通常接近于 1。(计算方差,一般使用默认值)
epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。
decay: float >= 0. 每次参数更新后学习率衰减值。
amsgrad: boolean. 是否应用此算法的 AMSGrad 变种
3、RMSProp
keras.optimizers.RMSprop(lr=0.001, rho=0.9, epsilon=None, decay=0.0)
lr: float >= 0. 学习率。
rho: float >= 0. RMSProp梯度平方的移动均值的衰减率.
epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。
decay: float >= 0. 每次参数更新后学习率衰减值。
建议使用优化器的默认参数(除了学习率 lr,它可以被自由调节),这个优化器通常是训练循环神经网络RNN的不错选择。
4、Adagrad
keras.optimizers.Adadelta(lr=1.0, rho=0.95, epsilon=None, decay=0.0)
lr: float >= 0. 学习率.
epsilon: float >= 0. 若为 None, 默认为 K.epsilon().
decay: float >= 0. 每次参数更新后学习率衰减值.
Adagrad是一种具有特定参数学习率的优化器,它根据参数在训练期间的更新频率进行自适应调整。参数接收的更新越多,更新越小。建议使用优化器的默认参数。
5、Adadelta
keras.optimizers.Adadelta(lr=1.0, rho=0.95, epsilon=None, decay=0.0)
lr: float >= 0. 学习率,建议保留默认值。
rho: float >= 0. Adadelta梯度平方移动均值的衰减率。
epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。
decay: float >= 0. 每次参数更新后学习率衰减值。
Adadelta 是 Adagrad 的一个具有更强鲁棒性的的扩展版本,它不是累积所有过去的梯度,而是根据渐变更新的移动窗口调整学习速率。这样,即使进行了许多更新,Adadelta 仍在继续学习。与Adagrad相比,在Adadelta的原始版本中,您无需设置初始学习率。在此版本中,与大多数其他Keras优化器一样,可以设置初始学习速率和衰减因子。建议使用优化器的默认参数。
6、Adamax
keras.optimizers.Adamax(lr=0.002, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0)
lr: float >= 0. 学习率。
beta_1/beta_2: floats, 0 < beta < 1. 通常接近于 1。
epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。
decay: float >= 0. 每次参数更新后学习率衰减值。
它是Adam算法基于无穷范数(infinity norm)的变种。 默认参数遵循默认值。
7、Nadam
keras.optimizers.Nadam(lr=0.002, beta_1=0.9, beta_2=0.999, epsilon=None, schedule_decay=0.004)
lr: float >= 0. 学习率。
beta_1/beta_2: floats, 0 < beta < 1. 通常接近于 1。
epsilon: float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon()。
正像 Adam 本质上是 RMSProp 与动量 momentum 的结合, Nadam 是采用 Nesterov momentum 版本的 Adam 优化器。默认参数遵循论文中提供的值,建议使用优化器的默认参数。
九、评价指标
评价指标与损失函数类似,可以理解为一个函数,用于判断模型在另一个未接触过的数据集(即验证数据集)上的性能,仅在报告时用于验证或描述测试结果。
1.binary_accuracy(二元准确率)
keras.metrics.binary_accuracy(y_true, y_pred, threshold=0.5)
2.categorical_accuracy(分类准确率)
keras.metrics.categorical_accuracy(y_true, y_pred)
3.sparse_categorical_accuracy(稀疏分类准确率)
keras.metrics.sparse_categorical_accuracy(y_true, y_pred)
4.top_k_categorical_accuracy(加k值的分类准确率)
keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=5)
5.sparse_top_k_categorical_accuracy(稀疏top_k分类准确率)
keras.metrics.sparse_top_k_categorical_accuracy(y_true, y_pred, k=5)
具体方法详解见https://zhuanlan.zhihu.com/p/95293440
十、配置模型
设计完神经网络后,利用compile命令可实现一步配置过程。需要提供三个参数:优化函数、损失函数和模型评价指标。
model.compile(optimizer=, loss=None, metrics=None, loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
optimizer: 字符串(优化器名)或者优化器实例。 详见 optimizers。
loss: 字符串(目标函数名)或目标函数。 详见 losses。 如果模型具有多个输出,则可以通过传递损失函数的字典或列表,在每个输出上使用不同的损失。 模型将最小化的损失值将是所有单个损失的总和。
metrics: 在训练和测试期间的模型评估标准。 通常你会使用 metrics = [‘accuracy’]。 要为多输出模型的不同输出指定不同的评估标准, 还可以传递一个字典,如 metrics = {‘output_a’:‘accuracy’}。
loss_weights: 可选的指定标量系数(Python 浮点数)的列表或字典, 用以衡量损失函数对不同的模型输出的贡献。 模型将最小化的误差值是由 loss_weights 系数加权的加权总和误差。 如果是列表,那么它应该是与模型输出相对应的 1:1 映射。 如果是张量,那么应该把输出的名称(字符串)映到标量系数。
sample_weight_mode: 如果你需要执行按时间步采样权重(2D 权重),请将其设置为 temporal。 默认为 None,为采样权重(1D)。 如果模型有多个输出,则可以通过传递 mode 的字典或列表,以在每个输出上使用不同的 sample_weight_mode。
weighted_metrics: 在训练和测试期间,由 sample_weight 或 class_weight 评估和加权的度量标准列表。
target_tensors: 默认情况下,Keras 将为模型的目标创建一个占位符,在训练过程中将使用目标数据。 相反,如果你想使用自己的目标张量(反过来说,Keras 在训练期间不会载入这些目标张量的外部 Numpy 数据), 您可以通过 target_tensors 参数指定它们。 它可以是单个张量(单输出模型),张量列表,或一个映射输出名称到目标张量的字典。
**kwargs: 当使用 Theano/CNTK 后端时,这些参数被传入 K.function。 当使用 TensorFlow 后端时,这些参数被传递到 tf.Session.run。
示例:
model = Sequential()
model.add(Dense(32, input_dim=10, activation="relu"))
model.add(Dense(16, activation="relu"))
model.add(Dense(1, activation="sigmoid"))
model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])
十一、训练模型
输入数据集,对于验证数据,通常的做法是将可用数据划分为三部分,比例为3:1:1,使用60%的数据进行训练,20%的数据进行验证,最后20%数据进行测试,比例应根据数据集的大小进行调整,如数据集超过100万个样本,最好将95%的数据用于训练,2%的数据用于验证,3%的数据用于测试,比例可调。
model.fit(x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None)
x: 训练数据的 Numpy 数组(如果模型只有一个输入), 或者是 Numpy 数组的列表(如果模型有多个输入)。 如果模型中的输入层被命名,你也可以传递一个字典,将输入层名称映射到 Numpy 数组。 如果从本地框架张量馈送(例如 TensorFlow 数据张量)数据,x 可以是 None(默认)。
y: 目标(标签)数据的 Numpy 数组(如果模型只有一个输出), 或者是 Numpy 数组的列表(如果模型有多个输出)。 如果模型中的输出层被命名,你也可以传递一个字典,将输出层名称映射到 Numpy 数组。 如果从本地框架张量馈送(例如 TensorFlow 数据张量)数据,y 可以是 None(默认)。
batch_size: 整数或 None。每次梯度更新的样本数。如果未指定,默认为 32。
epochs: 整数。训练模型迭代轮次。一个轮次是在整个 x 和 y 上的一轮迭代。 请注意,与 initial_epoch 一起,epochs 被理解为 「最终轮次」。模型并不是训练了 epochs 轮,而是到第 epochs 轮停止训练。
verbose: 0, 1 或 2。日志显示模式。 0 = 安静模式, 1 = 进度条, 2 = 每轮一行。
callbacks: 一系列的 keras.callbacks.Callback 实例。一系列可以在训练时使用的回调函数。 详见 callbacks。
validation_split: 0 和 1 之间的浮点数。用作验证集的训练数据的比例。 模型将分出一部分不会被训练的验证数据,并将在每一轮结束时评估这些验证数据的误差和任何其他模型指标。 验证数据是混洗之前 x 和y 数据的最后一部分样本中。
validation_data: 元组 (x_val,y_val) 或元组 (x_val,y_val,val_sample_weights), 用来评估损失,以及在每轮结束时的任何模型度量指标。 模型将不会在这个数据上进行训练。这个参数会覆盖 validation_split。
shuffle: 布尔值(是否在每轮迭代之前混洗数据)或者 字符串 (batch)。 batch 是处理 HDF5 数据限制的特殊选项,它对一个 batch 内部的数据进行混洗。 当 steps_per_epoch 非 None 时,这个参数无效。
class_weight: 可选的字典,用来映射类索引(整数)到权重(浮点)值,用于加权损失函数(仅在训练期间)。 这可能有助于告诉模型 「更多关注」来自代表性不足的类的样本。
sample_weight: 训练样本的可选 Numpy 权重数组,用于对损失函数进行加权(仅在训练期间)。 您可以传递与输入样本长度相同的平坦(1D)Numpy 数组(权重和样本之间的 1:1 映射), 或者在时序数据的情况下,可以传递尺寸为 (samples, sequence_length) 的 2D 数组,以对每个样本的每个时间步施加不同的权重。 在这种情况下,你应该确保在 compile() 中指定 sample_weight_mode=“temporal”。
initial_epoch: 整数。开始训练的轮次(有助于恢复之前的训练)。
steps_per_epoch: 整数或 None。 在声明一个轮次完成并开始下一个轮次之前的总步数(样品批次)。 使用 TensorFlow 数据张量等输入张量进行训练时,默认值 None 等于数据集中样本的数量除以 batch 的大小,如果无法确定,则为 1。
validation_steps: 只有在指定了 steps_per_epoch 时才有用。停止前要验证的总步数(批次样本)。
示例:
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation
# 获得训练数据集
np.random.seed(2018)
x_train = np.random.random((6000, 10))
y_train = np.random.randint(2, size=(6000, 1))
# 验证数据集
x_val = np.random.random((2000, 10))
y_val = np.random.randint(2, size=(2000, 1))
# 验证测试集
x_test = np.random.random((2000, 10))
y_test = np.random.randint(2, size=(2000, 1))
# 定义模型结构
model = Sequential()
model.add(Dense(64, input_dim=10, activation="relu")) # layer 1
model.add(Dense(32, activation="relu")) # layer 2
model.add(Dense(16, activation="relu")) # layer 3
model.add(Dense(8, activation="relu")) # layer 4
model.add(Dense(4, activation="relu")) # layer 5
model.add(Dense(1, activation="sigmoid")) # output layer
# 配置模型
model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])
# 训练模型
model.fit(x_train, y_train, batch_size=64, epochs=3, validation_data=(x_val, y_val))
训练模型输出:
Epoch 1/3
94/94 [----------------------------------------------------]- 0s 4ms/step - loss: 0.6933 - accuracy: 0.4987 - val_loss: 0.6932 - val_accuracy: 0.4985
Epoch 2/3
94/94 [----------------------------------------------------] - 0s 2ms/step - loss: 0.6932 - accuracy: 0.5120 - val_loss: 0.6930 - val_accuracy: 0.5060
Epoch 3/3
94/94 [----------------------------------------------------] - 0s 2ms/step - loss: 0.6930 - accuracy: 0.5127 - val_loss: 0.6931 - val_accuracy: 0.5060
进程已结束,退出代码为 0
最好使用已经配置的评价指标隔几个历元查看一次结果,来跟踪模型的性能。
十二、模型评估
Keras为序列模型提供的评估方法如下:
model.evaluate(x=None, y=None, batch_size=None, verbose=1, sample_weight=None, steps=None)
x: 测试数据的 Numpy 数组(如果模型只有一个输入), 或者是 Numpy 数组的列表(如果模型有多个输入)。 如果模型中的输入层被命名,你也可以传递一个字典,将输入层名称映射到 Numpy 数组。 如果从本地框架张量馈送(例如 TensorFlow 数据张量)数据,x 可以是 None(默认)。
y: 目标(标签)数据的 Numpy 数组,或 Numpy 数组的列表(如果模型具有多个输出)。 如果模型中的输出层被命名,你也可以传递一个字典,将输出层名称映射到 Numpy 数组。 如果从本地框架张量馈送(例如 TensorFlow 数据张量)数据,y 可以是 None(默认)。
batch_size: 整数或 None。每次评估的样本数。如果未指定,默认为 32。
verbose: 0 或 1。日志显示模式。 0 = 安静模式,1 = 进度条。
sample_weight: 测试样本的可选 Numpy 权重数组,用于对损失函数进行加权。 您可以传递与输入样本长度相同的扁平(1D)Numpy 数组(权重和样本之间的 1:1 映射), 或者在时序数据的情况下,传递尺寸为 (samples, sequence_length) 的 2D 数组,以对每个样本的每个时间步施加不同的权重。 在这种情况下,你应该确保在 compile() 中指定 sample_weight_mode=“temporal”。
steps: 整数或 None。 声明评估结束之前的总步数(批次样本)。默认值 None。
其中在参数x和y中提供了测试数据和测试标签。如果测试数据量很大,并且预计会占用大量内存,可以使用batch_size告诉Keras模型按批次进行预测,然后合并所有结果。
print(model.evaluate(x_test, y_test))
在evaluate方法中,模型返回损失值以及模型配置中定义的所有评价指标。可通过在模型的matrics_names属性中应用评价指标名称,来获取这些评价指标的值。
print(model.matrics_names)
另外,可以使用模型的predict方法,得到实际的预测结果。
pred = model.predict(x_test)
未完待续…