全部开发者教程

TensorFlow 入门教程

Python / 使用 TensorFlow 进行微分操作

使用 TensorFlow 进行微分操作

在之前的学习之中,我们所使用的模型,包括 Keras 模型与 Estimator 模型,都是使用已经定义好的模型进行 “组装”,从而达到我们的目的。那么我们能不能自定义一个网络模型,从而进行自定义的训练呢?答案是肯定的。

因此从这节课开始,我们会帮助大家学习如何进行自定义的一些操作,这节课我们便来学习所有机器学习自定义的基础 —— 自定义梯度。

1. 什么是梯度

微分是所有目前几乎所有机器学习的基础,也是 TensorFlow 与 Pytorch 等框架的基础。我们对模型进行优化的过程大致可以分为以下三个步骤:

  • 数据通过模型得到输出;
  • 我们通过计算得到模型中每个参数的梯度;
  • 确定学习的步长(学习率);
  • 按照梯度的方向和学习率对每个参数进行优化。

我们可以很清楚的看到,最后的两步是关键的优化部分,而第二步 —— 求得梯度的一步就是这两个关键步骤的前提和基础。因此我们要首先了解什么是 “梯度”。

梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值 —— 百度百科。

简单来说,就是梯度是一个方向,它会指明某一个参数在哪个方向上面变化地更快。或者不恰当地说(但是却非常容易理解),梯度可以理解为一个参数的导数

因此我们可以通过梯度就可以得到模型的参数在哪个方向上面变化,会使得最终的结果的 Loss 变小;进而我们就可以进行模型的优化工作。

举个例子:

y = x**2 + 4

这是一个很简单的赋值公式:我们将 x 的平方加上 4 ,然后将其赋给 y 。

那么 y 对于 x 求导数,便得到:

dy_dx = 2*x

因此我们在 x 取任意一个值的时候便可以得到 y 对于 x 的梯度。比如当 x 为 5 的时候,那么 y 对于 x 的梯度便为 2 * 5 = 10 。

2. TensorFlow 之中的梯度带

既然我们能了解了什么是微分,那么在 TensorFlow 是如何进行梯度的计算的呢?

答案就是 “梯度带”:

tf.GradientTape()

我们先来看一个简单的例子:

import tensorflow as tf

x = tf.constant(5.0)
with tf.GradientTape(persistent=True) as t:
  t.watch(x)
  y = x * x
  z = y * y
dz_dx = t.gradient(z, x)
dy_dx = t.gradient(y, x)

在这个例子之中,我们定义了两个操作:

  y = x * x
  z = y * y

我们令 y 为 x 的平方,让 z 为 y 的平方,然后我们来追踪 x 的梯度。

那么先让我们将 z 也改写为 x 的形式:

  y = x * x
  z = x * x * x * x

分别对于 x 求导,我们可以得到 y 和 z 的导数:

  dy_dx = 2 * x
  dz_dx = 4 * x * x * x

通过手动计算,我们可以将 x 的数值带入其中,从而得到 y 与 z 关于 x 的梯度值:分别为 10 和 500 。

在上面的代码之中,我们可以输出 dz_dx 和 dy_dx :

print(dz_dx)
print(dy_dx)

我们得到结果:

10.0
500.0

可以看到,程序已经自动求得了梯度,并且与我们计算的完全一致。

具体对于 tf.GradientTape () 这个 API 来说,它会在它自身的上下文之中,将所有执行与发生的操作都记录在一个 tape 上。 当我们计算结束的时候,这个 tape 上面便记录了一个完整的计算过程,那么它便从后向前,使用反向微分算法的方式来进行梯度的计算,从而得到每个变量的梯度。

3. 使用梯度带来进行自动微分

通过上面小节的例子,想必大家已经对使用 TensorFlow 进行梯度求解有了一个大体的了解。

具体来说,我们大致需要经过几个步骤进行自动微分的操作:

  • 定义梯度带 tf.GradientTape () 的上下文;
  • 指定我们要跟踪梯度的数据,tape.watch();
  • 在梯度带上下文之中进行我们想要的操作(一般是让数据通过网络);
  • 通过得到的结果来使用梯度带求得各个参数的梯度;
  • 后续的操作,比如根据梯度进行优化等。

那么让我们来看一个更加实际一些的例子,使用矩阵进行运算。

import tensorflow as tf

x = tf.ones((5, 5))

with tf.GradientTape() as t:
  t.watch(x)
  y = tf.reduce_sum(x)
  y = tf.reduce_sum(y+x)
  z = tf.multiply(y, y)

dz_dx = t.gradient(z, x)
print(dz_dx)

我们可以得到输出,值得注意的是,因为我们的输入都是矩阵,因此得到的梯度也是一个矩阵

tf.Tensor(
[[33800. 33800. 33800. 33800. 33800.]
 [33800. 33800. 33800. 33800. 33800.]
 [33800. 33800. 33800. 33800. 33800.]
 [33800. 33800. 33800. 33800. 33800.]
 [33800. 33800. 33800. 33800. 33800.]], shape=(5, 5), dtype=float32)

如此,我们便求得了梯度,这是我们进行自定义网络的第一步,下一步,我们要将整个网络的计算放在梯度带的上下文之中进行计算。

4. 小结

在这节课之中,我们学习了什么是梯度,然后又了解了 TensorFlow 之中对于梯度求解的 API:tf.GradientTape (),最后我们又通过矩阵的梯度求解了解了该 tf.GradientTape () API 的具体用法。