教程 | TensorFlow 1.11 教程 —— 研究与实验 —— 自定义训练:基础(9.15 ver.)

更新至 2018-9-15 版本

译自:TensorFlow 官方教程

在上一个教程中,我们介绍了 TensorFlow 中的自动微分 API,这是一个用于机器学习的基本构造块。本教程中,我们将使用 TensorFlow 原始代码来进行简单的机器学习。

TensorFlow 也包含了一个高级神经网络 API(tf.keras),我们强烈建议使用神经网络的人使用这些高级 API。然而,在这个小教程中我们将从基本原则建立神经网络训练。


设置

import tensorflow as tf

tf.enable_eager_execution()

变量(Variable)

TensorFlow 中的张量是不可变的无状态对象。然而机器学习模型需要改变状态:随着模型的训练,相同的代码计算的预测都应不同(最好是更低的损失!)。为了表示在计算过程中需要更改的状态,你可以选择 Python 这种状态编程语言:

# 使用 python 状态
x = tf.zeros([10, 10])
x += 2  # 相当于 x = x + 2
print(x)
tf.Tensor(
[[2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]], shape=(10, 10), dtype=float32)

TensorFlow 内建了状态操作,这些操作通常比低级 Python 表示更易于使用。例如,为了表示模型中的权重,使用 TensorFlow 变量通常是方便而高效的。

变量是存储值的对象,当在 TensorFlow 计算中使用时,将会隐式地读取该值。tf.assign_subtf.scatter_update 等操作可以使用 TensorFlow 变量中存储的值。

v = tf.contrib.eager.Variable(1.0)
assert v.numpy() == 1.0

# 重新赋值
v.assign(3.0)
assert v.numpy() == 3.0

# 在 TensorFlow 操作(例如 tf.square)中使用 'v' 并且重新赋值
v.assign(tf.square(v))
assert v.numpy() == 9.0

(原文中 v = tf.Variable(1.0),运行时报错,当启用 eager execution 时需要使用 tf.contrib.eager.Variable

使用变量进行计算时,会自动跟踪梯度。对于表示嵌入的变量,TensorFlow 在默认情况下会进行稀疏更新,这样计算和存储效率更高。使用变量还可以快速地让代码的读者知道这个状态是可变的。


示例:拟合线性模型

现在,让我们把目前仅有的几个概念 —— 张量、梯度磁带、变量 —— 放在一起,来构建和训练一个简单的模型。这通常涉及几个步骤:

  1. 定义模型
  2. 定义损失函数
  3. 获取训练数据
  4. 遍历训练数据并且使用“优化器”调整变量以拟合数据

在本教程中,我们将介绍一个简单线性模型:
f ( x ) = x W + b f(x) = x * W + b
它有两个变量 —— W 和 b。此外,我们将合成数据,使训练的模型得到 W = 3.0 和 b = 2.0。

定义模型

让我们定义一个简单的类来封装变量和计算。

class Model(object):
  def __init__(self):
    # 初始化变量为 (5.0, 0.0)
    # 实际上,变量应初始化为随机值
    self.W = tf.contrib.eager.Variable(5.0)
    self.b = tf.contrib.eager.Variable(0.0)
    
  def __call__(self, x):
    return self.W * x + self.b
  
model = Model()

assert model(3.0).numpy() == 15.0

定义损失函数

损失函数度量给定输入的模型输出与期望输出的匹配程度。我们用标准的 L2 损失。

def loss(predicted_y, desired_y):
  return tf.reduce_mean(tf.square(predicted_y - desired_y))

获取训练数据

让我们合成带噪声的训练数据。

TRUE_W = 3.0
TRUE_b = 2.0
NUM_EXAMPLES = 1000

inputs  = tf.random_normal(shape=[NUM_EXAMPLES])
noise   = tf.random_normal(shape=[NUM_EXAMPLES])
outputs = inputs * TRUE_W + TRUE_b + noise

在我们训练模型之前,让我们可视化当前模型。我们将用红色表示模型的预测,蓝色表示训练数据。

import matplotlib.pyplot as plt

plt.scatter(inputs, outputs, c='b')
plt.scatter(inputs, model(inputs), c='r')
plt.show()

print('Current loss: '),
print(loss(model(inputs), outputs).numpy())
Current loss: 
9.489469

这里写图片描述

定义训练循环

我们现在有了自己的网络和训练数据。训练网络就是利用训练数据更新模型的变量(W 和 b),使用梯度下降法使损失降低。tf.train.Optimizer 中有许多梯度下降方案的实现。我们强烈建议使用这些实现,但本着从基本原则构建的精神,在这个特定的示例中,我们将自己实现基本的数学方法。

def train(model, inputs, outputs, learning_rate):
  with tf.GradientTape() as t:
    current_loss = loss(model(inputs), outputs)
  dW, db = t.gradient(current_loss, [model.W, model.b])
  model.W.assign_sub(learning_rate * dW)
  model.b.assign_sub(learning_rate * db)

最后,让我们反复遍历训练数据,看看 W 和 b 是如何变化的。

model = Model()

# 收集 W 和 b 的历史值用于绘图
Ws, bs = [], []
epochs = range(10)
for epoch in epochs:
  Ws.append(model.W.numpy())
  bs.append(model.b.numpy())
  current_loss = loss(model(inputs), outputs)

  train(model, inputs, outputs, learning_rate=0.1)
  print('Epoch %2d: W=%1.2f b=%1.2f, loss=%2.5f' %
        (epoch, Ws[-1], bs[-1], current_loss))

# 绘制图形
plt.plot(epochs, Ws, 'r',
         epochs, bs, 'b')
plt.plot([TRUE_W] * len(epochs), 'r--',
         [TRUE_b] * len(epochs), 'b--')
plt.legend(['W', 'b', 'true W', 'true_b'])
plt.show()
Epoch  0: W=5.00 b=0.00, loss=9.48947
Epoch  1: W=4.57 b=0.42, loss=6.23411
Epoch  2: W=4.23 b=0.75, loss=4.22776
Epoch  3: W=3.96 b=1.01, loss=2.99100
Epoch  4: W=3.76 b=1.22, loss=2.22850
Epoch  5: W=3.60 b=1.39, loss=1.75832
Epoch  6: W=3.47 b=1.51, loss=1.46833
Epoch  7: W=3.37 b=1.62, loss=1.28944
Epoch  8: W=3.30 b=1.70, loss=1.17906
Epoch  9: W=3.24 b=1.76, loss=1.11095

在这里插入图片描述

下一步

在本教程中,我们介绍了变量,并使用当前讨论的 TensorFlow 原始代码构建和训练了一个简单的线性模型。

从理论上讲,这几乎就是你在机器学习研究中使用 TensorFlow 所需的全部内容。在实践中,特别是对于神经网络,像 tf.keras 这样的高级 API 将使模型构建更加方便,因为它提供了更高级的构造块(称为“层”),以及用于保存和恢复状态的实用程序,一组损失函数,一组优化策略等。

下一个教程将介绍这些高级 API。

猜你喜欢

转载自blog.csdn.net/qq_20084101/article/details/82662142