TensorFlow 基础(三)梯度和自动微分

news2024/12/26 12:04:37

文章目录

  • Computing gradients
  • Gradient tapes
  • Gradients with respect to a model
  • Controlling what the tape watches
  • Intermediate results
  • Gradients of non-scalar targets
  • Cases where gradients returns None
  • References


import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf

Computing gradients

要实现自动微分,TensorFlow 需要记住在前向传递(forward pass)过程中哪些运算以何种顺序发生。随后,在后向传递(backward pass)期间,TensorFlow 以相反的顺序遍历此运算列表来计算梯度。


Gradient tapes

TensorFlow 为自动微分提供了 tf.GradientTape API,即计算某个量相对于某些输入(通常是 tf.Variable)的梯度。TensorFlow 会将在 tf.GradientTape 内执行的相关运算“记录”到“条带”上,随后会使用该“条带”通过反向模式微分(reverse mode differentiation)计算梯度。

“记录”过程:

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

记录一些运算后,使用 GradientTape.gradient(target, sources) 计算某个目标(通常为损失)相对于某个源(通常为模型参数)的梯度:

dy_dx = tape.gradient(y, x)
dy_dx.numpy()
"""
6.0
"""

上面这个例子使用了一个标量,tf.GradientTape 在任何张量上都可以运行

w = tf.Variable(tf.random.normal((3, 2)), name='w')
b = tf.Variable(tf.zeros(2, dtype=tf.float32), name='b')
x = [[1., 2., 3.]]

with tf.GradientTape(persistent=True) as tape:
    y = x @ w + b
    loss = tf.reduce_mean(y**2)

要获得 loss 相对于两个变量的梯度,可以将这两个变量同时作为 gradient 方法的源传递。梯度带在关于源的传递方式上非常灵活,可以接受列表或字典的任何嵌套组合,并以相同的方式返回梯度结构。

相对于每个源的梯度具有源的形状:

[dl_dw, dl_db] = tape.gradient(loss, [w, b])
print(w.shape)
print(dl_dw.shape)
"""
(3, 2)
(3, 2)
"""

源处也可以传入变量字典:

source = {
    'w': w,
    'b': b
}

grad = tape.gradient(loss, source)
grad['b']
"""
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-0.85382605, -4.2623644 ], dtype=float32)>
"""

Gradients with respect to a model

通常会将 tf.Variables 收集到 tf.Module 或其子类之一(如 layers.Layerkeras.Model)中,用于 checkpoint 或者导出。

在大多数情况下,我们需要计算相对于模型的可训练变量的梯度。由于 tf.Module 的所有子类都在 Module.trainable_variables 属性中聚合其变量,梯度的计算也非常简单:

layer = tf.keras.layers.Dense(2, activation='relu')
x = tf.constant([[1., 2., 3.]])

with tf.GradientTape() as tape:
    y = layer(x)
    loss = tf.reduce_mean(y**2)

grad = tape.gradient(loss, layer.trainable_variables)
for var, gra in zip(layer.trainable_variables, grad):
    print(f'{var.name}, shape: {gra.shape}')
"""
dense/kernel:0, shape: (3, 2)
dense/bias:0, shape: (2,)
"""

Controlling what the tape watches

默认情况下 TensorFlow 会在访问可训练的 tf.Variable 后记录所有运算。以下示例无法计算梯度,因为默认情况下 tf.Tensor 不被 tf.GradientTape 监视,或者将 tf.Variabletrainable 属性设置为 False

# A trainable variable
x0 = tf.Variable(3.0, name='x0')
# Not trainable
x1 = tf.Variable(3.0, name='x1', trainable=False)
# Not a Variable: A variable + tensor returns a tensor.
x2 = tf.Variable(2.0, name='x2') + 1.0
# Not a variable
x3 = tf.constant(3.0, name='x3')

with tf.GradientTape() as tape:
    y = (x0**2) + (x1**2) + (x2**2)

grad = tape.gradient(y, [x0, x1, x2, x3])

for g in grad:
    print(g)
"""
tf.Tensor(6.0, shape=(), dtype=float32)
None
None
None
"""

要想记录相对于 tf.Tensor 的梯度,我们需要调用 GradientTape.watch(x)

x = tf.constant(3.0)
with tf.GradientTape() as tape:
    tape.watch(x)
    y = x ** 2

dy_dx = tape.gradient(y, x)
dy_dx.numpy()

Intermediate results

我们也可以得到在 tf.GradientTape 中计算的中间值的梯度

x = tf.constant(3.0)

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

# dz_dy = 2 * y and y = x ** 2 = 9
print(tape.gradient(z, y).numpy())

默认情况下,只要调用 GradientTape.gradient 方法,就会释放 GradientTape 保存的资源要在同一计算中计算多个梯度,可以设置 persistent=True。这样一来,当梯度带对象作为垃圾回收时,随着资源的释放,可以对 gradient 方法进行多次调用。例如:

x = tf.constant([1, 3.0])
with tf.GradientTape(persistent=True) as tape:
    tape.watch(x)
    y = x * x
    z = y * y

print(tape.gradient(z, x).numpy())  # [4.0, 108.0] (4 * x**3 at x = [1.0, 3.0])
print(tape.gradient(y, x).numpy())  # [2.0, 6.0] (2 * x at x = [1.0, 3.0])
"""
[  4. 108.]
[2. 6.]
"""

Gradients of non-scalar targets

梯度从根本上说是对标量的运算。对于计算多个目标的梯度,下面的例子中计算的是每个目标的梯度总和

x = tf.Variable(2.0)
with tf.GradientTape() as tape:
    y0 = x**2
    y1 = 1 / x

print(tape.gradient({'y0': y0, 'y1': y1}, x).numpy())
"""
3.75
"""

如果目标不是标量,则计算总和的梯度

x = tf.Variable(2.)

with tf.GradientTape() as tape:
    y = x * [3., 4.]

print(tape.gradient(y, x).numpy())
"""
7.0
"""

对每个条目都需要单独的梯度涉及到雅可比矩阵。在某些情况下,可以跳过雅可比矩阵。对于逐元素计算,总和的梯度给出了每个元素相对于其输入元素的导数,因为每个元素都是独立的

x = tf.linspace(-10.0, 10.0, 200+1)

with tf.GradientTape() as tape:
    tape.watch(x)
    y = tf.nn.sigmoid(x)

dy_dx = tape.gradient(y, x)
plt.plot(x, y, label='y')
plt.plot(x, dy_dx, label='dy/dx')
plt.legend()
_ = plt.xlabel('x')

在这里插入图片描述


Cases where gradients returns None

目标未连接到源时,gradient 将返回 None

x = tf.Variable(2.)
y = tf.Variable(3.)

with tf.GradientTape() as tape:
    z = y * y
print(tape.gradient(z, x))
"""
None
"""

我们还可以通过几种不太明显的方式将梯度断开:

  1. 使用张量替换变量
x = tf.Variable(2.0)

for epoch in range(2):
    with tf.GradientTape() as tape:
        y = x+1

    print(type(x).__name__, ":", tape.gradient(y, x))
    x = x + 1   # This should be `x.assign_add(1)`
"""
ResourceVariable : tf.Tensor(1.0, shape=(), dtype=float32)
EagerTensor : None
"""
  1. 在 TensorFlow 之外进行了计算

如果计算退出 TensorFlow, 梯度带将无法记录梯度路径:

x = tf.Variable([[1.0, 2.0],
                 [3.0, 4.0]], dtype=tf.float32)

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

    # This step is calculated with NumPy
    y = np.mean(x2, axis=0)

    # Like most ops, reduce_mean will cast the NumPy array to a constant tensor
    # using `tf.convert_to_tensor`.
    y = tf.reduce_mean(y, axis=0)

print(tape.gradient(y, x))
"""
None
"""
  1. 通过整数或字符串获取梯度

整数和字符串不可微分。如果计算路径使用这些数据类型,则不会出现梯度。

x = tf.constant(10)

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

print(tape.gradient(y, x))
"""
None
"""

References

TensorFlow 官方网站,https://tensorflow.google.cn/guide/autodiff.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【自学Python】Python三目运算符

Python三目运算符 Python三目运算符教程 Python 中没有其他语言类型的三目运算符&#xff0c;但是我们可以借助 if 语句实现类似的三目运算符。 Python三目运算符详解 说明 Python 的三目运算符是借助于 if 语句来实现的。 语法 True_statements if condition else Fals…

千锋教育嵌入式物联网教程之系统编程篇学习-02

目录 系统调用函数与库函数 库函数的组成 库函数与系统调用的关系 进程概述 进程的定义 进程与程序的区别 进程的状态及其转换 进程的调度机制 进程控制块 查看当前系统所有进程 进程号函数 进程创建fork函数 进程挂起 进程的等待 wait() waitpid 特殊进程 …

CPU 运行时的硬件环境详解

注&#xff1a;本文参考小林coding相关文章&#xff0c;侵权请联系 目录 1.图灵机的工作方式 2.冯诺依曼模型 3.内存 4.中央处理器 5总线 6.输入、输出设备 7.线路位宽与 CPU 位宽 代码写了那么多&#xff0c;你知道 a 1 2 这条代码是怎么被 CPU 执行的吗&#xff1f; …

TCP/IP协议族之TCP、UDP协议详解(小白也能看懂)

前言 在进行网络编程之前&#xff0c;我们必须要对网络通信的基础知识有个大概的框架&#xff0c;TCP/IP协议族涉及到多种网络协议&#xff0c;一般说TCP/IP协议&#xff0c;它不是指某一个具体的网络协议&#xff0c;而是一个协议族。本篇章主要针对IP协议、TCP和UDP协议记录总…

常用的代码命名方法

常见的三种命名方法1 驼峰命名法&#xff08;CamelCase&#xff09;驼峰命名法应该我们最常见的一个&#xff0c;这种命名方式使用大小写混合的格式来区别各个单词&#xff0c;并且单词之间不使用空格隔开或者连接字符连接的命名方式1 大驼峰命名法&#xff08;UpperCamelCase&…

Mysql 基础-持续更新

去重 DISTINCT DISTINCT 关键字的主要作用就是对数据表中一个或多个字段重复的数据进行过滤&#xff0c;只返回其中的一条数据给用户 注意点&#xff1a; DISTINCT 关键字只能在 SELECT 语句中使用。在对一个或多个字段去重时&#xff0c;DISTINCT 关键字必须在所有字段的最前…

IPv6路由协议实验配置(ospfv3、isis-ipv6、bgp4+)

目录 OSPFv3实验配置 建立OSPFv3邻居 AR1修改DR优先级 AR1引入直连路由 配置Stub区域 ISIS IPv6实验配置 建立ISIS邻居 修改AR1的DIS优先级 在AR1上配置路由泄露 BGP4实验配置 AR1与AR2、AR3建立IBGP邻居关系 AR2与AR4建立EBGP邻居关系 配置AR1为反射器 OSPFv3实验…

Android时间与服务器同步方案

转自&#xff1a; https://blog.csdn.net/qinci/article/details/70666631这个的吧&#xff1f;转发请注明来源吧&#xff1f;Android时间与服务器同步方法_飛舞的青春的博客-CSDN博客Android时间与服务器同步方案 在部分软件应用场景里&#xff0c;我们对应用时间要求非常苛刻…

mysql navicat函数_Navicat for MySQL函数高级属性

过程和函数是一组可以保存在服务器上的SQL语句。MySQL(www.formysql.com)函数高级属性主要涉及安全性&#xff0c;定义者&#xff0c;数据访问&#xff0c;决定性等方面的内容Navicat 函数高级属性安全性&#xff1a;指定用创建函数的用户权限来运行函数&#xff0c;或是用启用…

【C++】vector (vector的介绍及使用)

文章目录vector的介绍及使用前面我们学习了string&#xff0c;我们在学vector可以结合之前的理解&#xff0c;所以我们vector就不详细介绍了。 vector的介绍及使用 vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就…

一个没有混进大厂的普通程序员,10年真实收入变化

有人说&#xff0c;程序员的高收入和工作年限成正比&#xff0c;认为自己的薪资应该如此计算&#xff1a; private static boolean 计算工资() { //years工作时长(年)int years 5;while(years-- > 0){做项目();团建活动();涨工资();拿年终奖();}return 跳槽() &&…

2021年亚太杯APMCM数学建模大赛A题图像边缘分析与应用求解全过程文档及程序

2021年亚太杯APMCM数学建模大赛 A题 图像边缘分析与应用 原题再现&#xff1a; 随着科学技术的发展&#xff0c;对各种工件和零部件测量精度的要求越来越高&#xff0c;对测量仪器的要求也越来越高。数字图像尺寸测量仪器等各种图像测量设备目前正逐渐取代传统的手动卡尺测量…

Vue 常用内置指令

Vue 常用内置指令描述指令内容渲染指令{{}} 与 v-text覆盖面积v-text{{}}闪现问题{{}}v-textv-html属性绑定指令v-bind简写 :事件绑定指令v-on参数事件对象$event事件修饰符按键修饰符双向绑定指令v-model修饰符条件渲染指令v-show 与 v-if 的区别实现原理性能消耗举个栗子v-el…

C#构建Web服务项目实战(二)

概述本文演示了如何通过Ajax访问C# ASP.NET项目中的WebService方法&#xff08;.asmx文件形式&#xff09;。本文的项目配置参见前文&#xff1a;C#构建Web服务项目实战&#xff08;一&#xff09;。环境Visual Studio 2017 / VS2019C# ASP.NET Web服务Win11 / Win10类似.NET F…

由浅入深地学习指针(学习指针必看)

目录 指针初阶 指针定义 指针和指针类型 c语言的整型指针解引用与整型变量的区别 内存 指针和指针类型 指针类型的意义 野指针 规避野指针 指针运算 指针和数组 二级指针 指针数组 指针进阶 指针的概念再提起 字符指针 《剑指offer》 字符串常量&#xff1a; …

【基于机械臂触觉伺服的物体操控研究】UR5e运动学建模及代码实现

我的毕设题目定为《基于机械臂触觉伺服的物体操控研究》&#xff0c;这个系列主要用于记录做毕设的过程。 前言&#xff1a;UR系列是优傲公司的代表产品&#xff0c;也是目前比较通用的产品级机械臂。所以我打算用该机械臂进行毕设的仿真实现。关于其运动学建模&#xff0c;网…

【每日一题】【LeetCode】【第十二天】区域和检索 - 数组不可变

解决之路 题目描述 测试案例&#xff08;部分&#xff09; 第一次 emmm&#xff0c;说实话&#xff0c;一开始我还真没看懂题目是什么意思。。。。 自己按我自己理解的方式写了一下代码&#xff0c;用测试案例跑了下&#xff0c;成功了。 不过&#xff0c;放进去跑不通&…

VScode远程调试深度学习debug

VS Code CtrlP&#xff0c;在搜索框>select interpreter检查一下python环境 #查看GPU 环境&#xff1b;版本号 nvidia-smi.exe使用VSCode进行深度学习首先进行debug 首先要安装Remote Development个人理解可以远程打开编辑文件。 点击左下角的箭头&#xff0c;在对话框中…

async-excel整合站内信通知用户体验感满满

前面的文章我们讲过消息中心站内信的实现 【消息中心】 那么本章我们来说说异步导入导出完成后&#xff0c;如何使用消息中心站内信的功能进行通知用户业务处理完成了 在async-excel中异步逻辑处理完成后会调用一个callback方法进行回调&#xff0c;所以我们可以再对async-exc…

完全二叉树与堆(包含STL堆的用法)

完全二叉树 完全二叉树为一类特殊的二叉树&#xff0c;高度为h的完全二叉树满足如下条件&#xff1a; &#xff08;1&#xff09;所有叶结点都出现在第h或h-1层&#xff1b; &#xff08;2&#xff09;第h-1层的所有叶结点都在非叶结点的右边&#xff1b; &#xff08;3&#…