/ DEEPLEARNING

深度学习调参经验总结

一. 网络中loss表现过于震荡

1.1 模型拟合能力不够导致模型震荡

model(层数:input(30,300,3) ==> ful_collected_layer(30,300,64) ==> lstm ==> ful_collected_layer ==> output)此时模型的loss由1到192震动太大,acc也是在一个epoch中时好时坏,由此考虑到是模型的分类能力的问题(可能处理不了非线性或者是异或的问题)

"""增加了一层全连接层,之后效果显著,模型虽然也存在loss和acc会有极小幅度的震荡,但是趋向于收敛"""
input(30,300,3) ==> ful_collected_layer(30,300,64) ==> lstm ==> ful_collected_layer ==> ful_collected_layer ==> output

1.2 batch size 设置过小导致模型震荡

之前模型用的是batch_size = 30,经过增大batch_size之后,模型的震荡程度也减小。这里如果GPU显存小的情况下,只能将batch设置小。

1.3 输入模型的数据没有shuffle导致模型震荡

之前数据没有进行shuffle,导致在某一个batch_size中学习到的全是正样本,某一个batch_size里面又全是负样本,shuffle之后,振荡减小。

np.random.seed(110) # 设定种子数,不然下面shuffle之后的y无法与X对应上
np.random.shuffle(X)
np.random.seed(110)
np.random.shuffle(y)

二. 网络经过多轮迭代依然无法上升了,acc始终在79%

2.1 增大学习率

开始学习率设置的是learning_rate = 0.001,之后增大10倍,设置为learning_rate = 0.01之后,acc在70多轮的时候就能提升到90% ,300轮之后能到97%。

理由:当我们把学习率设置较小的时候,那么梯度下降的时候迈的步子就小,可能在遇到大的坑的时候就出去,然后就一致在坑里徘徊,最终只能达到局部最优,无法达到全局最优,调参的过程中应该首先实验大的学习率,然后再依次减小实验。

learning_rate = 0.1  ==> learning_rate = 0.01  ==> learning_rate = 0.001

2.2 优化器的选择

实验下其他的梯度下降的优化器(optimizer),比如Adam,SGD,Adadelta,RMSProp,Momentum等,一般来说Adam较快,SGD最慢,但是却是最准确和稳定的,因此可以先用Adam进行实验,最后用SGD进行调参。

tf.train.AdadeltaOptimizer(learning_rate=0.001, rho=0.95, epsilon=1e-08, use_locking=False, name=’Adadelta’)

tf.train.MomentumOptimizer(learning_rate, momentum, use_locking=False, name=’Momentum’, use_nesterov=False)

tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-08, use_locking=False, name=’Adam’)

tf.train.GradientDescentOptimizer(learning_rate, use_locking=False,name=’GradientDescent’)

三. 遇到loss和weights在训练中全是nan值的情况

3.1 查看输入数据中是否存在nan值

检查自己做完预处理的数据,看下是否存在nan值(比如需要计算0/0和log0的情况)

"""检验下input_data中是否存在nan值"""
input_data = np.array(input_data).reshape([-1,n_input])
# 这里的input_data 是三维数组必须转成2d
input_data_pd = pd.DataFrame(input_data)
if np.any(input_data_pd.isnull()) == True:
print("input data has nan value!")
list_nan = list(map(tuple, np.argwhere(np.isnan(input_data_pd.values))))
print(list_nan)

3.2 梯度爆炸或者是梯度消失

可能是梯度爆炸,有以下解决方式

  • (1)预训练+微调
  • (2)梯度剪切 + 权重正则
  • (3)使用不同的激活函数,比如之前用relu,可以换成tanh或者是elu
  • (4)使用batchnorm
  • (5)使用LSTM网络(如果之前用的是RNN结构)
  • (6)使用残差结构

以下是几种在不改变模型层数和结构的情况下解决梯度爆炸和梯度消失的方案。

"""权重L2正则化"""
cross_entropy = -tf.reduce_sum(ys * 	tf.log(tf.clip_by_value(tf.nn.softmax(prediction), 1e-10, 1.0)))
weights_lossL2 = tf.add(tf.nn.l2_loss(weights_in),tf.nn.l2_loss(weights_out)) * 0.01
regularzation_loss = cross_entropy + weights_lossL2
cost = tf.reduce_mean(regularzation_loss)
"""梯度剪裁"""
opt = tf.train.MomentumOptimizer(learning_rate=0.001, momentum=0.5)
# Compute the gradients for a list of variables.
grads_and_vars = opt.compute_gradients(cross_entropy, 	 tf.trainable_variables())
# grads_and_vars is a list of tuples (gradient, variable).  Do whatever you
# need to the 'gradient' part, for example cap them, etc.
capped_grads_and_vars = [(tf.clip_by_value(gv[0], 0.1, 5.), gv[1]) for gv in grads_and_vars]
# Ask the optimizer to apply the capped gradients.
optimizer = opt.apply_gradients(capped_grads_and_vars)

3.3 使用了梯度参见和L2正则之后出现Loss增大的情况

在使用了梯度裁剪之后,其实只是人为的控制梯度的变化(将weights控制在小范围内(0.1,5)之间),此时权重依旧可以通过BP算法向负梯度的方向前进,但是由于人为的控制,导致weight的梯度极有可能朝着正梯度方向进行,这就会导致可以更新权重,但是loss反而增大的原因。

3.4 这里必须要修改模型结构

举一个例子:利用4层全连接层作为一个分类器,来训练。经历过以上所有的方式(包括调整激活函数等),依旧无法使得模型的loss减少,当我将层数降低为3层的时候,模型loss开始收敛,那么这就说明当无法使得模型收敛的时候,其实极有可能是模型的结构问题,需要重新设计模型的结构层数。

四.训练中模型loss不收敛的几种情况

总结:

  • train loss 不断下降,val loss不断下降 ==> 说明网络仍在学习
  • train loss 不断下降,val loss趋于不变 ==> 说明网络过拟合
  • train loss 趋于不变,val loss不断下降 ==> 说明数据集100%有问题
  • train loss 趋于不变,val loss趋于不变 ==> 说明学习遇到瓶颈,需要减小学习率或批量数目
  • train loss 不断上升,val loss不断上升 ==> 说明网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题
  • train loss 到稳定的时候反而比val loss还要高 ==> 测试集数据量太小了,误差计算算法有问题
  • train loss 和 val loss趋于不变,但是val loss趋于0,而train loss却还很高 ==> 说明使用dropout层后模型拟合能力变差,去掉dropout层。
  • train loss 和 val loss同时极缓的形式增大,这里可以考虑降低学习率或者是从这里进行截断,以loss最低点作为模型最优点。