TensorFlow 基础

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看源代码 下载笔记本

本指南简要概述了TensorFlow 基础。本文档的每个部分都概述了一个更大的主题 - 您可以在每个部分的末尾找到指向完整指南的链接。

TensorFlow 是一个端到端的机器学习平台。它支持以下内容

  • 基于多维数组的数值计算(类似于 NumPy。)
  • GPU 和分布式处理
  • 自动微分
  • 模型构建、训练和导出
  • 等等

张量

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 的最重要的属性是它的 shapedtype

TensorFlow 在张量上实现了标准数学运算,以及许多专门用于机器学习的运算。

例如

x + x
5 * x
x @ tf.transpose(x)
tf.concat([x, x, x], axis=0)
tf.nn.softmax(x, axis=-1)
tf.reduce_sum(x)
tf.convert_to_tensor([1,2,3])
tf.reduce_sum([1,2,3])

在 CPU 上运行大型计算可能很慢。如果配置正确,TensorFlow 可以使用 GPU 等加速器硬件来非常快地执行操作。

if tf.config.list_physical_devices('GPU'):
  print("TensorFlow **IS** using the GPU")
else:
  print("TensorFlow **IS NOT** using the GPU")

有关详细信息,请参阅 张量指南

变量

普通的 tf.Tensor 对象是不可变的。要在 TensorFlow 中存储模型权重(或其他可变状态),请使用 tf.Variable

var = tf.Variable([0.0, 0.0, 0.0])
var.assign([1, 2, 3])
var.assign_add([1, 1, 1])

有关详细信息,请参阅 变量指南

自动微分

梯度下降 及其相关算法是现代机器学习的基石。

为了实现这一点,TensorFlow 实现了自动微分 (autodiff),它使用微积分来计算梯度。通常,您将使用它来计算模型误差损失相对于其权重的梯度。

x = tf.Variable(1.0)

def f(x):
  y = x**2 + 2*x - 5
  return y
f(x)

x = 1.0 时,y = f(x) = (1**2 + 2*1 - 5) = -2

y 的导数为 y' = f'(x) = (2*x + 2) = 4。TensorFlow 可以自动计算这一点

with tf.GradientTape() as tape:
  y = f(x)

g_x = tape.gradient(y, x)  # g(x) = dy/dx

g_x

这个简化的示例只计算了相对于单个标量 (x) 的导数,但 TensorFlow 可以同时计算相对于任意数量的非标量张量的梯度。

有关详细信息,请参阅 自动微分指南

图和 tf.function

虽然您可以像使用任何 Python 库一样交互式地使用 TensorFlow,但 TensorFlow 还提供用于以下方面的工具:

  • 性能优化:加速训练和推理。
  • 导出:以便您可以在训练完成后保存模型。

这些要求您使用 tf.function 将纯 TensorFlow 代码与 Python 代码分开。

@tf.function
def my_func(x):
  print('Tracing.\n')
  return tf.reduce_sum(x)

您第一次运行 tf.function 时,虽然它在 Python 中执行,但它会捕获一个完整的、优化的图,该图表示函数内完成的 TensorFlow 计算。

x = tf.constant([1, 2, 3])
my_func(x)

在后续调用中,TensorFlow 只执行优化的图,跳过任何非 TensorFlow 步骤。请注意,下面的 my_func 不会打印跟踪,因为 print 是一个 Python 函数,而不是 TensorFlow 函数。

x = tf.constant([10, 9, 8])
my_func(x)

对于具有不同签名shapedtype)的输入,图可能无法重用,因此会生成一个新图。

x = tf.constant([10.0, 9.1, 8.2], dtype=tf.float32)
my_func(x)

这些捕获的图提供了两个好处:

  • 在许多情况下,它们可以显着提高执行速度(尽管在这个简单的示例中并非如此)。
  • 您可以使用 tf.saved_model 导出这些图,以便在其他系统(如 服务器移动设备)上运行,无需安装 Python。

有关更多详细信息,请参阅 图简介

模块、层和模型

tf.Module 是一个用于管理 tf.Variable 对象和对它们进行操作的 tf.function 对象的类。 tf.Module 类对于支持两个重要功能是必要的:

  1. 您可以使用 tf.train.Checkpoint 保存和恢复变量的值。这在训练期间很有用,因为它可以快速保存和恢复模型的状态。
  2. 您可以使用 tf.saved_model 导入和导出 tf.Variable以及 tf.function 图。这使您能够独立于创建它的 Python 程序运行模型。

以下是一个完整的示例,用于导出一个简单的 tf.Module 对象:

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]))

保存 Module

save_path = './saved'
tf.saved_model.save(mod, save_path)

生成的 SavedModel 独立于创建它的代码。您可以从 Python、其他语言绑定或 TensorFlow Serving 加载 SavedModel。您还可以将其转换为使用 TensorFlow LiteTensorFlow JS 运行。

reloaded = tf.saved_model.load(save_path)
reloaded.multiply(tf.constant([1, 2, 3]))

tf.keras.layers.Layertf.keras.Model 类基于 tf.Module,提供了用于构建、训练和保存模型的额外功能和便捷方法。下一节将演示其中的一些功能。

有关详细信息,请参阅 模块简介

训练循环

现在将所有这些放在一起,构建一个基本模型并从头开始训练它。

首先,创建一些示例数据。这将生成一个松散地遵循二次曲线的点云。

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();

创建一个具有随机初始化权重和偏差的二次模型。

class Model(tf.Module):

  def __init__(self):
    # Randomly generate weight and bias terms
    rand_init = tf.random.uniform(shape=[3], minval=0., maxval=5., seed=22)
    # Initialize model parameters
    self.w_q = tf.Variable(rand_init[0])
    self.w_l = tf.Variable(rand_init[1])
    self.b = tf.Variable(rand_init[2])

  @tf.function
  def __call__(self, x):
    # Quadratic Model : quadratic_weight * x^2 + linear_weight * x + bias
    return self.w_q * (x**2) + self.w_l * x + self.b

首先,观察模型在训练前的性能。

quad_model = Model()
def plot_preds(x, y, f, model, title):
  plt.figure()
  plt.plot(x, y, '.', label='Data')
  plt.plot(x, f(x), label='Ground truth')
  plt.plot(x, model(x), label='Predictions')
  plt.title(title)
  plt.legend()
plot_preds(x, y, f, quad_model, 'Before training')

现在,为您的模型定义一个损失函数。

鉴于此模型旨在预测连续值,均方误差 (MSE) 是损失函数的良好选择。给定一个预测向量 \(\hat{y}\) 和一个真实目标向量 \(y\),MSE 定义为预测值与真实值之间平方差的平均值。

\(MSE = \frac{1}{m}\sum_{i=1}^{m}(\hat{y}_i -y_i)^2\)

def mse_loss(y_pred, y):
  return tf.reduce_mean(tf.square(y_pred - y))

为模型编写一个基本的训练循环。该循环将利用 MSE 损失函数及其相对于输入的梯度来迭代更新模型的参数。使用小批量进行训练可以提供内存效率和更快的收敛速度。 tf.data.Dataset API 具有用于批处理和混洗的有用函数。

batch_size = 32
dataset = tf.data.Dataset.from_tensor_slices((x, y))
dataset = dataset.shuffle(buffer_size=x.shape[0]).batch(batch_size)
# Set training parameters
epochs = 100
learning_rate = 0.01
losses = []

# Format training loop
for epoch in range(epochs):
  for x_batch, y_batch in dataset:
    with tf.GradientTape() as tape:
      batch_loss = mse_loss(quad_model(x_batch), y_batch)
    # Update parameters with respect to the gradient calculations
    grads = tape.gradient(batch_loss, quad_model.variables)
    for g,v in zip(grads, quad_model.variables):
        v.assign_sub(learning_rate*g)
  # Keep track of model loss per epoch
  loss = mse_loss(quad_model(x), y)
  losses.append(loss)
  if epoch % 10 == 0:
    print(f'Mean squared error for step {epoch}: {loss.numpy():0.3f}')

# Plot model results
print("\n")
plt.plot(range(epochs), losses)
plt.xlabel("Epoch")
plt.ylabel("Mean Squared Error (MSE)")
plt.title('MSE loss vs training iterations');

现在,观察模型在训练后的性能。

plot_preds(x, y, f, quad_model, 'After training')

这有效,但请记住,tf.keras 模块中提供了常见训练实用程序的实现。因此,在编写自己的代码之前,请考虑使用这些代码。首先, Model.compileModel.fit 方法为您实现了训练循环。

首先使用 tf.keras.Sequential 在 Keras 中创建一个顺序模型。最简单的 Keras 层之一是密集层,可以使用 tf.keras.layers.Dense 实例化。密集层能够学习形式为 \(\mathrm{Y} = \mathrm{W}\mathrm{X} + \vec{b}\) 的多维线性关系。为了学习形式为 \(w_1x^2 + w_2x + b\) 的非线性方程,密集层的输入应该是一个数据矩阵,其中 \(x^2\) 和 \(x\) 作为特征。可以使用 lambda 层 tf.keras.layers.Lambda 来执行此堆叠转换。

new_model = tf.keras.Sequential([
    tf.keras.layers.Lambda(lambda x: tf.stack([x, x**2], axis=1)),
    tf.keras.layers.Dense(units=1, kernel_initializer=tf.random.normal)])
new_model.compile(
    loss=tf.keras.losses.MSE,
    optimizer=tf.keras.optimizers.SGD(learning_rate=0.01))

history = new_model.fit(x, y,
                        epochs=100,
                        batch_size=32,
                        verbose=0)

new_model.save('./my_new_model')

观察 Keras 模型在训练后的性能。

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');
plot_preds(x, y, f, new_model, 'After Training: Keras')

有关更多详细信息,请参阅 基本训练循环Keras 指南