Bootstrap

深度学习3

五、自动微分

1、基础概念

        模块 autograd  负责自动计算张量操作的梯度,具有自动求导功能;autograd   创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。  

        属性 requires_grad 决定是否对张量进行梯度计算,默认不进行。

        方法 backward 进行反向传播,计算张量梯度。

        tensor.grad 返回梯度值

2、计算梯度

        元素必须为浮点数类型

2.1、标量

# 张量的梯度计算
import torch
x = torch.tensor(1.0,requires_grad = True)
y = x**2 +2*x +3
y.backward() # 梯度计算,(1,求y的导数;2、将标量带入导数函数求值)
# x.grad 表示求导带入值结果
print(x.grad)
import torch
# 多标量的梯度计算
x1 = torch.tensor(1., requires_grad=True)
x2 = torch.tensor(2., requires_grad=True)
y = x1**2 + 3*x2 +5
y.backward()
print(x1.grad)
print(x2.grad)

 2.2、向量

        损失函数接收向量后,需要进行整合称为一个元素(一半使用sum)才能进行反向传播。

反向传播后自动拆分为不同元素值的结果。

# 向量的梯度计算
import torch
x = torch.tensor([1.0,2.0,3.0],requires_grad = True)
y = x**2 +2*x +5
print(y)
y = y.sum()
print(y)
y.backward()
print(x.grad)
import torch
# 多向量的梯度计算
x1 = torch.tensor([1.,2.], requires_grad=True)
x2 = torch.tensor([2.,5.], requires_grad=True)
y = x1**2 + 3*x2 +5
y1 = y.sum()
y1.backward()
print(x1.grad)
print(x2.grad)
import torch
# 多向量的梯度计算
x1 = torch.tensor([1.,2.], requires_grad=True)
x2 = torch.tensor([2.,5.], requires_grad=True)
y = x1**2 + 3*x2 +5
y2 = y.mean()
y2.backward()
print(x1.grad)
print(x2.grad)

2.3、矩阵

# 矩阵的梯度计算
import torch
x1 = torch.tensor([[1.,2.],[3.,4.]], requires_grad=True)
y = x1**2 + 3*x1 +5
y2 = y.sum()
y2.backward()
print(x1.grad)
# 多矩阵的梯度计算
import torch
x1 = torch.tensor([[1.,2.],[3.,4.]], requires_grad=True)
x2 = torch.tensor([[11.,2.],[1.,22.]], requires_grad=True)
y = x1**2 + 3*x2 +5
y2 = y.sum()
y2.backward()
print(x1.grad)
print(x2.grad)

3、梯度控制

        由于 autograd   自动计算梯度,也就是在每个损失函数操作时都会自动运行,浪费资源,所以在无需求导的损失函数时,可以进行关闭求导功能。

3.1、全局控制

        创建tensor时,默认 requires_grad 等于 False;set_grad_enabled(False)

# 全局控制
import torch
x = torch.tensor(3.0,requires_grad = False)
y = x**2 +2*x +3
try:
    y.backward() 
    print(x.grad)
except:
    print("操作报错")

x = torch.tensor(3.0,requires_grad = True)
y = x**2 +2*x +3
torch.set_grad_enabled(False)
try:
    y.backward() 
    print(x.grad)
except:
    print("操作报错")

3.2、with进行上下文管理

        with torch.no_grad():在这个代码块内创建的损失函数,不会求导

# with 控制
import torch
x = torch.tensor(3.0,requires_grad = True)
with torch.no_grad():
    y = x**2 +2*x +3
try:
    y.backward() 
    print(x.grad)
except:
    print("操作报错")

3.3、装饰器函数控制

        将with torch.no_grad() 封装到函数,其他函数需要控制计算时候,装饰这个函数即可。

# 装饰器控制
import torch

x = torch.tensor(3.0,requires_grad = True)

def zsq(func):
    def wrapper(*args):
        with torch.no_grad():
            return func(*args)
    return wrapper

@zsq  
def fun():
    y = x**2 +2*x +3
    try:
        y.backward() 
        print(x.grad)
    except:
        print("操作报错")
    
fun() # 调用函数

4、梯度清零

        在多个损失函数反向传播或重复反向传播情况下,梯度值将累计以和的形式返回结果

# 累计梯度
import torch
# 多个损失函数反向传播
x =torch.tensor(4.0,requires_grad=True)

y = 2*x**2 +7 # 第一个损失函数
y.backward() 
print(x.grad)  # 导数结果为 16.

z = x**2  # 第二个损失函数
z.backward()
print(x.grad) # 导数结果为 8.   累加就是24.

         当进行梯度计算时,无法直观反应某次梯度的值,所以需要梯度清零:grad.zero_(),需要梯度存在后才可以使用清零,否则获取梯度为None,清零会报错,清零时将元素值变成0.,不会变成None

# 梯度清理
import torch
x =torch.tensor(4.0,requires_grad=True)
y = 2*x**2 +7
try:
    x.grad.zero_()
except:
    print("梯度为None,不能清零")
y.backward() # 反向传播
print(x.grad)

z = x**2
z.backward() 
print(x.grad)

x.grad.zero_()  # 梯度清理
print(x.grad is None)
print(x.grad)

5、梯度下降算法结合

import torch
w = torch.tensor(5., requires_grad=True) # 初始化 w

num =0

while True: 
    num+=1
    
    if num > 50: 
        break
    
    # 创建损失函数
    loss = w**2

    a=0

    # 梯度清零
    if w.grad is None: 
        pass 
    else: 
        a = w.grad.data
        w.grad.zero_()

    # 方向传播
    loss.backward()

    b = w.grad.data

    if (b>a and a!=0) or b ==0:
        break

    # 当前斜率
    print("斜率:\n",w.grad)

    w.data = w.data - 0.4*w.grad

    # 当前斜率
    print("更新的横坐标:\n",w.data)
    # 当前斜率
    print("----------",num)

;