栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

李沐笔记+课后习题(自动求导)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

李沐笔记+课后习题(自动求导)

向量链式法则:

自动求导:(计算一个函数在指定值上的导数)

计算图:

 

 

 自动求导的两种模式:(正向和反向)

 

 

 复杂度:

 自动求导实现:

import torch

x = torch.arange(4.0)  # 创建一个向量x并分配一个初始值
print(x)

# 在计算y关于x的梯度之前,需要一个地方来存储梯度
x.requires_grad_(True)  # 等价于x = torch.arange(4.0, requires_grad=True)
print(x.grad)  # 初始值为None

# 计算y
y = 2*torch.dot(x,x)
print(y)

# 通过调用反向传播函数来自动计算y关于x每个分量的梯度
y.backward()  # 求导
print(x.grad)

print(x.grad == 4*x)

# 计算x的另一个函数
# 在默认情况下,pytorch会累加梯度,所以需要清除之前的值
x.grad.zero_()  # 清除x的梯度值,重新赋值为0
y = x.sum()  # 向量求和导数全为1
y.backward()
print(x.grad)

# 深度学习中,目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和
# 对非标量调用`backward`需要传入一个`gradient`参数,该参数指定微分函数关于`self`的梯度。在我们的例子中,我们只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x  # x和y都是向量
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()  # 求和-标量,再求导
print(x.grad)

# 将某些计算移动到记录的计算图之外
x.grad.zero_()
y = x * x
u = y.detach()  # 把y当作一个常数而不是关于x的函数。u就是x*x的值。返回一个新的Tensor,但返回的结果是没有梯度的。
z = u * x

z.sum().backward()
print(x.grad == u)

x.grad.zero_()
y.sum().backward()  # y是关于x的函数
print(x.grad == 2 * x)

# python控制流的梯度计算
def f(a):
    b = a * 2
    while b.norm() < 1000:  # norm是L2范数
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c

a = torch.randn(size=(),requires_grad=True)  # a是随机数,size=()指a是标量
d = f(a)  # d的结果等于一个常数k乘a,k就是a.grad!
d.backward()
print(a.grad == d/a) 

 课后练习:

  1. 为什么计算二阶导数比一阶导数的开销要更大?

  2. 在运行反向传播函数之后,立即再次运行它,看看会发生什么。

  3. 在控制流的例子中,我们计算d关于a的导数,如果我们将变量a更改为随机向量或矩阵,会发生什么?此时,计算结果f(a)不再是标量。结果会发生什么?我们如何分析这个结果?

  4. 重新设计一个求控制流梯度的例子。运行并分析结果。

  5. 使f(x)=sin(x),绘制f(x)和(frac{df(x)}{dx})的图像,其中后者不使用f'(x)=cos(x)。

 

import torch
import matplotlib.pyplot as plt
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"

# 1为什么计算二阶导数比一阶导数的开销要更大?
# 二阶导数的计算是在一阶导数的基础上进行的

# 2在运行反向传播函数之后,立即再次运行它,看看会发生什么
x = torch.arange(4.0, requires_grad=True)
y = 2*torch.dot(x, x)
y.backward()
print(x.grad)
# y.backward()  # 报错,不可以连续两次backward,需要更新x.grad

# 3将a改成随机向量或矩阵会发生什么
def f(a):
    b = a * 2
    while b.norm() < 1000:  # norm是L2范数
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c

a = torch.randn(10,requires_grad=True)  # a是随机数,size=()指a是标量
d = f(a)  # d的结果等于一个常数k乘a,k就是a.grad!
d.sum().backward()  # 直接backward会报错
print(a.grad)
# print(a.grad == d/a)

# 4重新设计一个求控制流梯度的例子。运行并分析结果
def f(e):
    g = e * e
    while g < 100:  # norm是L2范数
        g = g * 2
    if g.norm() > 0:
        h = g + 100
    else:
        h = 100 * g
    return h

x = torch.randn(size=(),requires_grad=True)  # a是随机数,size=()指a是标量
y = f(x)  # d的结果等于一个常数k乘a,k就是a.grad!
y.backward()
print(x.grad == y/x)

# 5使f(x)=sin(x),绘制f(x)和(frac{df(x)}{dx})的图像,其中后者不使用f'(x)=cos(x)。
x = torch.arange(-10,10,0.1,requires_grad=True,dtype=torch.float32)
y = torch.sin(x)

y.sum().backward()

plt.plot(x.detach(),y.detach(),label='y=sin(x)')
plt.plot(x.detach(),x.grad,label='dy/dx')
plt.show()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/291018.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号