梯度爆炸与梯度消失是神经网络中最常见的问题之一,常出现于神经网络模型在反向传播时,由于需要对权重进行更改,所以存在梯度爆炸与梯度消失的风险。

        梯度爆炸:在BP网络之中,由于激活函数的原因,导致反向传播时,样本的梯度越来越大,导致权重更新变化大,导致算法分散。

        梯度消失:在BP网络之中,由于激活函数的原因,导致反向传播时,样本的梯度越来越小,基本上接近于0,权重更新基本不发生变化,导致无法得到一个良好的解。

        解决方法

        (一),使用Glorot和He初始化

        他可以显著的缓解不稳定梯度的问题,这里使用对权重进行随机初始化,可以让权重符合正态分布,平均方差等于输入方差+输出方差的一半,记作

神经网络 输出层梯度 神经网络梯度消失原因_深度学习

,对于Glorot初始化,是让权重符合正态分布,均值为0,方差为平均方差的倒数。它其实是防止再反向传播时,权重变化太大而设置的。

        对于Glorot初始化,适用于tanh,logistic,softmax等激活函数,而He初始化,适用于ReLU及其变体,而方差也有所改变,为2/输入方差,LeCun适用于SELU,方差为1/输入方差。

        在keras中默认为Glorot初始化,如果要设置He初始化或者是自定义方差,方法如下:

keras.layers.Dense(10,activation='relu',kernel_initializer='he_normal')
he_avg_init=keras.initializers.VarianceScaling(scale=2,mode='fan_avg',distribution='uniform')
keras.layers.Dense(10,activation='sigmoid',kernel_initializer=he_avg_init)

        (二)使用非饱和激活函数

        LeakyReLU:他输入ReLU的变体=max(ax,x),由于存在a,所以导致梯度永远不会消失。

keras.layers.LeakyReLU(alpha=0.2)

        ELU:称之为指数线性单位。

        SeLU:作为ELU的变体,需要满足4个条件:1.输入特征必须标准化,2.隐藏层权重必须使用LeCun正态初始化,3.神经网络必须是顺序API,4.保证归一化

        

layer =keras.layers.Dense(10,activation='selu',kernel_initializer='lecun_normal')

        (三)批量归一化

        在每个隐藏层的激活函数前后添加一个归一化操作,归一化的作用是使输入进行缩放以及偏移。

model =keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28,28]),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(300,activation='elu',kernel_initializer='he_normal'),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(100,activation='elu',kernel_initializer='he_normal'),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(10,activation='softmax')
])

        由于训练中可以得到均值以及误差,但是在测试中可能是输入单个样本,这样子均值与误差无意义,所以可以使用训练整体的均值以及误差,或者使用移动平均值。

        keras.BatchNormalization()层有四个参数:输出缩放向量,输出偏移向量,均值向量,方差向量。

[(var.name,var.trainable) for var in model.layers[1].variables]

        可以通过这个方法查看。

        另外,如果要在激活函数前面放置归一化,那么需要在隐藏层中删除激活函数。

model =keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28,28]),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(300,kernel_initializer='he_normal',use_bias=False),
    keras.layers.BatchNormalization(),
    keras.layers.Activation('elu'),
    keras.layers.Dense(100,kernel_initializer='he_normal',use_bias=False),
    keras.layers.BatchNormalization(),
    keras.layers.Activation('elu'),
    keras.layers.Dense(10,activation='softmax')
])

        (四)对梯度进行裁剪

        缓解反向传播梯度爆炸

        存在clipvalue和clipnorm两个超参数,第一个是将大于x的数变成x,而第二个是将其进行以x进行标准化。

optimizer=keras.optimizers.SGD(clipvalue=1.0)
model.compile(loss='mse',optimizer=optimizer)
optimizer=keras.optimizers.SGD(clipnorm=1.0)
model.compile(loss='mse',optimizer=optimizer)

实例:

        在MNIST数据集上训练一个深度MLP,要求精度在98以上,使用随机搜索找到最佳学习率。

        

(x_train_full, y_train_full), (x_test, y_test) = keras.datasets.mnist.load_data()
x_val,x_train = x_train_full[:5000]/255.,x_train_full[5000:]/255.
y_val,y_train=y_train_full[:5000],y_train_full[5000:]
x_test=x_test/255.
def build_models(learning_rate=3e-3):
    n_hidden=3
    n_neurons=400
    model =keras.models.Sequential()
    model.add(keras.layers.Flatten(input_shape=[28,28]))
    for layer in range(n_hidden):
        n_neurons -=100
        model.add(keras.layers.Dense(n_neurons,activation='relu'))
    model.add(keras.layers.Dense(10,activation='softmax'))
    model.compile(loss='sparse_categorical_crossentropy',
                 optimizer=keras.optimizers.SGD(lr=learning_rate),
                  metrics=['accuracy']
                 )
    return model
model=build_models()
model.save('keras_model.h5')
checkpoint_cb = keras.callbacks.ModelCheckpoint('keras_model.h5')
model.summary()
keras_classifier.fit(x_train,y_train,epochs=100,validation_data=(x_val,y_val),callbacks = [checkpoint_cb,keras.callbacks.EarlyStopping(patience=10)])
keras_classifier.score(x_test,y_test)
import tensorflow
keras_classifier =tensorflow.keras.wrappers.scikit_learn.KerasClassifier(build_models)
type(keras_classifier)
from scipy.stats import reciprocal
from sklearn.model_selection import RandomizedSearchCV
params = {
          'learning_rate':reciprocal(3e-4,3e-2)}
rndsearchcv =  RandomizedSearchCV(keras_classifier,params,cv=10)
rndsearchcv.fit(x_train,y_train,epochs=100)
model =rndsearchcv.best_estimator_.model
model.evaluate(x_test,y_test)