大家好,今天和大家分享一下如何使用 TensorFlow 自定义 指数学习率下降阶梯学习率下降余弦学习率下降 方法,并使用 Mnist数据集验证自定义的学习率下降策略

创建的自定义学习率类方法,需要继承 tf.keras.optimizers.schedules.LearningRateSchedule


1. 指数学习率下降

指数学习率下降公式为: 

其中  代表初始的学习率, 代表学习率衰减系数, 代表epoch,即每次迭代学习率衰减一次

以初始学习率  ,学习率衰减系数  , 为例指数学习率下降曲线如下图所示。

如果以step作为指数调整的标准,那么指数 n 等于当前的 step 除以 一个 epoch 包含的总 step。

我这里以 epoch 作为指数调整的标准。


首先创建一个学习率自定义类,继承 keras.optimizers.schedules.LearningRateSchedule 自定义学习率调度器

先对所有的属性完成初始化,其中 self.current 返回训练时每一个 step 的学习率self.epoch 代表指数学习率计算公式中的指数项 n,self.learning_rate_list 用于记录训练时每个 epoch 的学习率

在__call__方法中,step % self.print_step,其中 step 代表训练时传入的当前的 step,而 print_step外部指定的每经过多少个step调整一次学习率,并记录下当前 epoch 的学习率,返回调整后的学习率如果不满足 if 条件,那么这些 step 的学习率就是上一次调整后的学习率

以 epoch 作为指数调整标准的代码如下:

# ------------------------------------------------------------------ #
# 当前学习率 = 初始学习率 * 衰减系数 ^{迭代了多少次}
# ------------------------------------------------------------------ #
# eager模式防止graph报错
tf.config.experimental_run_functions_eagerly(True)
# ------------------------------------------------------------------ #
# 继承学习率的类
class ExponentialDecay(keras.optimizers.schedules.LearningRateSchedule):'''initial_lr: 初始的学习率decay_rate: 学习率衰减系数min_lr: 学习率下降的最小print_step: 训练时多少个batch打印一次学习率'''# 初始化def __init__(self, initial_lr, decay_rate, min_lr, print_step):# 继承父类的初始化方法super(ExponentialDecay, self).__init__()# 属性分配self.initial_lr = tf.cast(initial_lr, tf.float32)self.decay_rate = tf.cast(decay_rate, tf.float32)self.min_lr = tf.cast(min_lr, tf.float32) self.print_step = print_step# 记录记录每个epoch的学习率self.learning_rate_list = []# 最开始时,学习率为初始学习率self.current = self.initial_lr# 初始的迭代次数为0self.epoch = 0# 前向传播def __call__(self, step):  # 每多少个batch调整一次学习率, 一个batch处理32张图if step % self.print_step == 0:# 学习率指数下降,设置为每个epoch调整一次learning_rate = self.initial_lr * pow(self.decay_rate, self.epoch)# 调整当前学习率, 每一轮的学习率不能低于最小学习率self.current = tf.where(learning_rate>self.min_lr, learning_rate, self.min_lr)# 迭代次数加一self.epoch = self.epoch + 1# 将当前学习率保存下来self.learning_rate_list.append(learning_rate.numpy().item())# 打印学习率变化print('learning_rate:', learning_rate.numpy().item())# 返回调整后的学习率return self.current# 否则就返回上一次调整的学习率else:return self.current

2. 阶梯学习率下降

思路:每经过多少个 step 之后,学习率下降为上一次的 decay_rate 倍。例如初始学习率为0.001每经过三个 epoch,学习率就下降为原来的 0.5 倍,实现分段下降,示意图如下:


首先创建一个学习率自定义类,继承 keras.optimizers.schedules.LearningRateSchedule 自定义学习率调度器

先对所有的属性完成初始化,其中 self.change_step 在外部定义,代表每经过多少个 step 调整一次学习率。调整方式是当前学习率 self.current 乘以调整倍数 self.decay_rate,得到调整后的学习率并返回结果。如果不满足 if 条件,即当前 step 不需要调整,就返回上一次调整后的学习率。self.learning_rate_list 列表中记录训练过程中的每个 epoch 的学习率,训练完成后之后可以读取查看。

# ------------------------------------------------------------------ #
# 自定义的分段常数下降方法
# ------------------------------------------------------------------ #
# eager模式防止graph报错
tf.config.experimental_run_functions_eagerly(True)
# ------------------------------------------------------------------ #
# 继承学习率的类
class PiecewiseConstantDecay(keras.optimizers.schedules.LearningRateSchedule):'''initial_lr: 初始的学习率decay_rate: 学习率衰减系数min_lr: 学习率下降的最小change_step: 多少个epoch下降一次print_step: 训练时多少个step打印一次学习率'''# 初始化def __init__(self, initial_lr, decay_rate, min_lr, change_step, print_step):# 继承父类的初始化方法super(PiecewiseConstantDecay, self).__init__()# 属性分配self.initial_lr = tf.cast(initial_lr, tf.float32)self.decay_rate = tf.cast(decay_rate, tf.float32)self.min_lr = tf.cast(min_lr, tf.float32) self.change_step = change_stepself.print_step = print_step# 记录记录每个epoch的学习率self.learning_rate_list = []# 最开始时,学习率为初始学习率self.current = self.initial_lr# 前向传播def __call__(self, step):  # 这个step不是epoch# 多少个step记录一次学习率,外部指定为一个epoch记录一次if step % self.print_step == 0:# 训练过程中打印每一个epoch的学习率print('current learning_rate is ', self.current.numpy().item())# 记录下当前epoch的学习率self.learning_rate_list.append(self.current.numpy().item())# 多少个step调整一次学习率if step % self.change_step == 0:# 计算调整后的学习率learning_rate = self.current * self.decay_rate# 更新当前学习率指标, 学习率不能小于指定的最小值self.current = tf.where(learning_rate>self.min_lr, learning_rate, self.min_lr)# 返回调整后的学习率return self.current# 如果为满足调整要求,就返回上一次调整的学习率else:return self.current

3. 余弦学习率下降

余弦学习率下降公式为:

其中, 代表初始学习率, 是指当前是第几个 step, 是指多少个 step 之后学习率衰减为0

以初始学习率为 0.001,所有 epoch 结束后学习率降为 0 为例,学习率余弦下降曲线如下:


首先创建一个学习率自定义类,继承 keras.optimizers.schedules.LearningRateSchedule 自定义学习率调度器

先对所有的属性完成初始化,其中 self.current 用来记录当前 step 的学习率self.learning_rate_list 用来记录训练时所有 step 的学习率,训练结束后可调用查看。

训练时模型会传入当前的 step,调整每一个 step 的学习率 learning_rate,并且要求调整后的学习率不能低于最小学习率 self.min_lr,使用 tf.where() 函数对比调整后的学习率和最小学习率,选出最大的作为返回结果的学习率。

# ------------------------------------------------------------------ #
# 余弦学习率下降
# ------------------------------------------------------------------ #
# eager模式防止graph报错
tf.config.experimental_run_functions_eagerly(True)
# ------------------------------------------------------------------ #
# 继承学习率的类
class CosineDecay(keras.optimizers.schedules.LearningRateSchedule):'''initial_lr: 初始的学习率decay_rate: 学习率衰减到最低点的步长min_lr: 学习率下降的最小print_step: 训练时多少个step打印一次学习率    '''# 初始化def __init__(self, initial_lr, decay_step, min_lr, print_step):# 继承父类初始化方法super(CosineDecay, self).__init__()# 属性分配self.initial_lr = tf.cast(initial_lr, dtype=tf.float32)self.decay_step = tf.cast(decay_step, dtype=tf.float32)self.min_lr = tf.cast(min_lr, dtype=tf.float32)self.print_step = print_step# 最开始的当前学习率等于初始学习率self.current = self.initial_lr# 记录每个epoch的学习率值self.learning_rate_list = []# 前向传播def __call__(self, step):# 余弦衰减计算公式learning_rate = 0.5 * self.initial_lr * (1 + tf.math.cos(step*math.pi / self.decay_step))# 更新当前学习率指标, 学习率不能小于指定的最小值self.current = tf.where(learning_rate>self.min_lr, learning_rate, self.min_lr)# 记录每个step的学习率self.learning_rate_list.append(self.current.numpy().item())# 多少个step打印一次学习率,外部设置每个epoch打印一次学习率if step % self.print_step == 0:# 在训练时打印当前学习率print('learning_rate has changed to: ', self.current.numpy().item())return self.current

4. 实验验证

这里以学习率余弦衰减策略为例,来验证上面定义的学习率方法能不能用。

数据预处理和模型构建部分就不讲了,这部分都很基础。直接看到第(6)部分模型训练

首先需要对我们定义的学习率下降的类 CosineDecay 进行实例化,传入计算公式中所需的初始学习率 initial_lr余弦值下降到0所需的步长 decay_step,用变量 cosinedecay 来接收。

将自定义的学习率衰减方法传入至Adam优化器,这样在训练时就能接收到模型传入的每个step,用于计算衰减。

自定义方法也可以参照官方文档:自定义的学习速率调度

以Mnist手写数据集图像10分类问题为例,完整代码如下:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
import math# 调用GPU加速
gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
for gpu in gpus:tf.config.experimental.set_memory_growth(gpu, True)# ------------------------------------------------------------------ #
# (1)读取手写数字数据集
# ------------------------------------------------------------------ #
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
print('x_train.shape:', x_train.shape, 'y_train.shape:', y_train.shape) # (60000, 28, 28) , (60000,)
print('x_test.shape:', x_test.shape)  # (10000, 28, 28)# 记录一共训练多少张图
total_train_num = x_train.shape[0]# ------------------------------------------------------------------ #
# (2)余弦学习率下降
# ------------------------------------------------------------------ #
# eager模式防止graph报错
tf.config.experimental_run_functions_eagerly(True)
# ------------------------------------------------------------------ #
# 继承学习率的类class CosineDecay(keras.optimizers.schedules.LearningRateSchedule):'''initial_lr: 初始的学习率decay_rate: 学习率衰减到最低点的步长min_lr: 学习率下降的最小print_step: 训练时多少个step打印一次学习率    '''# 初始化def __init__(self, initial_lr, decay_step, min_lr, print_step):# 继承父类初始化方法super(CosineDecay, self).__init__()# 属性分配self.initial_lr = tf.cast(initial_lr, dtype=tf.float32)self.decay_step = tf.cast(decay_step, dtype=tf.float32)self.min_lr = tf.cast(min_lr, dtype=tf.float32)self.print_step = print_step# 最开始的当前学习率等于初始学习率self.current = self.initial_lr# 记录每个epoch的学习率值self.learning_rate_list = []# 前向传播def __call__(self, step):# 余弦衰减计算公式learning_rate = 0.5 * self.initial_lr * (1 + tf.math.cos(step*math.pi / self.decay_step))# 更新当前学习率指标, 学习率不能小于指定的最小值self.current = tf.where(learning_rate>self.min_lr, learning_rate, self.min_lr)# 记录每个step的学习率self.learning_rate_list.append(self.current.numpy().item())# 多少个step打印一次学习率,外部设置每个epoch打印一次学习率if step % self.print_step == 0:# 在训练时打印当前学习率print('learning_rate has changed to: ', self.current.numpy().item())return self.current# ------------------------------------------------------------------ #
# (3)参数设置
# ------------------------------------------------------------------ #
# 每个step处理32张图
batch_size = 32
# 迭代次数
num_epochs = 10
# 初始学习率
initial_lr = 0.001
# 学习率衰减系数
decay_rate = 0.9
# 学习率下降的最小值
min_lr = 0# 每个epoch打印一次学习率, 1个batch处理32张图
# 共60000张图,需要60000/32个batch,即1875个step
print_step = total_train_num / batch_size
# 余弦下降到0所需的步长
decay_step = total_train_num / batch_size * num_epochs# ------------------------------------------------------------------ #
# (4)构造数据集
# ------------------------------------------------------------------ #
def processing(x,y):  # 预处理函数x = 2 * tf.cast(x, dtype=tf.float32)/255.0 - 1   # 归一化x = tf.expand_dims(x, axis=-1)  # 增加通道维度y = tf.cast(y, dtype=tf.int32)  return x,y# 构造训练集
train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_ds = train_ds.map(processing).batch(batch_size).shuffle(10000)
# 构造测试集
test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test))
test_ds = test_ds.map(processing).batch(batch_size)# 迭代器查看数据是否正确
sample = next(iter(train_ds))
print('x_batch:', sample[0].shape, 'y_batch:', sample[1].shape)  # (32, 28, 28, 1), (32,)# ------------------------------------------------------------------ #
# (5)构造模型
# ------------------------------------------------------------------ #
inputs = keras.Input(sample[0].shape[1:])  # 构造输入层
# [28,28,1]==>[28,28,32]
x = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(inputs)
# [28,28,32]==>[14,14,32]
x = layers.MaxPool2D(pool_size=(2,2), strides=2, padding='same')(x)
# [14,14,32]==>[14,14,64]
x = layers.Conv2D(64, kernel_size=3, padding='same', activation='relu')(x)
# [14,14,64]==>[7,7,64]
x = layers.MaxPool2D(pool_size=(2,2), strides=2, padding='same')(x)
# [7,7,64]==>[None,7*7*64]
x = layers.Flatten()(x)
# [None,7*7*64]==>[None,128]
x = layers.Dense(128)(x)
# [None,128]==>[None,10]
outputs = layers.Dense(10, activation='softmax')(x)
# 构建模型
model = keras.Model(inputs, outputs)
# 查看模型结构
model.summary()# ------------------------------------------------------------------ #
# (6)模型训练
# ------------------------------------------------------------------ #
# 接收学习率调整方法
cosinedecay = CosineDecay(initial_lr=initial_lr,  # 初始学习率decay_step=decay_step,  # 学习率衰减系数min_lr=min_lr,          # 最小学习率值print_step=print_step)  # 每个epoch打印一次学习率值# 设置adam优化器,指定学习率
opt = keras.optimizers.Adam(cosinedecay)# 网络编译
model.compile(optimizer=opt,   # 学习率loss='sparse_categorical_crossentropy',  # 损失metrics=['accuracy'])  # 监控指标# 网络训练
model.fit(train_ds, epochs=num_epochs, validation_data=test_ds)# 绘制学习率变化曲线
plt.plot(range(decay_step), cosinedecay.learning_rate_list)
plt.xlabel("Train step")
plt.ylabel("Learning_Rate")
plt.title('cosinedecay')
plt.grid()
plt.show()

打印训练过程,可以看到每个epoch都打印了当前的学习率

Epoch 1/10
learning_rate has changed to:  0.0010000000474974513
313/313 [==============================] - 6s 19ms/step - loss: 0.6491 - accuracy: 0.7977 - val_loss: 0.0725 - val_accuracy: 0.9783
Epoch 2/10
learning_rate has changed to:  0.0009755282662808895
313/313 [==============================] - 6s 18ms/step - loss: 0.0673 - accuracy: 0.9793 - val_loss: 0.0278 - val_accuracy: 0.9911
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Epoch 9/10
learning_rate has changed to:  9.54914721660316e-05
313/313 [==============================] - 6s 18ms/step - loss: 8.1648e-04 - accuracy: 1.0000 - val_loss: 7.3570e-04 - val_accuracy: 1.0000
Epoch 10/10
learning_rate has changed to:  2.4471701181028038e-05
313/313 [==============================] - 6s 19ms/step - loss: 8.0403e-04 - accuracy: 1.0000 - val_loss: 7.2831e-04 - val_accuracy: 1.0000

绘制学习率曲线,每个epoch的学习率保存在了 self.learning_rate_list 列表中,通过 cosinedecay.learning_rate_list 调用该列表

【深度学习】(10) 自定义学习率衰减策略(指数、分段、余弦),附TensorFlow完整代码相关推荐

  1. 【深度学习】(11) 学习率衰减策略(余弦退火衰减,多项式衰减),附TensorFlow完整代码

    大家好,今天和各位分享一下如何使用 TensorFlow 构建 多项式学习率衰减策略.单周期余弦退火学习率衰减策略.多周期余弦退火学习率衰减策略,并使用Mnist数据集来验证构建的方法是否可行. 在上 ...

  2. 【深度学习】(7) 交叉验证、正则化,自定义网络案例:图片分类,附python完整代码

    各位同学好,今天和大家分享一下TensorFlow2.0深度学习中的交叉验证法和正则化方法,最后展示一下自定义网络的小案例. 1. 交叉验证 交叉验证主要防止模型过于复杂而引起的过拟合,找到使模型泛化 ...

  3. 资源|2019 年 11 月最新《TensorFlow 2.0 深度学习算法实战》中文版教材免费开源(附随书代码+pdf)...

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送 2019 年 10 月,谷歌正式宣布,开源机器学习库 TensorFlow 2.0 现在 ...

  4. 400页《TensorFlow 2.0 深度学习算法实战》中文版教材免费下载(附随书代码+pdf)...

    Tensorflow自谷歌提出以来就成为最受欢迎的深度学习框架之一,到目前为止也已经被下载超过 4000 万次.其中TensorFlow2.0更是修复之前非常多的不人性的特性,备大家欢迎. 今天给大家 ...

  5. 【图像分类案例】(2) DenseNet 天气图片四分类(权重迁移学习),附Tensorflow完整代码

    各位同学好,今天和大家分享一下使用 Tensorflow 构建 DenseNet 卷积神经网络模型,并使用预训练模型的权重,完成对四种天气图片的分类. 完整代码在我的 Gitee 中,有需要的自取: ...

  6. 【神经网络】(10) Resnet18、34 残差网络复现,附python完整代码

    各位同学好,今天和大家分享一下 TensorFlow 深度学习中如何搭载 Resnet18 和 Resnet34 残差神经网络,残差网络利用 shotcut 的方法成功解决了网络退化的问题,在训练集和 ...

  7. 【深度学习】(8) CNN中的通道注意力机制(SEnet、ECAnet),附Tensorflow完整代码

    各位同学好,今天和大家分享一下attention注意力机制在CNN卷积神经网络中的应用,重点介绍三种注意力机制,及其代码复现. 在我之前的神经网络专栏的文章中也使用到过注意力机制,比如在MobileN ...

  8. 【图像分类案例】(10) Vision Transformer 动物图像三分类,附Pytorch完整代码

    大家好,今天和各位分享一下如何使用 Pytorch 构建 Vision Transformer 网络模型,并使用 权重迁移学习方法 训练模型并预测. Vision Transformer 的原理和 T ...

  9. PyTorch学习率衰减策略:指数衰减(ExponentialLR)、固定步长衰减(StepLR)、多步长衰减(MultiStepLR)、余弦退火衰减(CosineAnnealingLR)

    梯度下降算法需要我们指定一个学习率作为权重更新步幅的控制因子,常用的学习率有0.01.0.001以及0.0001等,学习率越大则权重更新.一般来说,我们希望在训练初期学习率大一些,使得网络收敛迅速,在 ...

最新文章

  1. VS Code 成主宰、Vue 备受热捧!2019 前端开发趋势必读
  2. springData jpa update delete
  3. SQLServer 中存储过程返回的三种方式( 包括存储过程的创建, 在存储过程中调用, 在VS中调用的方法)...
  4. 数组和集合的相互转换
  5. Linux-No.04 Linux 设置定时任务发送邮件功能
  6. 04-numpy-笔记-transpose
  7. c语言getch() 头文件,用getch()需要头文件吗?
  8. python3 xpath_「手把手教python3接口自动化」:非结构化数据提取(二)
  9. 为什么你的发行版仍然在使用“过时的”Linux 内核? | Linux 中国
  10. Java中加载properties文件的6种方法
  11. c语言读写txt坐标文件数据,C语言——从txt文件中读写数据
  12. node2vec代码实现及详细解析
  13. 【深度学习】全面理解VGG16模型
  14. win8 32位系统上如何运行校园翼讯客户端
  15. Java 安全套接字编程以及keytool 使用最佳实践
  16. pl2303 USB转串口驱动
  17. poj1008(Maya Calendar)玛雅历(c++实现)
  18. linux中man命令的基本用法,linux中的man命令的详细解释
  19. 在电脑中如何调整PDF文件大小
  20. 英文文献翻译(白嫖版)

热门文章

  1. 中文分词器ICTCLAS使用方法(Java)
  2. hive的distribute by应用
  3. 华为手机的开发者模式真难用,为什么要把系统搞得这么烂?
  4. html5 堆栈不足,超简单!不用PS也能玩堆栈摄影
  5. 陈省身文集40——21世纪的数学
  6. Excel函数大全-10查找和引用函数
  7. 高性能网络开发框架vpp,让你的技术提高一个level
  8. 微信公众号(订阅号)文章阅读数监控V0.1
  9. python函数名前带个杠
  10. iOS TouchID和FaceID登录验证 简单使用