keras系列(一):参数设置

常用的 权重/偏置 初始化

常数初始化

为了在伪代码中形象化,让我们考虑一个具有64个输入和32个输出的神经网络的任意一层。

W = np.zeros((64, 32))
W = np.ones((64, 32))
W = np.ones((64, 32)) * C

虽然常量初始化很容易理解和理解,但使用这种方法的问题是,我们几乎不可能打破激活的对称性。因此,它很少被用作神经网络的权重初始化器。

均匀/正态分布

W = np.random.uniform(low=-0.05, high=0.05, size=(64, 32))
W = np.random.normal(0.0, 0.5, size=(64, 32))

在神经网络中,均匀分布和正态分布都可以用来初始化权值;然而,我们通常使用各种启发式来创建更好的初始化方案。

LeCun 均匀/正态分布

在这里,作者定义了一个参数Fin(称为fan in,或者是层的输入数)和Fout(称为fan out,或层的输出数)。使用这些值,我们可以应用统一的初始化。

F_in = 64
F_out = 32
limit = np.sqrt(3 / float(F_in))
W = np.random.uniform(low=-limit, high=limit, size=(F_in, F_out))

我们也可以用正态分布。Keras库使用一个截断的正态分布来构造上下限,以及零均值。

F_in = 64
F_out = 32
limit = np.sqrt(1 / float(F_in))
W = np.random.normal(0.0, limit, size=(F_in, F_out))

Glorot/Xavier 均匀/正态分布

在Keras库中使用的默认权值初始化方法称为Glorot初始化或Xavier初始化。
对于正态分布,其极限值是由平均的Fin和Fout组合而成,然后取平方根。然后使用一个零中心(均值为0)。

F_in = 64
F_out = 32
limit = np.sqrt(2 / float(F_in + F_out))
W = np.random.normal(0.0, limit, size=(F_in, F_out))

对于均匀分布,同样可以这样做,但一般在limit上给予更强的限制。

F_in = 64
F_out = 32
limit = np.sqrt(6 / float(F_in + F_out))
W = np.random.uniform(low=-limit, high=limit, size=(F_in, F_out))

学习使用这种初始化方法是非常有效的,我推荐它适用于大多数神经网络。

He et al. / Kaiming / MSRA 均匀/正态分布

我们通常在训练非常深的神经网络时使用这种方法,这种神经网络使用类似于relu的激活函数(特别是,一个’ PReLU ',或Parametric Rectified Linear Unit)。

F_in = 64
F_out = 32
limit = np.sqrt(6 / float(F_in))
W = np.random.uniform(low=-limit, high=limit, size=(F_in, F_out))

对于正态分布可以设置均值为0,方差为 2/Fin 开方,如下:

F_in = 64
F_out = 32
limit = np.sqrt(2 / float(F_in))
W = np.random.normal(0.0, limit, size=(F_in, F_out))

其中在Keras通常设置如下:

random_uniform:
权值被初始化为一致随机的小数值(-0.05,0.05)。换句话说,给定区间内的任何值都可能被取得。
random_normal:
根据高斯函数初始化权值,均值为零,标准差为0.05。
zero:
所有权重初始化为0。

完整的可选列表在链接如下:
keras权重和偏置初始化


激活函数

各类激活函数
左上:Step函数
右上:Sigmoid函数
左中:tanh函数
右中:Relu函数
左下:Leaky Relu函数(Relu变种)
右下:ELU函数(Relu变种)

Step函数

step函数是最简单的激活函数,形式如下:
这里写图片描述
然而,虽然这一阶跃函数是直观且易于使用的,但它是不可微的,当应用梯度下降和训练我们的网络时,它会导致问题。

Sigmoid函数

这里写图片描述
如下图所示,当输入变化时,它的输出变化很小(0,1)。从数学上讲,函数是连续的。典型的sigmoid函数在下图中表示:
这里写图片描述

一个神经元可以使用sigmoid来计算非线性函数 F(z = wx + b)。请注意,如果z = wx + b的值是非常大的且为正的,那么F(z = wx + b)趋于0,如果z = wx + b的值是非常大的且为负的,那么F(z = wx + b)趋于1。换句话说,具有sigmoid激活的神经元具有类似于感知器的行为,但变化是渐进的,输出值(如0.5539或0.123191)是完全可行的。

Tanh函数

这里写图片描述
tanh函数以零为中心,但当神经元饱和时,梯度仍然会消失。

ReLU函数

sigmoid并不是用于神经网络的唯一一种平滑激活函数。最近,一个非常简单的函数称为整流线性单元(ReLU)变得非常流行,因为它能产生非常好的效果。

一个ReLU被简单地定义为 F(x) = max(0,x),而非线性函数在下图中表示。正如你在下面的图中所看到的,负值的函数为零,它在正值方向线性增长。
这里写图片描述
截止到2015年,Relu函数是深度学习中最流行的激活函数,但是问题是当有一个值为0时,梯度就无法取得。

Leaky Relus函数

一种名为Leaky ReLUs的ReLUs变体,当该神经元未激活时,允许有一个小的、非零的梯度。

这里写图片描述
我们可以看到,这个函数确实被允许带有负值,而传统的ReLUs则是从0开始输出。

ELUs函数

这里写图片描述
也是Relus函数的一个变体,通常情况下ELUs会获得比Relus更好的分类精度。

Summary

在Keras部分的不同优化器中,我们将看到这些典型的sigmoid和ReLU函数,是开发一种学习算法的基本模块.。在几乎所有的情况下,我建议从一个ReLU开始,以获得基本的准确性(就像大多数发表在深度学习文献中的论文一样)。从那里你可以试着把你的标准ReLU换成一个Leaky ReLU变体。

我个人的偏好是,从一个ReLU开始,调优我的网络和优化器参数(体系结构、学习速率、正则化强度等),并注意准确性。一旦我对准确性相当满意,我就会在ELU上进行交换,并且通常会注意到根据数据集的分类精度提高了1-5%。

Keras支持许多激活函数,具体如下:
激活函数


损失函数

MSE

这是预测和真实值之间的平均平方误差。
这里写图片描述

Binary cross-entropy

这里写图片描述
其中 p 是模型的预测值,t 是目标值,这个目标函数适用于二分类的问题。

Categorical cross-entropy

这里写图片描述
这个目标函数适用于多分类任务,它也是与softmax激活关联的默认选择。

其他一些常用的目标函数如下:
目标函数


模型评判指标

Accuracy

这是对目标的正确预测的比例。

Precision

Precision = TP / (TP + NF)

Recall

Recall = TF / (NP + TF)
指标与目标函数相似,惟一的区别是它们不用于培训模型,而仅用于评估模型,在Keras中编译模型很简单。

其他一些常用的评判标准如下:
评判标准


Optimizers

Keras实现了梯度下降的快速变种,称为随机梯度下降(SGD)和两种更高级的优化技术,称为RMSprop和Adam。
RMSprop和Adam包括了动量的概念(速度分量),除了SGD的加速度分量。这允许以更高的计算速度更快地收敛。

SGD

即使有了一些更新的优化方法,SGD仍然是深度学习的工具,大多数神经网络都是通过SGD训练的,包括在像ImageNet这样的具有挑战性的图像数据集上获得最先进精度的网络。

当训练深度学习网络时,特别是当你第一次开始学习和学习的时候,SGD应该是你选择的优化器。然后需要设置适当的学习速率和正则化强度,需要训练网络的总时数,以及是否应该使用动量(如果有的话,是哪个值)或Nesterov加速。花些时间和SGD进行尽可能多的实验,并熟悉调优参数。

Adam/RMSprop

from keras.optimizers import RMSprop, Adam
...
OPTIMIZER = RMSprop() # optimizer
OPTIMIZER = Adam() # optimizer

更多常用的优化方法,如下:
optimizers

SGD的延伸

在实践中,将遇到两个主要的扩展。

Momentum(动量)

在SGD上应用的动量和我们的目标一样,我们的目标是在标准的重量更新中增加一个动量项,从而使我们的模型在更少的迭代中获得更低的损失(更高的精度)。

之前SGD的更新方式为:
这里写图片描述
现在介绍动态项 V ,如下:
这里写图片描述

动量项 r 通常设为0.9,虽然另一种常见的做法是将g调到0.5,直到学习稳定,然后将其增加到0.9,但是很少看到动量< 0.5。

Nesterov加速度

Nesterov加速可以被概念化为对动量的修正更新。
这里写图片描述
使用标准动量,我们计算梯度(小蓝向量),然后在梯度方向(大蓝色矢量)上大跳跃。在Nesterov加速度下,我们首先会在我们之前的梯度(棕色向量)的方向上做一个大的跳跃,测量梯度,然后做一个修正(红色矢量)绿色矢量是Nesterov加速度的最终修正的更新。

我个人的经验是,每当使用SGD时,要应用动量。在大多数情况下,可以设置为0.9。尽管有建议从0.5开始,并随着时间的增加,将其增加到更大的值。

对于Nesterov加速,我倾向于在较小的数据集上使用它,但是对于更大的数据集(如ImageNet),我几乎总是避免使用它。


Regularization

正则化帮助我们控制模型容量,确保我们的模型能够更好地在未训练的数据上正确分类,这也成为泛化能力。正则化是仅次于学习速率,模型最重要的参数(用于解决overfitting)。

正则化技术有多种类型,如L1正则化、L2正则化(通常称为权衰减)和Elastic Net[98],用于更新损失函数本身,增加一个附加参数来约束模型的容量。

然而,过多的正规化可能是一件坏事。在这种情况下,我们的模型在训练数据上可能表现不佳,无法对输入数据和输出类标签之间的关系建模(因为我们的模型容量太有限),即underfitting。

包含L2正则化的Cross-entropy loss函数:
这里写图片描述
同样,加入正则化的Multi-class SVM loss函数如下:
这里写图片描述
该方法根据学习速率的梯度倍数对权重进行更新,考虑正则化的权重更新为:
这里写图片描述

L2 正则化

模型的复杂度表示为权重平方和。
这里写图片描述

L1正则化

模型的复杂性表示为权重绝对值的和。
这里写图片描述

Elastic net 正则化

该模型的复杂性是由上述两种技术的组合而成的这里写图片描述

keras中正则化的设置如下:

from keras import regularizers
model.add(Dense(64, input_dim=64,
                kernel_regularizer=regularizers.l2(0.01),
                activity_regularizer=regularizers.l1(0.01)))

更多正则化的使用如下:
regularizers

Drop out

Drop out实际上是一种正规化的形式,目的是通过提高测试的准确性来防止过度拟合,也许是以牺牲训练的准确性为代价。

在网络结构的FC层之间,最常见的是加入drop out层,并设置 p = 0.5。在网络的前些层,也可以设置p = 0.10-0.25。

keras.layers.core.Dropout(rate, noise_shape=None, seed=None)

其中:

rate:它是一个介于0和1之间的浮点数,它表示输入单元的下降速率。
noise_shape:它是一个1维整数张量,它表示将与输入相乘的二维dropout mask的形状。
seed:它是一个被用作随机种子的整数。

其他超参数调节

batch sizes

通常设置为32,64,128 和 256

Batch normalization

Batch normalization:通过减少内部协变量转变加速深层网络训练,简称BN。顾名思义,是用来在网络中传递到下一层前规范给定输入的激活值。

如果认为x是mini-batch的激活值,则:
这里写图片描述

BN已经被证明在减少训练神经网络的时间上是非常有效的。批量标准化也有帮助稳定训练的附加好处,允许更大的学习速率和正则化强度。使用批量标准化并不会减少对这些参数的调整,但是这会使学习速率和正则化变得更简单,更容易调整。也就是说,我建议在几乎所有情况下使用批量标准化,因为它确实有很大的不同。

BN最大的缺点是,它实际上可以使网络运行速度慢下来。

keras.layers.normalization.BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001, center=

也就是说,额外的训练时间往往超过了负面效果,我强烈建议您将批处理规范化应用到您自己的网络体系结构中。

如在VGGNet中:

# first CONV => RELU => CONV => RELU => POOL layer set
model.add(Conv2D(32, (3, 3), padding="same",
input_shape=inputShape))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(32, (3, 3), padding="same"))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

学习率

梯度下降算法是通过学习速率来控制的,在训练你自己的模型时,学习率是最重要的参数。

通常设置学习速率是恒定的,然后在不改变学习速率的情况下训练网络。这种方法在某些情况下可能会很有效,但随着时间的推移,降低我们的学习速度通常会表现更好。

The Standard Decay Schedule in Keras

一种常见的学习率衰减方法是将初始学习速率除以当前训练的总次数,如:用初始学习速率0.01来训练网络,总共训练次数为epochs,因此衰减= 0.01 / epochs。

在keras中使用学习率变化的公式如下:
这里写图片描述
如果设置decay (公式中 r)为0,则不更新学习率。
这里写图片描述

opt = SGD(lr=0.01, decay=0.01 / 40, momentum=0.9, nesterov=True)
model = MiniVGGNet.build(width=32, height=32, depth=3, classes=10)
model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])

通过学习速率衰减,我们不仅可以提高分类精度,还可以减少过度拟合的影响,从而提高模型的泛化能力。

Step-based Decay

当我们的学习速度逐步衰减时,我们有两个选择:

1.定义一个方程,模型是我们希望达到的学习速率的分段下降。
2.使用我所称的***ctrl + c方法***来训练一个深度学习网络,在一个给定的学习速率下,我们训练一些不同的时代,最终发现验证性能已经停滞,然后按ctrl + c停止脚本,调整我们的学习速度,并继续训练。

在keras自定义学习率函数

学习率函数设置如下:
这里写图片描述
F越大,则学习率下降的越慢。
公式形式如下:

alpha = initAlpha * (factor ** np.floor((1 + epoch) / dropEvery))

在keras的代码如下:

from keras.callbacks import LearningRateScheduler

从Keras库中导入我们的LearningRateScheduler,这个类将使我们能够定义我们自己的自定义学习速率调度器。

def step_decay(epoch):
	# initialize the base initial learning rate, drop factor, and
	# epochs to drop every
	initAlpha = 0.01
	factor = 0.25
	dropEvery = 5

	# compute learning rate for the current epoch
	alpha = initAlpha * (factor ** np.floor((1 + epoch) / dropEvery))

	# return the learning rate
	return float(alpha)

定义初始学习速率0.01,下降因子0.25,设置dropEvery = 5,这意味着我们每5个epochs会将学习速率降低0.25。

# define the set of callbacks to be passed to the model during
# training
callbacks = [LearningRateScheduler(step_decay)]

# initialize the optimizer and model
opt = SGD(lr=0.01, momentum=0.9, nesterov=True)
model = MiniVGGNet.build(width=32, height=32, depth=3, classes=10)
model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])

# train the network
H = model.fit(trainX, trainY, validation_data=(testX, testY),batch_size=64, epochs=40, callbacks=callbacks, verbose=1)

猜你喜欢

转载自blog.csdn.net/hanzy88/article/details/79507396