自动求导

https://zh.d2l.ai/chapter_preliminaries/autograd.html

x = tf.range(-4, 4, 0.01)

with tf.GradientTape() as t:
    t.watch(x)
    y = tf.sin(x)
    dy_dx = t.gradient(y, x)
    
plot(x, [y, dy_dx], 'x', 'y', legend=['y=sin(x)', 'y=sin\'(x)'])

问题5需要用到t.watch(x)这个函数,不然dy_dx是个None。而且with tf.GradientTape() as t:下面这三句的顺序是不能改的,我理解一下大概是t要watch每个来的x,watch它是怎么得到y的,然后才能得到这个y对这个x的梯度。
没学过python不太懂with,之前用来维持打开文件的引用,又看到这里有个tape,前面提到说是什么磁带,所以python大概给了一个磁带的抽象,tf.GradientTape()就是TensorFlow给的一个磁带专门用来画计算图(计算图又只用来传播梯度)的,画出一个计算图只能求一次梯度。
下面绘曲线图的时候并不是每给个x然后现求函数值才得到点的(x, y),而是在with里就把函数值全都求出来了。实际上确实是只求了一次梯度,只是一下子把需要的都求出来了。
然后加上persistent=True还能把dy_dx = t.gradient(y, x)提到with外面。

x = tf.range(-4, 4, 0.01)

with tf.GradientTape(persistent=True) as t:
    t.watch(x)
    y = tf.sin(x)
dy_dx = t.gradient(y, x)
    
plot(x, [y, dy_dx], 'x', 'y', legend=['y=sin(x)', 'y=sin\'(x)'])

如果加上persistent=True而不提到外面,会警告你说效率低。可能加上是多此一举了吧。
主要问题还是这个watch(),菜鸟教程上也没有,胡乱百度我也不知道我是从哪看来这么一句了。

是不是忘记加 x = tf.Variable(x) 存储梯度值了?

第 5 题踩的坑:

  1. 如果使用 pycharm 编程,需要在 plot 函数最后加一句 plt.show()
  2. #@save:在第一次运行上一节中带 #@save 的 plot 函数时,我没有加上
    plt.show(),之后重新运行之后,在 d2l.tensorflow源代码中的 plot 函数未改动,导致在这一节调画图的时候一直不出图,后来查看了源代码才知道原来少了 plt.show() 这句话。。好像也说明了 #@save 只能保存第一次运行的代码到 d2l 中,至于怎么保存更新后的代码,,我也不到。。
  3. 算出来的梯度 dy_dx, y, x 都需要转成 numpy 的 array,然后再调用 d2l.plot(…)。

代码:

import tensorflow as tf
import numpy as np
from d2l import tensorflow as d2l

x = tf.range(-4, 4, 0.01)
x = tf.Variable(x) # 存储梯度值

with tf.GradientTape() as t:
    y = tf.sin(x)
dy_dx = t.gradient(y, x)

x = np.array(x)
y = np.array(y)
dy_dx = np.array(dy_dx)

d2l.plot(x, [y, dy_dx], 'x', 'y', legend=['y=sin(x)', 'y=sin\'(x)'])

最后出的图:
image