如何在TensorFlow中使用autograd来解决一个回归问题

118 阅读4分钟

我们通常使用TensorFlow来构建一个神经网络。然而,TensorFlow并不限于此。在幕后,TensorFlow是一个具有自动微分能力的张量库。因此,我们可以很容易地用它来解决梯度下降的数值优化问题。在这篇文章中,我们将展示TensorFlow的自动微分引擎autograd是如何工作的。

在完成本教程后,你将学会:

  • 什么是TensorFlow中的autograd
  • 如何利用autograd和一个优化器来解决一个优化问题

让我们开始吧。

概述 TensorFlow中的Autograd

在TensorFlow 2.x中,我们可以将变量和常量定义为TensorFlow对象,并用它们构建一个表达式。该表达式本质上是变量的一个函数。因此,我们可以推导出其导数函数,即微分或梯度。这个特征是TensorFlow中的许多基本特征之一。深度学习模型将在训练循环中利用这一点。

用一个例子来解释autograd比较容易。在TensorFlow 2.x中,我们可以创建一个常量矩阵,如下所示。

import tensorflow as tf

x = tf.constant([1, 2, 3])
print(x)
print(x.shape)
print(x.dtype)

上面的打印结果。

tf.Tensor([1 2 3], shape=(3,), dtype=int32)
(3,)
<dtype: 'int32'>

这意味着我们创建了一个整数向量(以Tensor对象的形式)。这个向量在大多数情况下可以像NumPy向量一样工作。例如,我们可以做x+x2*x ,结果就像我们所期望的那样。TensorFlow带有许多与NumPy相匹配的数组操作的函数,如tf.transposetf.concat

在TensorFlow中创建变量也是一样的,比如说。

import tensorflow as tf

x = tf.Variable([1, 2, 3])
print(x)
print(x.shape)
print(x.dtype)

这将打印。

<tf.Variable 'Variable:0' shape=(3,) dtype=int32, numpy=array([1, 2, 3], dtype=int32)>
(3,)
<dtype: 'int32'>

而我们可以应用于Tensor对象的操作(如x+x2*x )也可以应用于变量。变量和常量之间的唯一区别是前者允许数值变化,而后者是不可变的。这个区别在我们运行梯度带时很重要,如下所示。

import tensorflow as tf

x = tf.Variable(3.6)

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

dy = tape.gradient(y, x)
print(dy)

这就打印出来了。

tf.Tensor(7.2, shape=(), dtype=float32)

它的作用如下。我们定义了一个变量x (值为3.6),然后创建了一个梯度带。当梯度带工作时,我们计算y=x*xy=x2y=x^2。梯度带监控着变量的操作过程。之后,我们要求梯度带找到导数$$dfrac{dy}{dx}。我们知道。我们知道y=x^2意味着意味着y'=2x。因此,输出的值为。因此,输出的值为3.6/times 2=7.2$。

使用autograd进行多项式回归

TensorFlow中的这个功能有什么用?让我们考虑这样一种情况:我们有一个y=f(x)$形式的多项式,我们得到了几个(x,y)样本。我们怎样才能恢复样本。我们怎样才能恢复f(x)这个多项式?一种方法是假设多项式的随机系数,并输入样本这个多项式?一种方法是假设多项式的随机系数,并输入样本(x,y)。如果找到了多项式,我们应该看到$$y的值与f(x)f(x)相匹配。它们越接近,我们的估计就越接近于正确的多项式。

这的确是一个数值优化问题,比如我们要使yyf(x)f(x)之间的差值最小。我们可以使用梯度下降法来解决它。

让我们考虑一个例子。我们可以在NumPy中建立一个多项式$f(x)=x^2 + 2x + 3$$,如下所示。

import numpy as np

polynomial = np.poly1d([1, 2, 3])
print(polynomial)

这就打印出来了。

2
1 x + 2 x + 3

我们可以把多项式作为一个函数,比如。

print(polynomial(1.5))

而这将打印8.25 ,因为(1.5)2+2times(1.5)+3=8.25(1.5)^2+2\\times(1.5)+3 = 8.25

现在我们可以用NumPy从这个函数中生成一些样本。

N = 20   # number of samples

# Generate random samples roughly between -10 to +10
X = np.random.randn(N,1) * 5
Y = polynomial(X)

在上面,XY 都是形状为(20,1) 的 NumPy 数组,它们的关系是 $$y=f(x)为多项式 为多项式f(x)$。

现在假设我们不知道我们的多项式是什么,但它是二次型的。而我们想恢复系数。由于二次多项式的形式是$Ax^2+Bx+C$$,我们有三个未知数要找。我们可以使用我们实现的梯度下降算法,或者使用现有的梯度下降优化器来找到它们。下面演示了它的工作原理。

import tensorflow as tf

# Assume samples X and Y are prepared elsewhere

XX = np.hstack([X*X, X, np.ones_like(X)])

w = tf.Variable(tf.random.normal((3,1)))  # the 3 coefficients
x = tf.constant(XX, dtype=tf.float32)     # input sample
y = tf.constant(Y, dtype=tf.float32)      # output sample
optimizer = tf.keras.optimizers.Nadam(lr=0.01)
print(w)

for _ in range(1000):
    with tf.GradientTape() as tape:
        y_pred = x @ w
        mse = tf.reduce_sum(tf.square(y - y_pred))
    grad = tape.gradient(mse, w)
    optimizer.apply_gradients([(grad, w)])

print(w)

For循环之前的print 语句给出了三个随机数,如

<tf.Variable 'Variable:0' shape=(3, 1) dtype=float32, numpy=
array([[-2.1450958 ],
       [-1.1278448 ],
       [ 0.31241694]], dtype=float32)>

但是for循环之后的语句给出了与我们的多项式中非常接近的系数。

<tf.Variable 'Variable:0' shape=(3, 1) dtype=float32, numpy=
array([[1.0000628],
       [2.0002015],
       [2.996219 ]], dtype=float32)>

上述代码的作用如下。首先我们创建一个有3个值的变量向量w ,即系数$A,B,C。然后我们创建一个形状为。然后我们创建一个形状为(N,3)的数组,其中的数组,其中N是我们数组中的样本数X。这个数组有3列,分别是是我们数组中的样本数`X` 。这个数组有3列,分别是x^2x$$和1的值。我们使用np.hstack() 函数从矢量X 建立这样一个数组。同样地,我们从NumPy数组Y ,建立TensorFlow常量y

之后,我们使用for循环来运行1000次迭代的梯度下降。在每次迭代中,我们以矩阵形式计算x\\times w$$,找到Ax^2+Bx+C$,并将其分配给变量y_pred 。然后我们比较yy_pred ,并找出均方误差。接下来,我们得出梯度,即均方误差相对于系数的变化率w 。而基于这个梯度,我们使用梯度下降法来更新w

从本质上讲,上述代码是为了找到使均方误差最小的系数w

把所有东西放在一起,下面是完整的代码。

import numpy as np
import tensorflow as tf

N = 20   # number of samples

# Generate random samples roughly between -10 to +10
polynomial = np.poly1d([1, 2, 3])
X = np.random.randn(N,1) * 5
Y = polynomial(X)

# Prepare input as an array of shape (N,3)
XX = np.hstack([X*X, X, np.ones_like(X)])

# Prepare TensorFlow objects
w = tf.Variable(tf.random.normal((3,1)))  # the 3 coefficients
x = tf.constant(XX, dtype=tf.float32)     # input sample
y = tf.constant(Y, dtype=tf.float32)      # output sample
optimizer = tf.keras.optimizers.Nadam(lr=0.01)
print(w)

# Run optimizer
for _ in range(1000):
    with tf.GradientTape() as tape:
        y_pred = x @ w
        mse = tf.reduce_sum(tf.square(y - y_pred))
    grad = tape.gradient(mse, w)
    optimizer.apply_gradients([(grad, w)])

print(w)

使用autograd来解决数学难题

在上面,我们使用了20个样本,这对于拟合一个二次方程来说是绰绰有余的。我们也可以用梯度下降来解决一些数学难题。比如说,下面这个问题。

[ A ]  +  [ B ]  =  9
  +         -
[ C ]  -  [ D ]  =  1
  =         =
  8         2

换句话说,我们想找到A,B,C,DA,B,C,D的值,以便。

A + B &= 9 C - D &= 1 A + C &= 8 B - D &= 2 end{aligned}$$ 这也可以用autograd来解决,如下所示。 ``` import tensorflow as tf import random A = tf.Variable(random.random()) B = tf.Variable(random.random()) C = tf.Variable(random.random()) D = tf.Variable(random.random()) # Gradient descent loop EPOCHS = 1000 optimizer = tf.keras.optimizers.Nadam(lr=0.1) for _ in range(EPOCHS): with tf.GradientTape() as tape: y1 = A + B - 9 y2 = C - D - 1 y3 = A + C - 8 y4 = B - D - 2 sqerr = y1*y1 + y2*y2 + y3*y3 + y4*y4 gradA, gradB, gradC, gradD = tape.gradient(sqerr, [A, B, C, D]) optimizer.apply_gradients([(gradA, A), (gradB, B), (gradC, C), (gradD, D)]) print(A) print(B) print(C) print(D) ``` 这个问题可以有多种解决方案。其中一个解决方案如下。 ``` <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=4.6777573> <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=4.3222437> <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=3.3222427> <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=2.3222432> ``` 这意味着$A=4.68$,$B=4.32$,$C=3.32$,和$D=2.32$。我们可以验证这个解决方案符合问题的要求。 在上述代码中所做的是将四个未知数定义为具有随机初始值的变量。然后我们计算四个方程的结果,并将其与预期答案进行比较。然后我们把平方误差加起来,要求TensorFlow把它最小化。当我们的解决方案完全适合问题时,可能的最小平方误差为零。 注意我们要求梯度带产生梯度的方式。`A`我们要求`sqerr` 、`B` 、`C` 、`D` 。因此我们找到了四个梯度。然后我们将每个梯度应用于每个迭代中的各个变量。而不是在对`tape.gradient()` 的四个不同调用中寻找梯度,这在TensorFlow中是需要的,因为`sqerr` 的梯度默认只能被调用一次。 摘要 -- 在这篇文章中,我们展示了TensorFlow的自动分化是如何工作的。这是进行深度学习训练的构建块。具体来说,你学到了。 * 什么是TensorFlow的自动分化功能 * 我们如何使用梯度带来进行自动分化 * 我们如何使用自动分化来解决一个优化问题