Bootstrap

11.22 深度学习-pytorch自动微分

# 自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效

import torch

# 1. **张量**

#    Torch中一切皆为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。

# 2. **计算图**:

#    torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。

# 3. **反向传播**

#    使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。

# 4. **梯度**

#    计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。

# 张量的梯度计算 导数值

# 使用tensor.backward() 求tensor的导数值 会先求 tensor的导函数 然后带入值 这个tensor 是关于另一个tensor的表达式 原来的tensor是一组数据浮点型 这个tensor是一个函数

# 创建 原来的tensor时 要设置 requires_grad=True 表示这个tensor要被用于求导 设置了之后 tensor每次参加运算都会被求导一次 吃性能

# 调用backward 后 原来的tensor 就有个grad属性 这个属性是 导数值

# 一个 标量映射成另一个标量 对这个映射的结果做反向传播 结果在原来 标量里面

#

def demo1():

    t1= torch.tensor(7.0, requires_grad=True, dtype=torch.float32)

    t2= t1**2 + 2 * t1 + 7

    t2.backward()

    print(t1.grad)

   

# 向量的梯度计算

# 映射的结果必须是一个标量才行 对映射进行一下处理让他为标量 比如求和 或者平均值

def demo2():

    torch.manual_seed(666)

    t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)

    t2= t1**2 + 2 * t1 + 7

    t2=t2.sum()

    t2.backward()

    print(t1.grad) # 返回的是原向量每一个元素的梯度 是一个跟原来向量size一样的tensor

# 多标量梯度计算 就是映射式子里面有两个未知数 两个变量tensor 就是求了一个偏导

# 两个变量就都有了grad

def demo3():

    t1= torch.tensor(7.0, requires_grad=True, dtype=torch.float32)

    t2= torch.tensor(5.0, requires_grad=True, dtype=torch.float32)

    t3= t1**2 + 2 * t1 + 7+t2**2

    t3.backward()

    print(t1.grad)

    print(t2.grad)

# 多向量也差不多不过要先 变为标量

# 理解 一个tensor 代表一个W tensor里面代表这个W的取值情况 ,然后当这些W取值的时候 Y应该也有个对应的值组成了一个数据集 有x和y的 这样就可以 算梯度了

# y.backward()的时候 相当于把y 表达式的 导函数求出来存在y的一个属性里面 然后再把y表达式中的变量的值传进来计算导数 再返回给 变量的grad属性里面

def demo4():

    torch.manual_seed(666)

    t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)

    torch.manual_seed(3)

    t2=torch.rand(20,4, requires_grad=True, dtype=torch.float32)

    t3= t1**2 + 2 * t1 + 7+t2**2

    t3=t3.sum()

    t3.backward()

    print(t1.grad)

    print(t2.grad)

# 梯度的上下文控制

# 梯度计算的上下文控制和设置对于管理计算图、内存消耗、以及计算效率至关重要。下面我们学习下Torch中与梯度计算相关的一些主要设置方式。

# 映射函数y的requires_grad默认为ture 不用的话占性能 可以手动关了

def demo4():

    x=torch.rand(20,4, requires_grad=True, dtype=torch.float32)

    # 使用with语法 torch.no_grad() 关闭梯度 with torch.no_grad(): 局部作用 再with里面所有的torch都没有requires_grad

    with torch.no_grad():

        y = x**2 + 2 * x + 3 # 没有梯度的y

    print(y.requires_grad)  # False

    # 或者装饰器也可以

    @torch.no_grad()

    def test():

         y = x**2 + 2 * x + 3 # 没有梯度的y

         return y

    # 全局设置 设置了之后后面代码全部都没有梯度了

    torch.set_grad_enabled(False)

# 累积梯度 多次backward 的时候每次给变量返回的导数值 会累加起来 而不是覆盖 因为不管你怎么变y的表达式  都会给y里面的变量 返回个梯度 让他存起来

def demo5():

    torch.manual_seed(666)

    t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)

    for x in range(3):

        t2= t1**2 + 2 * t1 + 7

        t2=t2.sum()

        t2.backward()

        print(t1.grad)

# 梯度清空 每次backward 把梯度清空了 tensor.grad.zero_()

def demo6():

    torch.manual_seed(666)

    t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)

    for x in range(3):

        t2= t1**2 + 2 * t1 + 7

        t2=t2.sum()

        if t1.grad != None:

            t1.grad.zero_()

        t2.backward()

        print(t1.grad)

# 梯度更新 手动

def demo7():

    # 定义初始W 学习率和 训练轮次

    torch.manual_seed(666)

    w1=torch.rand(3,3,3,requires_grad=True)

    torch.manual_seed(6)

    w2=torch.rand(3,3,3,requires_grad=True)      

    print(w1,w2)

    lr=0.1

    turn=100        

    # 开始训练 假设 均方差公式

    for x in range(turn):

        y=w1**2+w2**2+2*w1+2*w2+100

        # 清空梯度

        if w1.grad!=None:

            w1.grad.zero_()

        if w2.grad!=None:

            w2.grad.zero_()

        # 计算梯度

        y=y.mean()

        y.backward()

        # 得到当前梯度

        # 梯度更新 使用tensor.data

        w1.data=w1.data-lr*w1.grad

        w2.data=w1.data-lr*w2.grad

    # 得到训练完后的 w值 可以保存 这个时候w有requires_grad=Ture 保存的时候 去掉用detach 下次使用 load出来

    print(w1,w2)

    torch.save(w1.detach(),"assets/w/w1.plt")

    torch.save(w2.detach(),"assets/w/w2.plt")

# 注意事项

# 当requires_grad=True时,在调用numpy转换为ndarray时报错

# 使用detach()方法创建张量的叶子节点即可 tensor.detach().numpy()

# detach() 就是原来   requires_grad=Flase的tensor 和原来的tensor 浅拷贝 改一个两个都变

def test():

    torch.manual_seed(666)

    w1=torch.rand(3,3,3,requires_grad=True)

    print(w1)

    w2=w1.detach()

    print(w2)

    w2[:,:,1]=100

    print(w1)

    print(w2)

   

if __name__=="__main__":

    # demo1()

    # demo2()

    # demo3()

    # demo4()

    # demo5()

    # demo6()

    # demo7()

    test()

;