TensorFlow 基础概念
本章将对TensorFlow的基本概念做简介,每节会对一个大概念做一个概论--你可以在每节末尾找到更详细的链接地址。
TensorFlow是机器学习的一个端对端平台,支持以下操作:
- 多维数组的数字计算
- GPU和分布式运算
- 自动微分
- 模型构建、训练和导出
- 以及其他
Tensors
TensorFlow的运算是基于多维数组或张量,这些运算对象在TensorFlow中被标识为tf.Tensor对象。下面的代码描述了一个二维张量:
import tensorflow as tf
x = tf.constant([[1., 2., 3. ], [4., 5., 6. ]])
print(x)
print(x.shape)
print(x.dtype)
运行结果为:
tf.Tensor(
[[1. 2. 3.]
[4. 5. 6.]], shape=(2, 3), dtype=float32)
(2, 3)
<dtype: 'float32'>
对于Tensor来说,这里有两个相当重要的概念shape和dtype:
- Tensor.shape: 表示获取张量在各个维度的大小。
- Tensor.dtype: 表示张量中所有元素的类型。
TensorFlow在实现了基于张量的标准数字操作符的同时也实现了机器学习中常用的专用的运算符。如:
x + x
结果为:
tf.Tensor(
[[ 2. 4. 6.]
[ 8. 10. 12.]], shape=(2, 3), dtype=float32)
5 * x
结果为:
tf.Tensor(
[[ 5. 10. 15.]
[20. 25. 30.]], shape=(2, 3), dtype=float32)
x @ tf.transpose(x)
结果为:
tf.Tensor(
[[14. 32.]
[32. 77.]], shape=(2, 2), dtype=float32)
tf.concat([x, x, x], axis=0)
结果为:
tf.Tensor(
[[1. 2. 3.]
[4. 5. 6.]
[1. 2. 3.]
[4. 5. 6.]
[1. 2. 3.]
[4. 5. 6.]], shape=(6, 3), dtype=float32)
tf.concat([x, x, x], axis=1)
结果为:
tf.Tensor(
[[1. 2. 3. 1. 2. 3. 1. 2. 3.]
[4. 5. 6. 4. 5. 6. 4. 5. 6.]], shape=(2, 9), dtype=float32)
tf.nn.softmax(x, axis=1)
结果为:
tf.Tensor(
[[0.09003057 0.24472848 0.66524094]
[0.09003057 0.24472848 0.66524094]], shape=(2, 3), dtype=float32)
tf.reduce_sum(x)
结果为:
tf.Tensor(21.0, shape=(), dtype=float32)
在CPU上执行大规模计算时,效率并不高。在进行配置之后,TensorFlow可以运行在如GPU之类的硬件之上执行运行操作,效率很高。
if tf.config.list_physical_devices('GPU'):
print("TensorFlow **IS** using the GPU")
else:
print("TensorFlow **IS NOT** using the GPU")
结果为:
TensorFlow **IS NOT** using the GPU
更多细节参考Tensor Guide
变量
tf.Tensor类型是不可变类型,当需要保存如模型参数之类的变量时,可以使用tf.Variable类型
var = tf.Variable([0.0, 0.0, 0.0])
var.assign([1, 2, 3])
<tf.Variable 'Variable:0' shape=(3,) dtype=float32, numpy=array([1., 2., 3.], dtype=float32)>
var.assign_add([1, 1, 1])
<tf.Variable 'Variable:0' shape=(3,) dtype=float32, numpy=array([2., 3., 4.], dtype=float32)>
更多细节参阅Variables guide
自动微分
梯度下降法及相关算法是现代机器学习的重要基础。
TensorFlow实现了基于微积分学的自动微分,可以帮助你自动计算梯度。通常你会使用这个功能来计算模型的loss关于其参数的梯度。
import tensorflow as tf
x = tf.Variable(1.0)
def f(x):
y = x ** 2 + 2 * x -5
return y
f(x)
结果为:
tf.Tensor(-2.0, shape=(), dtype=float32)
在x=1.0,y=f(x) = -2,其导数为y' = 2 * x + 2 = 4。TensorFlow可以自动计算出导数
with tf.GradientTape() as tape:
y = f(x)
g_x = tape.gradient(y, x)
g_x
运行结果为:
tf.Tensor(4.0, shape=(), dtype=float32)
这是一个对简单标量x求导数的例子。TensorFlow可以对任意数量的张量同时计算梯度。
参考自动微分获得更多的详情。
图和tf.function
TensorFlow可以让你像使用其他python库一样交互式地使用TensorFlow,TensorFlow还提供了一些工具:
- 性能优化:加速训练和推算过程
- 导出:在训练结束之后保存模型
这需要你使用tf.function让TensorFlow代码与Python代码分离。
@tf.function
def my_func(x):
print('Tracing')
return tf.reduce_sum(x)
x = tf.constant([1, 2, 3])
print(my_func(x))
结果为:
Tracing
tf.Tensor(6, shape=(), dtype=int32)
在接下来的调用中,TensorFlow只会运行优化过的图代码,跳过非TensorFlow代码。下面的代码中my_func不会打印“Tracing”字符,因为print是一个纯python代码,不是TensorFlow的函数。
x = tf.constant([10, 9, 8])
my_func(x)
结果为:
tf.Tensor(27, shape=(), dtype=int32)
一个图方法会为不同的入参类型(shape and dtype)创建不同的实例,即下面的代码会生成一个新的图。
x = tf.constant([10.0, 9.1, 8.2], dtype=tf.float32)
my_func(x)
这种图快照提供了两个优点:
- 很多情况下图快照可以提供显著的计算加速效果
- 你可以使用tf.saved_model导出图,并将其运行在其他系统上(如服务器和移动设备),而无需Python环境。
Modules, Layers 和models
tf.Module是用于管理tf.Variables对象以及基于这些对象的tf.function对象的类。tf.Module类是实现如下两个重要功能特性的必要条件:
- 使用tf.train.CheckPoint实现变量保存的功能,可以快速保存在训练过程中的模型状态
- 使用tf.saved_model来导入导出tf.Variable的值和tf.function的图,这使得你可以脱离Python环境而在任意的设备上运行你的模型。
下面是一个简单却又完整的tf.Module导出过程:
import tensorflow as tf
class MyModule(tf.Module):
def __init__(self, value):
self.weight = tf.Variable(value)
@tf.function
def multiply(self, x):
return x * self.weight
mod = MyModule(3)
mod.multiply(tf.constant([1, 2, 3]))
save_path = './saved'
tf.saved_model.save(mod, save_path)
reload = tf.saved_model.load(save_path)
print(reload.multiply(tf.constant([1, 2, 3])))
保存结果SavedModel独立于创建时的代码。你可以重新载入SavedModel至Python、其他支持TensorFlow的编程语言或TensorFlow Serving。你也可以将其运行在 TensorFlow Lite 或 TensorFlow JS上
基于tf.Module的tf.keras.layers.Layer和tf.keras.Model类提供了更多的功能和便捷的方法用于编译、训练和保存模型,这些将在下一节提及。
更多细节请参考Intro to modules
Training Loops
下面,我们将上述的所有概念综合使用起来建立一个模型,并且从头开始进行训练。
首先我们创建一下测试数据,使用二次曲线产生一些数据点。
import tensorflow as tf
import matplotlib
from matplotlib import pyplot as plt
matplotlib.rcParams['figure.figsize'] = [9, 6]
x = tf.linspace(-2, 2, 201)
x = tf.cast(x, tf.float32)
def f(x):
y = x ** 2 + 2 * x - 5
return y
y = f(x) + tf.random.normal(shape=[201])
plt.plot(x.numpy(), y.numpy(), '.', label='Data')
plt.plot(x, f(x), label='Ground truth')
plt.legend()
plt.show()
Figure_1.png
然后,创建一个模型
class Model(tf.keras.Model):
def __init__(self, units):
super(Model, self).__init__()
self.dense1 = tf.keras.layers.Dense(units=units,
activation=tf.nn.relu,
kernel_initializer=tf.random.normal,
bias_initializer=tf.random.normal)
self.dense2 = tf.keras.layers.Dense(1)
def call(self, x, training=True):
x = x[:, tf.newaxis]
x = self.dense1(x)
x = self.dense2(x)
return tf.squeeze(x, axis=1)
model = Model(64)
plt.plot(x.numpy(), y.numpy(), '.', label='Data')
plt.plot(x, f(x), label='Ground truth')
plt.plot(x, model(x), label='Untrained predictions')
plt.title('Before training')
plt.legend()
plt.show()
Figure_1.png
编写代码进入训练阶段。由于tf.keras已经集成了大多数常用的训练工具,因此直接调用keras自带的功能即可。为此,你需要使用Model.compile和Model.fit方法来实现训练过程。
model.compile(loss=tf.keras.losses.MSE, optimizer=tf.keras.optimizers.SGD(learning_rate=0.01))
history = model.fit(x, y, epochs=100, batch_size=32, verbose=0)
model.save("./my_model")
将训练过程中的loss打印出来:
plt.plot(history.history['loss'])
plt.xlabel('Epoch')
plt.ylim([0, max(plt.ylim())])
plt.ylabel('Loss [Mean Squared Error]')
plt.title('Keras training progress')
plt.show()
Figure_1.png
更多详情请参考 Basic training loops 与 Keras guide