TensorFlow自定义训练函数
本文记录了在TensorFlow框架中自定义训练函数的模板并简述了使用自定义训练函数的优势与劣势。
首先需要说明的是,本文中所记录的训练函数模板参考自https://stackoverflow.com/questions/59438904/applying-callbacks-in-a-custom-training-loop-in-tensorflow-2-0中的回答以及Hands-On Machine Learning with Scikit-Learn, Keras, and Tensorflow一书中第12.3.9节的内容,如有错漏,欢迎指正。
为什么和什么时候需要自定义训练函数
除非你真的需要额外的灵活性,否则应该更倾向使用fit()方法,为不是实现你自己的循环,尤其是在团队合作中。
如果你还在困惑为什么需要自定义训练函数的时候,那说明你还不需要自定义训练函数。通常只有在搭建一些结构奇特的模型时,我们才会发现model.fit()无法完全满足需求,接下来首先该尝试的方法是去看TensorFlow相关部分的源码,看看有没有认识之外的参数或方法,其次才是考虑使用自定义训练函数。毫无疑问,自定义训练函数会让代码更长、更难维护、更难懂。
但是,自定义训练函数的灵活性是fit()方法无法比拟的。比如,在自定义函数中你可以实现使用多个不同优化器的训练循环或是在多个数据集上计算验证循环。
自定义训练函数模板
模板设计的目的在于让我们通过对代码块的复用以及对关键部位的填空快速完成自定义训练函数,以使我们更专注于训练函数结构本身而非一些细枝末节的部分(如未知长度训练集的处理)并实现一些fit()方法支持的功能(如Callback类的使用)。
def train(model:keras.Model,train_batchs,epochs=1,initial_epoch=0,callbacks=None,steps_per_epoch=None,val_batchs=None):
callbacks = tf.keras.callbacks.CallbackList(
callbacks, add_history=True, model=model)
logs_dict = {}
# init optimizer, loss function and metrics
optimizer = keras.optimizers.Nadam(learning_rate=0.0005)
loss_fn = keras.losses.MeanSquaredError
train_loss_tracker = keras.metrics.Mean(name="train_loss")
val_loss_tracker = keras.metrics.Mean(name="val_loss")
# train_acc_metric = tf.keras.metrics.BinaryAccuracy(name="train_acc")
# val_acc_metric = tf.keras.metrics.BinaryAccuracy(name="val_acc")
def count(): # infinite iter
x = 0
while True:yield x;x+=1
def print_status_bar(iteration, total, metrics=None):
metrics = " - ".join(["{}:{:.4f}".format(m.name,m.result()) for m in (metrics or [])])
end = "" if iteration < total or float('inf') else "\n"
print("\r{}/{} - ".format(iteration,total) + metrics, end=end)
def train_step(x,y,loss_tracker:keras.metrics.Metric):
with tf.GradientTape() as tape:
outputs = model(x)
main_loss = tf.reduce_mean(loss_fn(y,outputs))
loss = tf.add_n([main_loss] + model.losses)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients,model.trainable_variables))
loss_tracker.update_state(loss)
return {loss_tracker.name:loss_tracker.result()}
def val_step(x,y,loss_tracker:keras.metrics.Metric):
outputs = model.predict(x,verbose=0)
main_loss = tf.reduce_mean(loss_fn(y,outputs))
loss = tf.add_n([main_loss] + model.losses)
loss_tracker.update_state(loss)
return {loss_tracker.name:loss_tracker.result()}
# init train_batchs
train_iter = iter(train_batchs)
callbacks.on_train_begin(logs=logs_dict)
for i_epoch in range(initial_epoch, epochs):
# init steps
infinite_flag = False
if steps_per_epoch is None:
infinite_flag = True
step_iter = count()
else:
step_iter = range(steps_per_epoch)
# train_loop
for i_step in step_iter:
callbacks.on_batch_begin(i_step, logs=logs_dict)
callbacks.on_train_batch_begin(i_step, logs=logs_dict)
try:
X_batch, y_batch = train_iter.next()
except StopIteration:
train_iter = iter(train_batchs)
if infinite_flag is True:
break
else:
X_batch, y_batch = train_iter.next()
train_logs_dict = train_step(x=X_batch,y=y_batch,loss_tracker=train_loss_tracker)
logs_dict.update(train_logs_dict)
print_status_bar(i_step, steps_per_epoch or i_step, [train_loss_tracker])
callbacks.on_train_batch_end(i_step, logs=logs_dict)
callbacks.on_batch_end(i_step, logs=logs_dict)
if steps_per_epoch is None:
print()
steps_per_epoch = i_step
if val_batchs is not None:
# val_loop
for i_step,(X_batch,y_batch) in enumerate(iter(val_batchs)):
callbacks.on_batch_begin(i_step, logs=logs_dict)
callbacks.on_test_batch_begin(i_step, logs=logs_dict)
val_logs_dict = val_step(x=X_batch,y=y_batch,loss_tracker=val_loss_tracker)
logs_dict.update(val_logs_dict)
callbacks.on_test_batch_end(i_step, logs=logs_dict)
callbacks.on_batch_end(i_step, logs=logs_dict)
logs_dict.update(val_logs_dict)
print_status_bar(steps_per_epoch, steps_per_epoch, [train_loss_tracker, val_loss_tracker])
callbacks.on_epoch_end(i_epoch, logs=logs_dict)
for metric in [train_loss_tracker, val_loss_tracker]:
metric.reset_states()
callbacks.on_train_end(logs=logs_dict)
# Fetch the history object we normally get from keras.fit
history_object = None
for cb in callbacks:
if isinstance(cb, tf.keras.callbacks.History):
history_object = cb
return history_object
TensorFlow自定义训练函数的更多相关文章
- 深度学习笔记 (二) 在TensorFlow上训练一个多层卷积神经网络
上一篇笔记主要介绍了卷积神经网络相关的基础知识.在本篇笔记中,将参考TensorFlow官方文档使用mnist数据集,在TensorFlow上训练一个多层卷积神经网络. 下载并导入mnist数据集 首 ...
- 在 C/C++ 中使用 TensorFlow 预训练好的模型—— 直接调用 C++ 接口实现
现在的深度学习框架一般都是基于 Python 来实现,构建.训练.保存和调用模型都可以很容易地在 Python 下完成.但有时候,我们在实际应用这些模型的时候可能需要在其他编程语言下进行,本文将通过直 ...
- 在 C/C++ 中使用 TensorFlow 预训练好的模型—— 间接调用 Python 实现
现在的深度学习框架一般都是基于 Python 来实现,构建.训练.保存和调用模型都可以很容易地在 Python 下完成.但有时候,我们在实际应用这些模型的时候可能需要在其他编程语言下进行,本文将通过 ...
- 在C#下使用TensorFlow.NET训练自己的数据集
在C#下使用TensorFlow.NET训练自己的数据集 今天,我结合代码来详细介绍如何使用 SciSharp STACK 的 TensorFlow.NET 来训练CNN模型,该模型主要实现 图像的分 ...
- 关于jqGrig如何写自定义格式化函数将JSON数据的字符串转换为表格各个列的值
首先介绍一下jqGrid是一个jQuery的一个表格框架,现在有一个需求就是将数据库表的数据拿出来显示出来,分别有id,name,details三个字段,其中难点就是details字段,它的数据是这样 ...
- 自定义el函数
1.1.1 自定义EL函数(EL调用Java的函数) 第一步:创建一个Java类.方法必须是静态方法. public static String sayHello(String name){ retu ...
- ORACLE 自定义聚合函数
用户可以自定义聚合函数 ODCIAggregate,定义了四个聚集函数:初始化.迭代.合并和终止. Initialization is accomplished by the ODCIAggrega ...
- SQL Server 自定义聚合函数
说明:本文依据网络转载整理而成,因为时间关系,其中原理暂时并未深入研究,只是整理备份留个记录而已. 目标:在SQL Server中自定义聚合函数,在Group BY语句中 ,不是单纯的SUM和MAX等 ...
- Matlab中如何将(自定义)函数作为参数传递给另一个函数
假如我们编写了一个积分通用程序,想使它更具有通用性,那么可以把被积函数也作为一个参数.在c/c++中,可以使用函数指针来实现上边的功能,在matlab中如何实现呢?使用函数句柄--这时类似于函数指针的 ...
随机推荐
- sklearn机器学习实战-KNN
KNN分类 KNN是惰性学习模型,也被称为基于实例的学习模型 简单线性回归是勤奋学习模型,训练阶段耗费计算资源,但是预测阶段代价不高 首先工作是把label的内容进行二值化(如果多分类任务,则考虑On ...
- 安装Suberversion[SVN]到CentOS(YUM)
运行环境 系统版本:CentOS Linux release 7.3.1611 (Core) 软件版本:Suberversion-1.7.14 硬件要求:无 安装过程 1.安装YUM-EPEL源 Su ...
- CF1682C. LIS or Reverse LIS?
题意:给\(n\)个数,问你能构出严格上升子序列长度和下降子序列长度最小值的最大值. 思路: 如果一个数出现至少两次,ans++. 统计出现一次的个数,因为再最长上升子序列中,只能有一个值能贡献到下降 ...
- IDEA初始化基础配置
0.前言 这篇博客是给认识的那帮新手搞的,刚进入IT行业的崽们 这个东西配置好了,也可以选择弄成在线文档,下一次安装IDEA时,有一个import导入配置,然后就可以自己配置好了( 虽然方便,但不建议 ...
- SQL中的数字、字母和汉字
知识点001 当变量的数据类型为VARCHAR时,变量赋值后,变量中的字符所占字节数,数字和字母是1个bytes,汉字是2个bytes; 当变量的数据类型为NVARCHAR时,变量赋值后,变量中的字符 ...
- mysql刷题笔记
近期,为提升自己的工程能力,在休息时常通过刷题来回顾一下基础性知识. 于是选择了牛客网上的mysql知识题库练手,过程中,主要遇到了几个比较有意思的题,记录下来,方便回顾. 题1:SQL29 计算用户 ...
- 为什么要使用TypeScript(Why Typescript?)
客观原因 静态类型. 在编译期即可进行静态类型分析, 减少JS运行时类型错误. 语法功能强大 对于大型项目具有更好构建机制,加入了类.接口.泛型.模块等概念. 兼容JavaScript 与现存的Jav ...
- Vue回炉重造之三次封装axios
源码目录 在src目录下建立一个request文件夹.里面建立两个文件: http.js api.js 源码内容 http.js import axios from 'axios' // 引入axio ...
- C++ 炼气期之数组探幽
1. 数组概念 变量是内存中的一个存储块,大小由声明时的数据类型决定. 数组可以认为是变量的集合,在内存中表现为一片连续的存储区域,其特点为: 同类型多个变量的集合. 每一个变量没有自己的名字. 数组 ...
- java中常见的锁
1.悲观锁 认为别的线程都会修改数据,二话不说先锁上 synchronized 2.乐观锁 乐观豁达,起初不操作.最后修改的时候比对一下版本,不一致再上锁 3.可重入锁 外层锁了之后,内层仍可以直接使 ...