TensorFlow2.x 常用知识
1. 简介
- 官方教程
- 实验环境
- Google云盘
- TensorFlow2.0 30天教程
- 神经网络的本质
- 是一个函数逼近器,理论上可以以任意精度逼近任何函数
- 分类(classification)
- 目的是预测出如价格或概率这样连续值的输出
- 回归(regression)
- 目的是从一系列的分类出选择出一个分类
1.1 损失函数
1.1.1 MSE
- MSE (Mean-Square Error):均方误差
M S E = 1 N Σ ( y − Y ) 2 MSE= \frac{1}{N}\Sigma(y-Y)^2 MSE=N1Σ(y−Y)2- y:预测值
- Y:标签值
1.2 梯度消失
- 梯度消失:在模型参数w都是(-1,1)之间的数的前提下,如果激活函数选择的是sigmod(x),那么他的导函数σ’(x)的值域为(0,0.25]
- 参数更新慢:那么w1的导数会有很多(0,0.25]范围的数累乘,就会造成w1的导数很小,这就是梯度消失。梯度消失的后果就是,w1的更新就会很慢,使得神经网络的学习变得很慢
- 解决方法:使用relu(x)这样的激活函数,因为他的导函数的值可以稳定在1,累乘后不会让梯度消失
1.3 常用激活函数
1.3.1 ReLU
- tf.keras.activations.relu:修正线性单元,最流行的激活函数
- 用途:多用于卷积层
- 解决梯度消失问题:如果激活函数的导数为1,那么就不存在梯度消失的问题了,每层的网络都可以得到相同的更新速度
- ReLU优点
- 解决了梯度消失、爆炸的问题
- 计算方便,计算速度快
- 加速了网络的训练
- ReLU缺点
- 由于负数部分恒为0,会导致一些神经元无法激活(可通过设置小学习率部分解决)
- 输出不是以0为中心的
- 目前使用最多的激活函数
1.3.2 Sigmoid
- tf.keras.activations.sigmoid:将实数压缩到0到1之间
- 用途:一般只在二分类的最后输出层使用或全连接层
- 特点:
- 左图是sigmoid的损失函数图,右图为它的导数图像
- 如果使用sigmoid作为损失函数,其梯度是不可能超过0.25的,这样经过链式求导之后,很容易发生梯度消失
- 在激活的时候,主要关注数据在零点/中心点前后的细小变化,而忽略数据在极端时的变化
1.3.3 Softmax
- tf.keras.activations.softmax:sigmoid的多分类扩展
- 用途:一般只在多分类问题的最后输出层使用
S o f t m a x ( x i ) = e x i Σ j e x j Softmax(x_i) = \frac{e^{x_i}}{\Sigma_j e^{x_j}} Softmax(xi)=Σjexjexi
1.4 数据容器
1.4.1 Python
- Python:list
1.4.2 Numpy
- Numpy:np.array
1.4.3 TensorFlow
- TensorFlow: tf.Tensor
- 标量、向量、矩阵
tf.Tensor(3, shape=(), dtype=int32) // 标量
tf.Tensor([4 6], shape=(2,), dtype=int32) // 行向量
tf.Tensor([[2 3]], shape=(1, 2), dtype=int32) // 矩阵 1x2
tf.Tensor(
[[1]
[2]], shape=(2, 1), dtype=int32) // 矩阵2x1
1.4.4 np.array与tf.Tensor的区别
- NumPy数组和tf.Tensor之间最明显的区别是:
- 张量可以由加速器内存(如GPU,TPU)支持
- 张量是不变的
数据类型 | 内存位置 |
---|---|
tf.Tensor | 主机内存、GPU内存、TPU内存 |
np.array | 主机内存 |
- 当数据在主机内存时,Tensor与np.array共享数据
- 当数据在GPU或TPU内存时,把Tensor转换为np.array需要进行数据copy
1.4.5 tf.Tensor与np.array(np.ndarray)互转
- 操作隐式转换
- TensorFlow操作:会自动将NumPy ndarray转换为Tensors
- NumPy操作:会自动将Tensors转换为NumPy ndarrays
- 显式转换
- Tensor转NumPy: tensor.numpy()
- NumPy转Tensor: tf.convert_to_tensor(np)
import tensorflow as tf
import numpy as np
ndarray = np.ones([3, 3])
print("TensorFlow operations convert numpy arrays to Tensors automatically")
tensor = tf.multiply(ndarray, 42)
print(tensor)
print("And NumPy operations convert Tensors to numpy arrays automatically")
print(np.add(tensor, 1))
print("The .numpy() method explicitly converts a Tensor to a numpy array")
print(tensor.numpy())
2. 常用
2.1 查看TensorFlow版本
import tensorflow as tf
print(tf.__version__)
2.2 查看帮助
- 在Jupyter Notebook中 查看函数帮助:输入函数?,然后执行(Ctrl+Enter)
2.3 常用导入
import numpy as np
import tensorflow as tf
from tensorflow import keras
#import os, sys, pickle
#import scipy
#import pandas as pd
#import matplotlib.pyplot as plt
2.4 查看GPU信息
# 查看是否使用GPU
import tensorflow as tf
tf.test.gpu_device_name()
tf.config.list_physical_devices('GPU')
tf.test.is_gpu_available()
# 查看在使用哪个GPU
from tensorflow.python.client import device_lib
device_lib.list_local_devices()
2.4 工作模式
2.4.1 TensorFlow 1.x
- 默认模式:Graph Execution (图执行<在会话中执行>)
- 启用即时执行模式(Eager Execution)
tf.enable_eager_execution()
2.4.2 TensorFlow 2.x
- 默认模式:Eager Execution (即时执行)
- 关闭即时执行模式(Eager Execution)
tf.compat.v1.disable_eager_execution()
2.5 张量
- 张量不可对单个元素进行赋值,可读取任意元素的值
2.5.1 定义
- 张量:就是多维数组
- 标量:0维数组,shape:(1, ), 如:[0]
- 向量:1维数组,shape:(5, ), 如:[1, 2, 3, 4, 5]
- 矩阵:2维数组,shape:(5, 1),如:[ [1], [2], [3], [4], [5] ]
- 训练数据格式:[b, w, h, c] :
- b (batch):参与训练的图片张数
- w,h:图片的宽度和高度
- c:图片的通道数
# 定义一个随机数(标量)
random_float = tf.random.uniform(shape=())
print('random_float={}'.format(random_float))
# 定义一个有2个元素的零向量
zero_vector = tf.zeros(shape=(2))
print('zero_vector={}'.format(zero_vector))
# 定义两个2×2的常量矩阵
A = tf.constant([[1., 2.], [3., 4.]])
print('A=\n{}'.format(A))
2.5.2 属性
- 张量的属性:
- 形状(shape)
- 类型(dtype)
- 值(numpy())
A = tf.constant([[1., 2.], [3., 4.]])
print('A=\n{}'.format(A))
print('A.shape={}'.format(A.shape))
print('A.dtype={}'.format(A.dtype))
print('A.numpy=\n{}'.format(A.numpy()))
# static shape
test_data = tf.fill([3,5], 6)
# print(test_data[1,3])
print('test_data shape=\n{}'.format(test_data.get_shape()))
# dynamic shape
new_test_data = tf.reshape(test_data, [5,3])
print('new_test_data shape=\n{}'.format(new_test_data.get_shape()))
2.5.3 常量
- 常量不可赋值 (元素或整体赋值都不可)
# 常量
tf_c = tf.constant([[1,2,3],[4,5,6]])
print('tf_c=\n{}'.format(tf_c))
2.5.4 变量
- 变量可对整个变量赋值,但其shape不可变
# 变量
tf_v = tf.Variable([[1,2,3],[4,5,7]])
print('tf_v=\n{}'.format(tf_v))
print(tf_v[1,1])
#tf_v[1,1] = 100 #tensor does not support item assignment
tf_v.assign([[3,3,3],[9,9,9]])
print('tf_v=\n{}'.format(tf_v))
2.5.5 张量与Numpy互转
- Tensor转NumPy: tensor.numpy()
- NumPy转Tensor: tf.convert_to_tensor(np)
# Tensor转NumPy
tensor_a = tf.constant([[1,2,3,4],[5,6,7,8]])
print('tensor_a=\n{}'.format(tensor_a))
np_a = tensor_a.numpy()
print('np_a=\n{}'.format(np_a))
# NumPy转Tensor
np_aa = [[1,2,3],[4,5,6]]
tensor_aa = tf.convert_to_tensor(np_aa)
print('tensor_aa=\n{}'.format(tensor_aa))
tensor_v = tf.Variable([[1,2,3],[5,6,7]])
np_v = tensor_v.numpy()
print('np_v=\n{}'.format(np_v))
2.5.6 访问张量元素 (索引+切片)
- 索引:取一个值
- 切片:取子结构 (冒号切片、省略号切片)
- 无论索引还是切片,行列是使用逗号分隔的,并且无论行列,其索引都是从0开始的
## 索引(index)
a = tf.constant([[1,2,3], [4,5,6], [7, 8, 9]])
print('a=\n{}'.format(a))
print('a[0,0]={}'.format(a[0,0]))
print('a[2,2]={}'.format(a[2,2]))
# a[2,2] = 100 # tensor不支持元素赋值
# print('a=\n{}'.format(a))
np_a = [[1,2,3],[4,5,6]]
print('np_a=\n{}'.format(np_a))
tensor_a = tf.convert_to_tensor(np_a)
print('tensor_a=\n{}'.format(tensor_a))
## 切片(slice)
print(tensor_a[:, 1:])
t = tf.constant([ [[1,2],[3,4]],
[[5,6],[7,8]],
[[9,10], [11,12]]
])
print('t=\n{}'.format(t))
# 三维切片
print('t[:,:,1:]=\n{}'.format(t[:,:,1:]))
print('t[...,1:]=\n{}'.format(t[...,1:]))
2.5.7 查看数据类型
- 查看张量和numpy数据的类型
import numpy as np
import tensorflow as tf
np_a = np.array([1,2,3])
print("np_a type:{}".format(type(np_a)))
tensor_a = tf.constant([1,2,3])
print("tensor_a type:{}".format(type(tensor_a)))
- 输出
np_a type:<class 'numpy.ndarray'>
tensor_a type:<class 'tensorflow.python.framework.ops.EagerTensor'>
3. 常用数据操作API
- 常用的API都能在NumPy中找到对应的方法
- TensorFlow就是TensorFlow式的NumPy+神经网络
3.1 数据初始化API
np_matrix = np.ones([3,5])
print('type={}, np_matrix=\n{}'.format(type(np_matrix), np_matrix))
tf_matrix = tf.convert_to_tensor(np_matrix)
print('type={}, tf_matrix=\n{}'.format(type(tf_matrix), tf_matrix))
# tf.zeros
zeros = tf.zeros([3,2])
print('zeros=\n{}'.format(zeros))
# tf.zeros_like
zeros_like = tf.zeros_like(tf_matrix)
print('zeros_like=\n{}'.format(zeros_like))
# tf.ones
ones = tf.ones([3,2])
print('ones=\n{}'.format(ones))
# tf.ones_like
ones_like = tf.ones_like(tf_matrix)
print('ones=\n{}'.format(ones_like))
# tf.fill
fills = tf.fill([3,5], 8)
print('fills=\n{}'.format(fills))
# start and stop must be float, the incremental = (stop - start) / (num - 1)
# tf.linspace
linspace = tf.linspace(3.0, 8.0, 5)
print('linspace=\n{}'.format(linspace))
# tf.range
range = tf.range(1,11)
print('range=\n{}'.format(range))
3.2 tf.function装饰器
- 这个API作为一个装饰器使用,用来将Python语法尽可能有效地转换为TF语法、图结构
- 在定义的函数f内部,一个tf语法也没有(当然可以有),只装饰了一行@tf.function,而调用结果返回值就是一个tensor,这就是@tf.function装饰器的作用了
- 函数中不允许定义变量
@tf.function
def f():
a = np.array([1,2,3])
b = np.array([2,3,4])
return a + b
print(f())
- 输出:
tf.Tensor([3 5 7], shape=(3,), dtype=int32)
4. 常用模块
模块名 | 功能 |
---|---|
tf. | 包含了张量定义,变换等常用函数和类 |
tf.data | 输入数据处理模块,提供了tf.data.Dataset 等类用于封装输入数据,指定批量大小等 |
tf.image | 图像处理模块,提供了像图像裁剪,变换,编码,解码等类 |
tf.keras | 原Keras 框架高阶API。包含原tf.layers 中高阶神经网络层 |
tf.linalg | 线性代数模块,提供了大量线性代数计算方法和类 |
tf.losses | 损失函数模块,用于方便神经网络定义损失函数 |
tf.math | 数学计算模块,提供了大量数学计算函数 |
tf.saved_model | 模型保存模块,可用于模型的保存和恢复 |
tf.train | 提供用于训练的组件,例如优化器,学习率衰减策略等 |
tf.nn | 提供用于构建神经网络的底层函数,以帮助实现深度神经网络各类功能层 |
tf.estimator | 高阶API,提供了预创建的Estimator 或自定义组件 |
5.数值计算
5.1 均值:tf.reduce_mean
- reduce的含义为减少数据维数
x = tf.constant([[1.,2.,3.],[4.,5.,6.]])
# 所以元素之和除以元素个数
mean_all = tf.reduce_mean(x)
print('mean_all={}'.format(mean_all))
# 所有行向量相加,再除以行数: 输出一行,列数等于原列数
mean_0 = tf.reduce_mean(x, axis=0)
print('mean_0={}'.format(mean_0))
# 每个行向量内的元素相加再除以列数:输出一行,列数等于原行数
mean_1 = tf.reduce_mean(x, axis=1)
print('mean_1={}'.format(mean_1))
tf.reduce_mean?
- 输出:
mean_all=3.5
mean_0=[2.5 3.5 4.5]
mean_1=[2. 5.]
5.2 平方:tf.square
- 计算每个元素的平方,输出数据的shape与输入数据的shape一致
x = tf.constant([9])
s1 = tf.square(x)
print('s1={}'.format(s1))
y = tf.constant([[1,2,3],[4,5,6]])
s3 = tf.square(y)
print('s3=\n{}'.format(s3))
- 输出
s1=[81]
s3=
[[ 1 4 9]
[16 25 36]]
5.3 矩阵相乘 tf.matmul
x = tf.constant([[1,2,3],[3,4,5]])
w = tf.constant([[1,2,],[3,4],[5,6]])
v = tf.matmul(x,w)
print('v=\n{}'.format(v))
- 输出:
v=
[[22 28]
[40 52]]
6. 梯度下降法
- 核心:根据参数的梯度更新参数的值,以使损失减少
- 在数学中,微分是对函数的局部变化率的一种线性描述。虽然微分和导数是两个不同的概念。但是,对一元函数来说,可微与可导是完全等价的
- tf.GradientTape 会像磁带一样记录下计算图中的梯度信息,然后使用.gradient 即可回溯计算出任意梯度,这对于使用TensorFlow 低阶API 构建神经网络时更新参数非常重要
- 微分
- 手动微分
- 在使用程序计算导函数值之前,手动计算目标函数的导函数,对于表达式比较复杂的函数,这显然是比较容易出错的,并且通用性和灵活性都较差,现在较少使用
- 符号微分
- 属于符号计算的范畴,基于初等函数的求导公式和四则运算法则及链式法则搭建在计算搭建求导系统求出导函数再代入变量,即可求出某点的导数值。符号微分计算出的表达式需要用字符串或其他数据结构存储,如表达式树
- 对于深层复合函数,如神经网络的映射函数,符号微分算法得到的导数计算公式将会非常冗长
- 对于机器学习中的应用,不需要得到导数的表达式,而只需计算函数在某一点处的导数值,因此存在计算上的冗余且成本高昂
- 数值微分
- 定义:一种方法能直接求出目标函数的导数,而不是求出导函数之后再带入数值得到导数
- 导数定义:
f ′ ( x 0 ) = lim x → x 0 f ( x ) − f ( x 0 ) x − x 0 = lim Δ x → 0 f ( x + Δ x ) − f ( x ) Δ x f'(x_0) = \lim_{x \to x_0} \frac {f(x) - f(x_0)}{x - x_0} = \lim_{\Delta x \to 0} \frac {f(x+\Delta x) - f(x)}{\Delta x} f′(x0)=x→x0limx−x0f(x)−f(x0)=Δx→0limΔxf(x+Δx)−f(x) - 当
Δ
x
\Delta x
Δx足够小时,可将该点的导数值近似为:
f ′ ( x 0 ) ≈ f ( x 0 + Δ x ) − f ( x 0 ) Δ x f'(x_0) \approx \frac {f(x_0 + \Delta x) - f(x_0)}{\Delta x} f′(x0)≈Δxf(x0+Δx)−f(x0) - 在实际计算时,使用它的变形:
f ′ ( x 0 ) ≈ f ( x 0 + Δ x ) − f ( x 0 − Δ x ) 2 Δ x f'(x_0) \approx \frac {f(x_0 + \Delta x) - f(x_0 - \Delta x)}{2 \Delta x} f′(x0)≈2Δxf(x0+Δx)−f(x0−Δx) - 数值微分有着更小的误差和更好的稳定性
- 由于进行了近似操作,得到的导数值肯定是有误差的(误差还在能接受的范围内)
- 按上面推导的式子计算导数,对每个自变量求导时都需要计算两次函数值,因此也会导致计算量的问题。所以,数值微分通常只用于检验其他算法的结果的正确性,例如在实现反向传播算法的时候用数值微分算法检验反向传播算法所求导数的正确性
- 自动微分
- 自动微分是介于数值微分和符号微分之间的一种折中方案:数值微分直接带入数值得到近似解,符号微分先求出解析式再带入数值得到导数。而自动微分将符号微分应用于最基本的运算(或称原子操作),如常数,幂函数,指数函数,对数函数,三角函数等基本初等函数,代入自变量的值得到其导数值,作为中间结果进行保留。然后再根据这些基本运算单元的求导结果计算出整个函数的导数值
- 自动微分的灵活性强,可实现完全向用户隐藏求导过程,由于它只对基本函数或常数运用符号微分法则,因此可以灵活地结合编程语言的循环、分支等结构,根据链式法则,借助于计算图计算出任意复杂函数的导数值。由于存在上述优点,该方法在现代深度学习库中得到广泛使用。自动微分在实现时有前向模式和反向模式两种实现方案
- 前向模式(forward model):前向模式从计算图的起点开始,沿着计算图边的方向依次向前计算,直到到达计算图的终点。它根据自变量的值计算出计算图中每个节点的值以及导数值,并保留中间结果。直到得到整个函数的值和其导数值。整个过程对应于一元复合函数求导时从最内层逐步向外层求导
- 反向模式:相对前向模式来说比较复杂,因为要首先正向求出函数值,然后再反向求导数。这一般用computation graph实现:将函数求值过程中的所有中间节点按求值顺序存储到栈中,得到表达式对应的计算图,然后再挨个弹出栈中的元素,求相应的导数
- TensorFlow工具:GradientTape
- 自动求导机制在机器学习中,我们经常需要计算函数的导数。TensorFlow 提供了强大的 自动求导机制 来计算导数。在即时执行模式下,TensorFlow 引入了 tf.GradientTape() 这个 “求导记录器” 来实现自动求导
- 手动微分
6.1 梯度下降优化器
- 分类
- batch GD:【全部样本,速度慢】
- 随机GD/SGD:【随机一个样本,速度快,但局部最优】
- mini-batch GD:【batch个样本,常在数据量较大时使用】
- 使用规则
- 训练集样本数少【≤2000】:采用batchGD
- 训练集样本数多:采用mini-batch GD,batch大小一般为64-512. 训练时多尝试一下2的次方来找到最合适的batch大小
tf.train.GradientDescentOptimizer(learning_rate)
6.2 梯度带
- 用途:用于计算梯度
- 要在同一计算中计算多个梯度,需要建一个持久梯度带(persistent=True),这允许多次调用 gradient() 方法
- 参数:
- persistent: 布尔值,用来指定新创建的gradient tape是否是可持续性的。默认是False,意味着只能够调用一次gradient()函数。
- watch_accessed_variables: 布尔值,表明这个gradien tape是不是会自动追踪任何能被训练(trainable)的变量。默认是True。要是为False的话,意味着你需要手动去指定你想追踪的那些变量。
- 定义
tf.GradientTape(
persistent=False, watch_accessed_variables=True
)
6.3 计算梯度
tf.GradientTape.gradient(
target, sources, output_gradients=None,
unconnected_gradients=tf.UnconnectedGradients.NONE
)
- 一元梯度实例(一阶)
x = tf.constant(3.0)
with tf.GradientTape() as g:
g.watch(x)
y = x * x
# y’ = 2*x = 2*3 = 6
dy_dx = g.gradient(y, x)
-
watch函数
- 把需要计算梯度的变量x加进来了
- GradientTape默认只监控由tf.Variable创建的traiable=True属性(默认)的变量
- 上面例子中的x是constant,因此计算梯度需要增加g.watch(x)函数
- 当然,也可以设置不自动监控可训练变量,完全由自己指定,设置watch_accessed_variables=False就行了(一般用不到)
-
一元梯度实例(二阶)
x = tf.constant(3.0)
with tf.GradientTape() as g:
g.watch(x)
with tf.GradientTape() as gg:
gg.watch(x)
y = x * x
dy_dx = gg.gradient(y, x) # Will compute to 6.0
print('dy_dx=%f'% dy_dx)
d2y_dx2 = g.gradient(dy_dx, x) # Will compute to 2.0
print('d2y_dx2=%f'% d2y_dx2)
- 二元梯度实例(一阶)
x = tf.constant(value=3.0)
y = tf.constant(value=2.0)
with tf.GradientTape(persistent=True,watch_accessed_variables=True) as g:
g.watch([x,y])
z=x*x*y+x*y
dz_dx = g.gradient(target=z,sources=x) # 输出标量
dz_dy = g.gradient(target=z, sources=y) # 输出标量
dz_d = g.gradient(target=z,sources=[x,y]) # 输出向量
print("dz_dx:", dz_dx)
print("dz_dy:", dz_dy)
print("dz_d:",dz_d)
print("type of dz_d:",type(dz_d))
- 输出
dz_dx: tf.Tensor(14.0, shape=(), dtype=float32)
dz_dy: tf.Tensor(12.0, shape=(), dtype=float32)
dz_d: [<tf.Tensor: id=252, shape=(), dtype=float32, numpy=14.0>, <tf.Tensor: id=253, shape=(), dtype=float32, numpy=12.0>]
type of dz_d: <class 'list'>
6.4 更新参数的值(根据梯度)
- 当前变量的值减去delta,然后再赋值给当前变量
# 当前变量减去一个值,再赋值给当前变量
tf.Variable.assign_sub(
delta, use_locking=False, name=None, read_value=True
)
# 给当前变量赋一个新值value
tf.Variable.assign(
value, use_locking=False, name=None, read_value=True
)
7. Keras
7.1 简介
- Keras 是一个用Python 编写的高级神经网络API,它能够在TensorFlow, 或者Theano 作为后端运行。Keras 的开发重点是支持快速的实验。能够以最小的时延把你的想法转换为实验结果,是做好研究的关键
- Keras是一个意在降低机器学习编程入门门槛的项目,其在业界拥有众多的拥护者和使用者。经过Keras社区的多年发展,Keras集成了很多符合工业和研究需求的高阶API,使用这些API只需要几行代码就可以构建和运行一个非常复杂的神经网络
- Keras 兼容的Python 版本: Python 2.7-3.X
- Keras 易于使用
- Keras 是为人类而非机器设计的API。Keras 遵循减少认知困难的最佳实践: 它提供一致且简单的API,它将常见用例所需的用户操作数量降至最低,并且在用户错误时提供清晰和可操作的反馈
- Keras 易于学习和使用。作为Keras 用户,你的工作效率更高
- 这种易用性并不以降低灵活性为代价:因为Keras 与底层深度学习语言(特别是TensorFlow)集成在一起,所以它可以让你实现任何你可以用基础语言编写的东西。特别是,tf.keras 作为Keras API 可以与TensorFlow 工作流无缝集成
7.2 TensorFlow 与Keras
- TensorFlow 1.x 中,我们可以通过tf.layers 高阶层封装开快速搭建神经网络。如今,2.0 已完全移除了tf.layers 模块,转而引入了tf.keras
- 如果你熟悉Keras 的使用,那么tf.keras 用起来就得心应手了,因为其基本和单独发行版本一致,子模块结构也几乎完全一样
- tf.keras 中的Keras 允许我们使用标准的Keras 包
- Keras 的核心数据结构是model,一种组织网络层的方式。最简单的模型是Sequential 顺序模型,它是由多个网络层线性堆叠的栈。对于更复杂的结构,使用Keras 函数式API,它允许构建任意的神经网络图
- 弃用collections:TensorFlow1.X中可以通过集合(collection) 来管理不同类别的资源。例如使用tf.add_to_collection 函数可以将资源加入一个或多个集合。使用tf.get_collection获取一个集合里面的所有资源。这些资源可以是张量、变量或者运行TensorFlow程序所需要的资源
- TensorFlow2.0非常依赖Keras API,因此如果你使用tf.keras,每个层都会处理自己的变量,当你需要获取可训练变量的列表,可直接查询每个层
7.3 keras建立模型
7.3.1 顺序模型
- Sequential模型适用于简单的层堆栈,其中每一层都有一个输入张量和一个输出张量,不能构建任意模型
- Keras 的Sequential API通过向tf.keras.models.Sequential() 提供一个层的列表,由Keras 将它们自动首尾相连
- 在Keras 里,你用layers 来搭建模型。一个模型(通常)是一个layer 组成的图(Graph)。最常见的模型类型一般是由多个layer 堆叠体:tf.keras.Sequential 模型
- 通过model.weights,就可以查询每一层的可训练的变量
- Keras中的所有图层都需要知道其输入的形状,以便能够创建其权重,将input_shape参数传递给model的第一层
- 顺序模型不适用于以下情况 :
- 模型有多个输入或多个输出
- 任何一层都有多个输入或多个输出
- 需要进行图层共享
- 您需要非线性拓扑(例如,残余连接,多分支模型)
7.3.1.1 通过构造函数创建顺序模型
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential(
[
keras.layers.Dense(100, activation="relu", input_shape=[2]),
keras.layers.Dense(100, activation="relu"),
keras.layers.Dense(1)
]
)
print(model.weights) # 查看权重初始值
print(model.summary()) # 查看模型摘要
print('layers num ={}'.format(len(model.layers))) # 查看模型的层数
keras.utils.plot_model(model, "model_1.png") # 绘制模型(安装pydot, pydot-ng, graphviz)
keras.utils.plot_model(model, "model_1.png", show_shapes = True) # 显示每层的形状
7.3.1.2 通过add()方法增量创建一个顺序模型
# 创建这样的图层时,最初没有权重,因为没有input_shape
model = keras.Sequential(name='my_model')
model.add(layers.Dense(2, activation="relu"))
model.add(layers.Dense(3, activation="relu"))
model.add(layers.Dense(4))
7.3.1.3 常用调试工作流程:add()+summary()
- 在构建新的顺序体系结构时,使用add()和频繁打印模型摘要来逐步堆叠图层很有用
- 例如,可以监视Conv2D和MaxPooling2D图层堆栈如何对图像特征图进行下采样:
model = keras.Sequential()
model.add(keras.Input(shape=(250, 250, 3))) # 250x250 RGB images
model.add(layers.Conv2D(32, 5, strides=2, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(3))
model.summary() # The answer : (40, 40, 32), so we can keep downsampling...
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(3))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(2))
model.summary()
# Now that we have 4x4 feature maps, time to apply global max pooling.
model.add(layers.GlobalMaxPooling2D())
# Finally, we add a classification layer.
model.add(layers.Dense(10))
model.summary()
- 输出
Model: "sequential_5"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_4 (Conv2D) (None, 123, 123, 32) 2432
_________________________________________________________________
conv2d_5 (Conv2D) (None, 121, 121, 32) 9248
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 40, 40, 32) 0
=================================================================
Total params: 11,680
Trainable params: 11,680
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_5"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_4 (Conv2D) (None, 123, 123, 32) 2432
_________________________________________________________________
conv2d_5 (Conv2D) (None, 121, 121, 32) 9248
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 40, 40, 32) 0
_________________________________________________________________
conv2d_6 (Conv2D) (None, 38, 38, 32) 9248
_________________________________________________________________
conv2d_7 (Conv2D) (None, 36, 36, 32) 9248
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 12, 12, 32) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 10, 10, 32) 9248
_________________________________________________________________
conv2d_9 (Conv2D) (None, 8, 8, 32) 9248
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 4, 4, 32) 0
=================================================================
Total params: 48,672
Trainable params: 48,672
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_5"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_4 (Conv2D) (None, 123, 123, 32) 2432
_________________________________________________________________
conv2d_5 (Conv2D) (None, 121, 121, 32) 9248
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 40, 40, 32) 0
_________________________________________________________________
conv2d_6 (Conv2D) (None, 38, 38, 32) 9248
_________________________________________________________________
conv2d_7 (Conv2D) (None, 36, 36, 32) 9248
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 12, 12, 32) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 10, 10, 32) 9248
_________________________________________________________________
conv2d_9 (Conv2D) (None, 8, 8, 32) 9248
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 4, 4, 32) 0
_________________________________________________________________
global_max_pooling2d (Global (None, 32) 0
_________________________________________________________________
dense_9 (Dense) (None, 10) 330
=================================================================
Total params: 49,002
Trainable params: 49,002
Non-trainable params: 0
_________________________________________________________________
7.3.2 非线性模型 (通过函数式API构建)
- Keras 功能性API是一种创建模型的方式,该模型比tf.keras.Sequential API更灵活
- 功能性API可以处理具有非线性拓扑
- 主要思想是,深度学习模型通常是层的有向无环图(DAG)。因此,功能性API是一种构建层图的方法
- 使用Keras函数式API构建复杂的模型
- 多输入模型
- 多输出模型
- 具有共享层的模型(同一层被调用多次)
- 具有非顺序数据流的模型(例如,residual connections)
- 特点
- 层是可调用的,返回值是一个tensor
- 输入tensors 和输出tensors 被用来定义一个tf.keras.Model实例
- 函数式API 构建的模型的训练同Sequential 模型
7.3.2.1 函数式API构建模型的方法
- 函数式API构建模型的方法如下
- 调用层实例,并且返回张量(tensor)
- 输入张量和输出张量用于定义tf.keras.Model实例
- 这个模型的训练就像Sequential模型一样
# 层的实例可调用,参数为tensor,返回一个tensor
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)
# 使用函数式API构建一个简单,全连接(fully-connected)的网络
inputs = tf.keras.Input(shape=(32,)) # Returns a placeholder tensor
# 层的实例可调用,参数为tensor,返回一个tensor
# A layer instance is callable on a tensor, and returns a tensor.
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)
#实例化指定输入和输出的模型
model = tf.keras.Model(inputs=inputs, outputs=predictions)
# 编译这个步骤指定了训练的配置(the training configuration)
model.compile(optimizer=tf.optimizers.RMSprop(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
# 训练5个epoch
model.fit(data, labels, batch_size=32, epochs=5)
# 查看网络信息
print(model.summary()) # 查看模型摘要
print('layers num ={}'.format(len(model.layers))) # 查看模型的层数
keras.utils.plot_model(model, "model_1.png") # 绘制模型(安装pydot, pydot-ng, graphviz)
keras.utils.plot_model(model, "model_1.png", show_shapes = True) # 显示每层的形状
7.3.2.2 模型子类化 (Model Subcalssing)
- 创建一个tf.keras.Model的派生类
class MyModel(tf.keras.Model):
def __init__(self, num_classes=10):
super(MyModel, self).__init__(name='my_model')
self.num_classes = num_classes
# 在__init__函数中定义层
self.dense_1 = layers.Dense(32, activation='relu')
self.dense_2 = layers.Dense(num_classes, activation='sigmoid')
def call(self, inputs):
# 在call函数中定义前向传播
# 使用在__init__中定义的层
x = self.dense_1(inputs)
return self.dense_2(x)
def compute_output_shape(self, input_shape):
# 如果你需要把这个子类化的模型当作一个函数式模型的一部分,那么你需要重载这个函数。
# 否则,这个函数是可选的
shape = tf.TensorShape(input_shape).as_list()
shape[-1] = self.num_classes
return tf.TensorShape(shape)
- 实例化派生类
model = MyModel(num_classes=10)
model.compile(optimizer=keras.optimizers.RMSprop(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(data, labels, batch_size=32, epochs=5)
7.3.2.3 自定义层
- 定义层
class MyLayer(layers.Layer):
def __init__(self, output_dim, **kwargs):
self.output_dim = output_dim
super(MyLayer, self).__init__(**kwargs)
def build(self, input_shape):
shape = tf.TensorShape((input_shape[1], self.output_dim))
# 为此层创建一个可训练的权重
self.kernel = self.add_weight(name='kernel',
shape=shape,
initializer='uniform',
trainable=True)
# 确保在函数结束时调用下面的语句
super(MyLayer, self).build(input_shape)
def call(self, inputs):
# 这里定义了这层要实现的操作,也就是前向传播的操作
return tf.matmul(inputs, self.kernel)
def compute_output_shape(self, input_shape):
# 计算输出tensor的shape
shape = tf.TensorShape(input_shape).as_list()
shape[-1] = self.output_dim
return tf.TensorShape(shape)
def get_config(self):
base_config = super(MyLayer, self).get_config()
base_config['output_dim'] = self.output_dim
return base_config
@classmethod
def from_config(cls, config):
return cls(**config)
- 使用层
model = tf.keras.Sequential(
[
MyLayer(10),
layers.Activation('softmax')
]
)
model.compile(optimizer=keras.optimizers.RMSprop(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(data, labels, batch_size=32, epochs=5)
7.3.2.4 回调
- 示例代码
callbacks = [
# 如果`val_loss`在超过两个epoch都没有提升,那么中断训练
tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
# 把TensorBoard的日志写入文件夹`./logs`
tf.keras.callbacks.TensorBoard(log_dir='logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
validation_data=(val_data, val_labels))
7.3.2.5 保存与载入
# 把权重保存为TensorFlow Checkpoint文件
model.save_weights('weights/my_model')
# 载入权重。要求模型和保存权重的模型具有相同的架构
model.load_weights('weights/my_model')
7.4 keras设置层的参数
- activation:设置层使用的激活函数。指定方法:名称或可调用对象默认为空
- kernel_initializer 和bias_initializer:设置层创建时,权重和偏差的初始化方法。指定方法:名称或可调用对象默认为"Glorot uniform"initializer
- kernel_regularizer 和bias_regularizer:设置层的权重、偏差的正则化方法。比如:L1 或L2 正则。默认为空
7.5 keras训练和评估
7.5.1 配置训练
- 构建模型后,通过调用compile函数configure,tf.keras.Model.compile有三个重要参数(
Model.compile(
optimizer="rmsprop",
loss=None,
metrics=None,
loss_weights=None,
weighted_metrics=None,
run_eagerly=None,
steps_per_execution=None,
**kwargs
)
- optimizer:指定优化器。从tf.train模块传递优化器实例,例如tf.train.AdamOptimizer tf.train.RMSPropOptimizer或tf.train.GradientDescentOptimizer。(tf1.0)
- loss:在优化期间最小化的函数。常见的选择包括均方误差(mse),categorical_crossentropy和binary_crossentropy。损失函数由名称或通过从tf.keras.losses模块传递可调用对象来指定
- metrics:设置训练中要输出的指标(原文为metrics,这里翻译为指标)组成的列表,上面的代码中就只有准确率(accuracy)。指标是来自tf.keras.metrics模块的字符串名称或可调用对象组成的list
model.compile(
optimizer=tf.optimizers.Adam(0.001),
loss='categorical_crossentropy',
metrics=['accuracy']
)
# 编译均方误差回归模型(a model for mean-squared error regression)
#model.compile(optimizer=tf.train.AdamOptimizer(0.01),
model.compile(optimizer=tf.optimizers.Adam(0.01),
loss='mse', # 最小均方误差(mean squared error)
metrics=['mae']) # 平均绝对误差(mean absolute error)
# 编译一个分类模型(a model for categorical classification)
# 多元分类问题例子,比如手写数字识别
model.compile(optimizer=tf.optimizers.RMSprop(0.01),
loss=tf.keras.losses.categorical_crossentropy,
metrics=[tf.keras.metrics.categorical_accuracy])
# 和上面的相同
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
# 二元分类例子
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
7.5.2 训练模型
- 小数据,使用fit方法将模型“拟合”到训练数据
Model.fit(
x=None,
y=None,
batch_size=None,
epochs=1,
verbose=1,
callbacks=None,
validation_split=0.0,
validation_data=None,
shuffle=True,
class_weight=None,
sample_weight=None,
initial_epoch=0,
steps_per_epoch=None,
validation_steps=None,
validation_batch_size=None,
validation_freq=1,
max_queue_size=10,
workers=1,
use_multiprocessing=False,
)
- epoch:训练多少个epoch。一个epoch是对整个训练数据集的一次训练(这是以较小的批次完成的)
- batch_size:当传递NumPy数据时,模型将数据分成较小的批次(batch),并在训练期间训练这些批次。此整数指定每个批次的大小。请注意,如果样本总数不能被批次大小整除,则最后一批可能会更小
- validation_data:在对模型进行原型设计时,若要监控其在某些验证数据集上的性能。传递由(输入,标签)组成的元组, 模型在每个epoch的末尾显示损失和指标。
model.fit(data, labels, epochs=10, batch_size=32)
model.fit(data, labels, epochs=10, batch_size=32,
validation_data=(val_data, val_labels))
7.5.3 评估
Model.evaluate(
x=None,
y=None,
batch_size=None,
verbose=1,
sample_weight=None,
steps=None,
callbacks=None,
max_queue_size=10,
workers=1,
use_multiprocessing=False,
return_dict=False,
)
# example
model.evaluate(data, labels, batch_size=32)
7.5.4 预测
Model.predict(
x,
batch_size=None,
verbose=0,
steps=None,
callbacks=None,
max_queue_size=10,
workers=1,
use_multiprocessing=False,
)
# example
result = model.predict(data, batch_size=32)
7.6 MINIST实例
- 定义网络
import pydot_ng as pydot
inputs = tf.keras.Input(shape=(784,), name='img')
h1 = layers.Dense(32, activation='relu')(inputs)
h2 = layers.Dense(32, activation='relu')(h1)
outputs = layers.Dense(10, activation='softmax')(h2)
model = tf.keras.Model(inputs=inputs, outputs=outputs, name='mnistmodel')
model.summary()
keras.utils.plot_model(model, 'mnist_model.png')
keras.utils.plot_model(model, 'model_info.png', show_shapes=True)
- 加载数据
#加载数据
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# data must reside in: C:\\Users\\iamlu\\.keras\\datasets\\minist/minist.npz
# (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data('minist/minist.npz')
- 训练、验证及测试
#加载数据
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# data must reside in: C:\\Users\\iamlu\\.keras\\datasets\\minist/minist.npz
# (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data('minist/minist.npz')
x_train = x_train.reshape(60000, 784).astype('float32') /255
x_test = x_test.reshape(10000, 784).astype('float32') /255
model.compile(optimizer=keras.optimizers.RMSprop(),
loss='sparse_categorical_crossentropy', # 直接填api,后面会报错
metrics=['accuracy'])
history = model.fit(x_train, y_train, batch_size=64, epochs=5, validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=0)
print('test loss:', test_scores[0])
print('test acc:', test_scores[1])
- 模型保存和序列化
model.save('model_save.h5')
del model
model_x = keras.models.load_model('model_save.h5')
model_x.summary()
7.7 keras Callback
7.7.1 Callback的本质
- 回调函数只是一个很形象的说法,它的本质是一个类
- 回调函数是一组在训练的特定阶段被调用的函数集,你可以使用回调函数来观察训练过程中网络内部的状态和统计信息
- 通过传递回调函数列表到模型的.fit()中,即可在给定的训练阶段调用该函数集中的函数
7.7.2 Callback的定义
- tf.keras.callbacks.Callback(),这是回调函数的抽象类,定义新的回调函数必须继承自该类
- Callback属性:
属性 | 描述 |
---|---|
params | 字典,训练参数集(如信息显示方法verbosity,batch大小,epoch数等) |
model | keras.models.Model对象,是正在训练的模型的引用 |
- history = model.fit():
- history对应的类是Callback的派生类
- 该回调函数在Keras模型上会被自动调用
- History对象即为fit方法的返回值
- Callback定义
@keras_export('keras.callbacks.Callback')
class Callback(object):
"""Abstract base class used to build new callbacks.
Attributes:
params: Dict. Training parameters
(eg. verbosity, batch size, number of epochs...).
model: Instance of `keras.models.Model`.
Reference of the model being trained.
The `logs` dictionary that callback methods
take as argument will contain keys for quantities relevant to
the current batch or epoch (see method-specific docstrings).
"""
def __init__(self):
self.validation_data = None # pylint: disable=g-missing-from-attributes
self.model = None
# Whether this Callback should only run on the chief worker in a
# Multi-Worker setting.
# TODO(omalleyt): Make this attr public once solution is stable.
self._chief_worker_only = None
self._supports_tf_logs = False
def set_params(self, params):
self.params = params
def set_model(self, model):
self.model = model
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_batch_begin(self, batch, logs=None):
"""A backwards compatibility alias for `on_train_batch_begin`."""
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_batch_end(self, batch, logs=None):
"""A backwards compatibility alias for `on_train_batch_end`."""
@doc_controls.for_subclass_implementers
def on_epoch_begin(self, epoch, logs=None):
"""Called at the start of an epoch.
Subclasses should override for any actions to run. This function should only
be called during TRAIN mode.
Arguments:
epoch: Integer, index of epoch.
logs: Dict. Currently no data is passed to this argument for this method
but that may change in the future.
"""
@doc_controls.for_subclass_implementers
def on_epoch_end(self, epoch, logs=None):
"""Called at the end of an epoch.
Subclasses should override for any actions to run. This function should only
be called during TRAIN mode.
Arguments:
epoch: Integer, index of epoch.
logs: Dict, metric results for this training epoch, and for the
validation epoch if validation is performed. Validation result keys
are prefixed with `val_`.
"""
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_train_batch_begin(self, batch, logs=None):
"""Called at the beginning of a training batch in `fit` methods.
Subclasses should override for any actions to run.
Arguments:
batch: Integer, index of batch within the current epoch.
logs: Dict, contains the return value of `model.train_step`. Typically,
the values of the `Model`'s metrics are returned. Example:
`{'loss': 0.2, 'accuracy': 0.7}`.
"""
# For backwards compatibility.
self.on_batch_begin(batch, logs=logs)
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_train_batch_end(self, batch, logs=None):
"""Called at the end of a training batch in `fit` methods.
Subclasses should override for any actions to run.
Arguments:
batch: Integer, index of batch within the current epoch.
logs: Dict. Aggregated metric results up until this batch.
"""
# For backwards compatibility.
self.on_batch_end(batch, logs=logs)
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_test_batch_begin(self, batch, logs=None):
"""Called at the beginning of a batch in `evaluate` methods.
Also called at the beginning of a validation batch in the `fit`
methods, if validation data is provided.
Subclasses should override for any actions to run.
Arguments:
batch: Integer, index of batch within the current epoch.
logs: Dict, contains the return value of `model.test_step`. Typically,
the values of the `Model`'s metrics are returned. Example:
`{'loss': 0.2, 'accuracy': 0.7}`.
"""
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_test_batch_end(self, batch, logs=None):
"""Called at the end of a batch in `evaluate` methods.
Also called at the end of a validation batch in the `fit`
methods, if validation data is provided.
Subclasses should override for any actions to run.
Arguments:
batch: Integer, index of batch within the current epoch.
logs: Dict. Aggregated metric results up until this batch.
"""
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_predict_batch_begin(self, batch, logs=None):
"""Called at the beginning of a batch in `predict` methods.
Subclasses should override for any actions to run.
Arguments:
batch: Integer, index of batch within the current epoch.
logs: Dict, contains the return value of `model.predict_step`,
it typically returns a dict with a key 'outputs' containing
the model's outputs.
"""
@doc_controls.for_subclass_implementers
@generic_utils.default
def on_predict_batch_end(self, batch, logs=None):
"""Called at the end of a batch in `predict` methods.
Subclasses should override for any actions to run.
Arguments:
batch: Integer, index of batch within the current epoch.
logs: Dict. Aggregated metric results up until this batch.
"""
@doc_controls.for_subclass_implementers
def on_train_begin(self, logs=None):
"""Called at the beginning of training.
Subclasses should override for any actions to run.
Arguments:
logs: Dict. Currently no data is passed to this argument for this method
but that may change in the future.
"""
@doc_controls.for_subclass_implementers
def on_train_end(self, logs=None):
"""Called at the end of training.
Subclasses should override for any actions to run.
Arguments:
logs: Dict. Currently the output of the last call to `on_epoch_end()`
is passed to this argument for this method but that may change in
the future.
"""
@doc_controls.for_subclass_implementers
def on_test_begin(self, logs=None):
"""Called at the beginning of evaluation or validation.
Subclasses should override for any actions to run.
Arguments:
logs: Dict. Currently no data is passed to this argument for this method
but that may change in the future.
"""
@doc_controls.for_subclass_implementers
def on_test_end(self, logs=None):
"""Called at the end of evaluation or validation.
Subclasses should override for any actions to run.
Arguments:
logs: Dict. Currently the output of the last call to
`on_test_batch_end()` is passed to this argument for this method
but that may change in the future.
"""
@doc_controls.for_subclass_implementers
def on_predict_begin(self, logs=None):
"""Called at the beginning of prediction.
Subclasses should override for any actions to run.
Arguments:
logs: Dict. Currently no data is passed to this argument for this method
but that may change in the future.
"""
@doc_controls.for_subclass_implementers
def on_predict_end(self, logs=None):
"""Called at the end of prediction.
Subclasses should override for any actions to run.
Arguments:
logs: Dict. Currently no data is passed to this argument for this method
but that may change in the future.
"""
def _implements_train_batch_hooks(self):
"""Determines if this Callback should be called for each train batch."""
return (not generic_utils.is_default(self.on_batch_begin) or
not generic_utils.is_default(self.on_batch_end) or
not generic_utils.is_default(self.on_train_batch_begin) or
not generic_utils.is_default(self.on_train_batch_end))
def _implements_test_batch_hooks(self):
"""Determines if this Callback should be called for each test batch."""
return (not generic_utils.is_default(self.on_test_batch_begin) or
not generic_utils.is_default(self.on_test_batch_end))
def _implements_predict_batch_hooks(self):
"""Determines if this Callback should be called for each predict batch."""
return (not generic_utils.is_default(self.on_predict_batch_begin) or
not generic_utils.is_default(self.on_predict_batch_end))
- 回调函数的参数 logs
- logs:是字典, 它包含了一系列与当前batch或epoch相关的信息
- model.fit()中有以下参数会被记录到logs中
时间点 | 记录的信息 |
---|---|
on_epoch_end | logs将包含训练的正确率和误差,acc和loss,如果指定了验证集,还会包含验证集的正确率和误差val_acc和val_loss,val_acc还额外需要在.compile中启用metrics=[‘accuracy’]。 |
on_train_batch_begin | logs包含size,即当前batch的样本数 |
on_train_batch_end | logs包含loss,若启用accuracy则还包含acc |
7.7.3 Callback的用途
-
在训练达预期目标时,自动停止训练
- 在我们训练神经网络时,对于训练数据的迭代次数(epochs)的设置,是一个值得思考的问题
- epochs 越大,最后训练的损失值会越小,但是迭代次数过大,会导致过拟合的现象
- 希望当loss值,或准确率达到一定值后,就停止训练。但是我们不可能去人为的等待或者控制
- tensorfow 中的回调机制,就为我们很好的处理了这个问题
-
Callback回调时机及数据
- 状态和统计:模型在训练过程中需要从过程中获取什么信息,比如损失loss,准确率accuracy等信息就是训练过程中的状态与统计信息;再比如希望每一个epoch结束之后打印一些相应的自定义提示信息,这也是状态信息。
- 各自的阶段:模型的训练一般是分为多少个epoch,然后每一个epoch又分为多少个batch,所以这个阶段可以是在每一个epoch之后执行回调函数,也可以是在每一个batch之后执行回调函数。
-
何时停止训练
- 一个好方法就是在测试识别率不再上升的时候,我们终止训练就可以了,callback可以帮助我们做到这一点
- callback是一个obj类型的,它可以让模型去拟合,也常在各个点被调用。它和所有模型的 状态和表现的数据,能够采取措施打断训练,保存模型,加载不同的权重,或者替代模型状态
-
Callback常用于:
- 模型断点续训:保存当前模型的所有权重
- 提早结束:当模型的损失不再下降的时候就终止训练,当然,会保存最优的模型
- 动态调整训练时的参数,比如优化的学习速度
7.7.4 系统预定义回调函数
回调类 | 功能 |
---|---|
class Callback | 用于建立新回调的抽象基类,下面所有类的基类 |
class BaseLogger | 累积指标的时期平均值的回调 |
class CSVLogger | 将纪元结果流式传输到csv文件的回调 |
class EarlyStopping | 当监视的数量停止改善时,停止训练 |
class History | 将事件记录到History对象中的回调 |
class LambdaCallback | 用于即时创建简单,自定义回调的回调 |
class LearningRateScheduler | 学习率调度程序 |
class ModelCheckpoint | 每个时期后保存模型 |
class ProgbarLogger | 将指标输出到标准输出的回调 |
class ReduceLROnPlateau | 当指标停止改善时,降低学习率 |
class RemoteMonitor | 用于将事件流传输到服务器的回调 |
class TensorBoard | 为TensorBoard启用可视化 |
class TerminateOnNaN | 当遇到NaN丢失时回调将终止训练 |
7.7.5 Callback示例
import tensorflow as tf
from tensorflow.keras.utils import get_file
import gzip
import numpy as np
print(tf.__version__)
class myCallback(tf.keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs={}):
if(logs.get('loss')<0.4):
print("\nReached 60% accuracy so cancelling training!")
self.model.stop_training = True
(training_images, training_labels), (test_images, test_labels) =tf.keras.datasets.fashion_mnist.load_data()
training_images=training_images/255.0
test_images=test_images/255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(512, activation=tf.nn.relu),
tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
myCB = myCallback()
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')
model.fit(training_images, training_labels, epochs=5, callbacks=[myCB])
- 输出
Train on 60000 samples
Epoch 1/5
60000/60000 [==============================] - 4s 67us/sample - loss: 0.4745
Epoch 2/5
59584/60000 [============================>.] - ETA: 0s - loss: 0.3592
Reached 60% accuracy so cancelling training!
60000/60000 [==============================] - 4s 63us/sample - loss: 0.3590