第17章 学习速度设计

神经网络的训练是很困难的优化问题。传统的随机梯度下降算法配合设计好的学习速度有时效果更好。本章包括:

  • 调整学习速度的的原因
  • 如何使用按时间变化的学习速度
  • 如何使用按训练次数变化的学习速度

我们开始吧。

17.1 学习速度

随机梯度下降算法配合设计好的速度可以增强效果,减少训练时间:也叫学习速度退火或可变学习速度。其实就是慢慢调整学习速度,而传统的方法中学习速度不变。

最简单的调整方法是学习速度随时间下降,一开始做大的调整加速训练,后面慢慢微调性能。两个简单的方法:

  • 根据训练轮数慢慢下降
  • 到某个点下降到某个值

我们分别探讨一下。

17.2 电离层分类数据集

本章使用电离层二分类数据集,研究电离层中的自由电子。分类g(好)意味着电离层中有某个结构;b(坏)代表没有,信号通过了电离层。数据有34个属性,351个数据。

10折检验下最好的模型可以达到94~98%的准确度。数据在本书的data目录下,也可以自行下载,重命名为ionosphere.csv。数据集详情请参见UCI机器学习网站。

17.3 基于时间的学习速度调度

Keras内置了一个基于时间的学习速度调度器:Keras的随机梯度下降SGD类有decay参数,按下面的公式调整速度:

  1. LearnRate = LearnRate x (1 / 1 + decay x epoch)

默认值是0:不起作用。

  1. LearningRate = 0.1 * 1/(1 + 0.0 * 1)
  2. LearningRate = 0.1

如果衰减率大于1,例如0.001,效果是:

  1. Epoch Learning Rate
  2. 1 0.1
  3. 2 0.0999000999
  4. 3 0.0997006985
  5. 4 0.09940249103
  6. 5 0.09900646517

到100轮的图像:

17.2 按时间

可以这样设计:

  1. Decay = LearningRate / Epochs
  2. Decay = 0.1 / 100
  3. Decay = 0.001

下面的代码按时间减少学习速度。神经网络有1个隐层,34个神经元,激活函数是整流函数。输出层是1个神经元,激活函数是S型函数,输出一个概率。学习率设到0.1,训练50轮,衰减率0.002,也就是0.1/50。学习速度调整一般配合动量使用:动量设成0.8。代码如下:

  1. import pandas
  2. import numpy
  3. from keras.models import Sequential
  4. from keras.layers import Dense
  5. from keras.optimizers import SGD
  6. from sklearn.preprocessing import LabelEncoder
  7. # fix random seed for reproducibility
  8. seed = 7
  9. numpy.random.seed(seed)
  10. # load dataset
  11. dataframe = pandas.read_csv("ionosphere.csv", header=None)
  12. dataset = dataframe.values
  13. # split into input (X) and output (Y) variables
  14. X = dataset[:,0:34].astype(float)
  15. Y = dataset[:,34]
  16. # encode class values as integers
  17. encoder = LabelEncoder()
  18. encoder.fit(Y)
  19. Y = encoder.transform(Y)
  20. # create model
  21. model = Sequential()
  22. model.add(Dense(34, input_dim=34, init='normal', activation='relu')) model.add(Dense(1, init='normal', activation='sigmoid'))
  23. # Compile model
  24. epochs = 50
  25. learning_rate = 0.1
  26. decay_rate = learning_rate / epochs
  27. momentum = 0.8
  28. sgd = SGD(lr=learning_rate, momentum=momentum, decay=decay_rate, nesterov=False) model.compile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])
  29. # Fit the model
  30. model.fit(X, Y, validation_split=0.33, nb_epoch=epochs, batch_size=28)

训练67%,测试33%的数据,准确度达到了99.14%,高于不使用任何优化的95.69%:

  1. 235/235 [==============================] - 0s - loss: 0.0607 - acc: 0.9830 - val_loss:
  2. 0.0732 - val_acc: 0.9914
  3. Epoch 46/50
  4. 235/235 [==============================] - 0s - loss: 0.0570 - acc: 0.9830 - val_loss:
  5. 0.0867 - val_acc: 0.9914
  6. Epoch 47/50
  7. 235/235 [==============================] - 0s - loss: 0.0584 - acc: 0.9830 - val_loss:
  8. 0.0808 - val_acc: 0.9914
  9. Epoch 48/50
  10. 235/235 [==============================] - 0s - loss: 0.0610 - acc: 0.9872 - val_loss:
  11. 0.0653 - val_acc: 0.9828
  12. Epoch 49/50
  13. 235/235 [==============================] - 0s - loss: 0.0591 - acc: 0.9830 - val_loss:
  14. 0.0821 - val_acc: 0.9914
  15. Epoch 50/50
  16. 235/235 [==============================] - 0s - loss: 0.0598 - acc: 0.9872 - val_loss:
  17. 0.0739 - val_acc: 0.9914

17.3 基于轮数的学习速度调度

也可以固定调度:到某个轮数就用某个速度,每次的速度是上次的一半。例如,初始速度0.1,每10轮降低一半。画图就是:

17.3 轮数

Keras的LearningRateScheduler作为回调参数可以控制学习速度,取当前的轮数,返回应有的速度。还是刚才的网络,加入一个step_decay函数,生成如下学习率:

  1. LearnRate = InitialLearningRate x Droprate ^ floor((1+Epoch)/EpochDrop)

InitialLearningRate是初始的速度,DropRate是减速频率,EpochDrop是降低多少:

  1. import pandas
  2. import pandas
  3. import numpy
  4. import math
  5. from keras.models import Sequential
  6. from keras.layers import Dense
  7. from keras.optimizers import SGD
  8. from sklearn.preprocessing import LabelEncoder
  9. from keras.callbacks import LearningRateScheduler
  10. # learning rate schedule
  11. def step_decay(epoch):
  12. initial_lrate = 0.1
  13. drop = 0.5
  14. epochs_drop = 10.0
  15. lrate = initial_lrate * math.pow(drop, math.floor((1+epoch)/epochs_drop))
  16. return lrate
  17. # fix random seed for reproducibility
  18. seed = 7
  19. numpy.random.seed(seed)
  20. # load dataset
  21. dataframe = pandas.read_csv("../data/ionosphere.csv", header=None)
  22. dataset = dataframe.values
  23. # split into input (X) and output (Y) variables
  24. X = dataset[:,0:34].astype(float)
  25. Y = dataset[:,34]
  26. # encode class values as integers
  27. encoder = LabelEncoder()
  28. encoder.fit(Y)
  29. Y = encoder.transform(Y)
  30. # create model
  31. model = Sequential()
  32. model.add(Dense(34, input_dim=34, init='normal', activation='relu'))
  33. model.add(Dense(1, init='normal', activation='sigmoid'))
  34. # Compile model
  35. sgd = SGD(lr=0.0, momentum=0.9, decay=0.0, nesterov=False) model.compile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])
  36. # learning schedule callback
  37. lrate = LearningRateScheduler(step_decay)
  38. callbacks_list = [lrate]
  39. # Fit the model
  40. model.fit(X, Y, validation_split=0.33, nb_epoch=50, batch_size=28, callbacks=callbacks_list)

效果也是99.14%,比什么都不做好:

  1. Epoch 45/50
  2. 235/235 [==============================] - 0s - loss: 0.0546 - acc: 0.9830 - val_loss:
  3. 0.0705 - val_acc: 0.9914
  4. Epoch 46/50
  5. 235/235 [==============================] - 0s - loss: 0.0542 - acc: 0.9830 - val_loss:
  6. 0.0676 - val_acc: 0.9914
  7. Epoch 47/50
  8. 235/235 [==============================] - 0s - loss: 0.0538 - acc: 0.9830 - val_loss:
  9. 0.0668 - val_acc: 0.9914
  10. Epoch 48/50
  11. 235/235 [==============================] - 0s - loss: 0.0539 - acc: 0.9830 - val_loss:
  12. 0.0708 - val_acc: 0.9914
  13. Epoch 49/50
  14. 235/235 [==============================] - 0s - loss: 0.0539 - acc: 0.9830 - val_loss:
  15. 0.0674 - val_acc: 0.9914
  16. Epoch 50/50
  17. 235/235 [==============================] - 0s - loss: 0.0531 - acc: 0.9830 - val_loss:
  18. 0.0694 - val_acc: 0.9914

17.5 调整学习速度的技巧

这些技巧可以帮助调参:

  1. 增加初始学习速度。因为速度后面会降低,一开始速度快点可以加速收敛。
  2. 动量要大。这样后期学习速度下降时如果方向一样,还可以继续收敛。
  3. 多试验。这个问题没有定论,需要多尝试。也试试指数下降和什么都不做。

17.6 总结

本章关于调整学习速度。总结一下:

  • 调整学习速度为什么有效
  • 如何在Keras使用基于时间的学习速度下降
  • 如何自己编写下降速度函数
17.6.1 下一章

第四章到此结束,包括Keras的一些高级函数和调参的高级方法。下一章研究卷积神经网络(CNN),在图片和自然语言处理上尤为有效。