zoukankan      html  css  js  c++  java
  • 动手学深度学习1- pytorch初学

    pytorch 初学

    Tensors

    Tensors 与numpy 中的ndarrays很像,pytorch可以支持GPU操作

    from __future__ import print_function
    import torch
    
    创建空的tensor
    x = torch.empty(5,3) # 5行3列的空的tensors
    print(x)
    
    tensor([[0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.]])
    
    创建随机的一个随机数矩阵
    x = torch.rand(5,3)
    print(x)
    
    tensor([[0.5109, 0.1927, 0.5499],
            [0.8677, 0.8713, 0.9610],
            [0.9356, 0.0391, 0.3159],
            [0.0266, 0.7895, 0.6610],
            [0.7188, 0.1331, 0.2180]])
    
    创建0元素的矩阵
    x = torch.zeros(5,3)
    
    print(x)
    
    tensor([[0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.],
            [0., 0., 0.]])
    
    直接从已经数据创建tensor
    x= torch.tensor([5,5,3])
    print(x)
    
    tensor([5, 5, 3])
    
    创建新的矩阵
    x = x.new_ones(5,3,dtype=torch.double)
    print(x)
    # 根据现有的张量创建张量。 这些方法将重用输入张量的属性,例如, dtype,除非设置新的值进行覆盖
    
    tensor([[1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.]], dtype=torch.float64)
    
    x = torch.randn_like(x,dtype=torch.float)
    print(x)
    # 更新了x的dtype,保留了原始的x的size
    
    tensor([[ 0.8914,  1.5704, -0.1844],
            [ 0.7747, -0.6860, -0.5596],
            [ 0.1804, -0.2909, -1.3262],
            [-1.3021, -0.4132, -2.7060],
            [ 0.8989, -0.7269,  1.3862]])
    
    print(x.size())
    
    torch.Size([5, 3])
    

    计算操作

    加法操作
    y = torch.rand(5,3)
    print(x+y)
    
    tensor([[ 1.6333,  2.1744,  0.4975],
            [ 1.5430, -0.5863, -0.1416],
            [ 0.6954,  0.6694, -0.4113],
            [-0.9279, -0.1156, -1.8519],
            [ 1.5791,  0.1524,  2.1037]])
    
    print(torch.add(x,y))
    
    tensor([[ 1.6333,  2.1744,  0.4975],
            [ 1.5430, -0.5863, -0.1416],
            [ 0.6954,  0.6694, -0.4113],
            [-0.9279, -0.1156, -1.8519],
            [ 1.5791,  0.1524,  2.1037]])
    
    result = torch.empty(5,3)
    torch.add(x,y,out=result)
    print(result)
    
    tensor([[ 1.6333,  2.1744,  0.4975],
            [ 1.5430, -0.5863, -0.1416],
            [ 0.6954,  0.6694, -0.4113],
            [-0.9279, -0.1156, -1.8519],
            [ 1.5791,  0.1524,  2.1037]])
    
    # 加法操作,inplace 操作,替换y值,类似于y+=x
    y.add_(x)
    print(y)
    
    tensor([[ 1.6333,  2.1744,  0.4975],
            [ 1.5430, -0.5863, -0.1416],
            [ 0.6954,  0.6694, -0.4113],
            [-0.9279, -0.1156, -1.8519],
            [ 1.5791,  0.1524,  2.1037]])
    
    转化形状
    x = torch.randn(4,4)
    y= x.view(16)
    z=x.view(-1,8)  # the size -1 是根据其他维度进行计算出来的
    print(x.size(),y.size(),z.size())
    
    torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
    
    torch.numel(x)  # return the number of the input tensor
    
    16
    
    tensor 与numpy 的转化
    import numpy as np
    a = np.array([1,2,3])
    t = torch.as_tensor(a)
    print(t)
    
    tensor([1, 2, 3])
    
    a = torch.ones(5)
    print(a)
    
    tensor([1., 1., 1., 1., 1.])
    
    b = a.numpy()  # 类似于a和b,a 变了,b也跟着变,类似于numpy 中的view的操作
    print(b)
    
    [1. 1. 1. 1. 1.]
    
    a.add_(1)
    print(a)
    print(b)
    
    tensor([2., 2., 2., 2., 2.])
    [2. 2. 2. 2. 2.]
    
    id(a),id(b)
    
    (4487109080, 4807132064)
    

    数据在GPU上的操作

    # 以下代码只有在PyTorch GPU版本上才会执行,配的mac没有GPU,所以没有显示结果
    if torch.cuda.is_available():
        device = torch.device("cuda")          # GPU
        y = torch.ones_like(x, device=device)  # 直接创建一个在GPU上的Tensor
        x = x.to(device)                       # 等价于 .to("cuda")
        z = x + y
        print(z)
        print(z.to("cpu", torch.double))       # to()还可以同时更改数据类型
    

    自动梯度求导

    深度学习中通常需要对函数求梯度(gradient),pytorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行方向传播过程,后续将主要介绍autograd包实现自动求梯度的有关操作

    自动求导的概念

    上节介绍的Tensor是这个包的核心类,如果将其属性 .required_grad 设置为True,将开始追踪(track)在其上的所有操作(可以利用链式法则进行梯度传播了)。计算完成后,可以调用.backward() 来完成所有的梯度计算。此Tensor的梯度将累积到.grad属性中。
    需要注意的是,如果调用y.backward()时,如果y是标量,则不需要为backward() 传入任何参数。其余情况,需要传入一个与y相同shape的Tensor。

    如果不想被继续追踪,可以调用.detach()将其追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了。此外还可以用with torch.no_grad() 将不想被追踪的操作代码块包裹起来,这样的方法在评估模型的时候常用,因为在评估模型时,不需要计算已经训练出的参数的的梯度。

    Function 类

    Function是另外一个很重要的类,Tensor和Function相互结合就可以构建一个记录有整个计算过程的有向无环图(DAG)????
    每个Tensor都有一个.grad_fn属性,该属性即创建Tensor的Function,就是说该Tensor是不是通过某些运算得到的,如果是,grad_fn返还一个与这些运算相关的对象,否则是None。

    Tensor实例
    # 创建一个Tensor并设置requires_grad=True
    x= torch.ones(2,2,requires_grad=True)
    print(x)
    print(x.grad_fn)# 返回结果为None,x是直接创建的,则说明该Tensor不是通过运算得到
    
    tensor([[1., 1.],
            [1., 1.]], requires_grad=True)
    None
    
    y =x+2
    print(y)
    print(y.grad_fn)  ## AddBackward0,y是通过一个假发操作创建的
    
    
    '''
    想x这样直接通过创建的称为叶子节点,叶子节点对应grad_fn 是None
    
    
    '''
    print(x.is_leaf,y.is_leaf)
    
    
    tensor([[3., 3.],
            [3., 3.]], grad_fn=<AddBackward0>)
    <AddBackward0 object at 0x11eb55cc0>
    True False
    
    复杂运算
    z = y*y*3
    out = z.mean()
    print(z,out)
    
    tensor([[27., 27.],
            [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward1>)
    
    """
    可以通过.requires_grad_()来用in_place的方式改变requires_grad的属性
    """
    a= torch.randn(2,2)  # 缺失的情况下默认requires_grad=False
    a =(a*3)/(a-1)
    print(a.requires_grad)   # False
    a.requires_grad_(True)
    print(a.requires_grad)    # True
    b = (a*a).sum()
    print(b.grad_fn)
    
    
    False
    True
    <SumBackward0 object at 0x11eb65780>
    
    梯度
    '''
    因为out是一个标量,所以调用backward()时不需要指定求导变量:
    
    '''
    out.backward()    # 等价于out.backward(torch.tensor(1.))
    

    我们看下out关于x的梯度

    [frac { d ( o u t ) } { d x } ]

    print(x.grad)
    
    tensor([[4.5000, 4.5000],
            [4.5000, 4.5000]])
    

    [o = frac { 1 } { 4 } sum _ { i = 1 } ^ { 4 } z _ { i } = frac { 1 } { 4 } sum _ { i = 1 } ^ { 4 } 3 left( x _ { i } + 2 ight) ^ { 2 } ]

    [left. frac { partial o } { partial x _ { i } } ight| _ { x _ { i } = 1 } = frac { 9 } { 2 } = 4.5 ]

    数学上的意义

    数学上,如果有一个函数值和自变量都为向量的函数 y=f(x)
    y=f(x), 那么 y关于x 的梯度就是一个雅可比矩阵(Jacobian matrix):

    [J = left( egin{array} { c c c } { frac { partial y 1 } { partial x _ { 1 } } } & { cdots } & { frac { partial y _ { 1 } } { partial x _ { n } } } \ { vdots } & { ddots } & { vdots } \ { frac { partial y _ { m } } { partial x _ { 1 } } } & { cdots } & { frac { partial y _ { m } } { partial x _ { n } } } end{array} ight) ]

    而torch.autograd这个包就是用来计算一些雅克比矩阵的乘积的,例如如果v是一个标量函数的L = g(y)的梯度:

    [v = left( egin{array} { c c c } { frac { partial l } { partial y 1 } } & { cdots } & { frac { partial l } { partial y m } } end{array} ight) ]

    那么根据链式法则,可以得到:L关于x的雅克比矩阵就是

    [v J = left( egin{array} { c c c } { frac { partial l } { partial y _ { 1 } } } & { cdots } & { frac { partial l } { partial y _ { m } } } end{array} ight) left( egin{array} { c c c } { frac { partial y _ { 1 } } { partial x _ { 1 } } } & { cdots } & { frac { partial y _ { 1 } } { partial x _ { n } } } \ { vdots } & { ddots } & { vdots } \ { frac { partial y _ { m } } { partial x _ { 1 } } } & { cdots } & { frac { partial y _ { m } } { partial x _ { n } } } end{array} ight) = left( egin{array} { c c c } { frac { partial l } { partial x _ { 1 } } } & { cdots } & { frac { partial l } { partial x _ { n } } } end{array} ight) ]

    注意:grad在反向传播过程中是累加的(accumulated),这意味着运行反向传播,梯度都会累加到前一次的梯度,所以一般在反正传播之前需要把梯度清零

    # 再来反向传播一次,注意grad是累加的
    out2 = x.sum()
    out2.backward()
    print(x.grad)
    
    out3 = x.sum()
    x.grad.data.zero_()   # 梯度清零,将梯度的数据变成0
    out3.backward()
    print(x.grad)
    
    
    tensor([[5.5000, 5.5000],
            [5.5000, 5.5000]])
    tensor([[1., 1.],
            [1., 1.]])
    

    现在需要解释一个问题:
    为什么在y.backward()时,如果y是标量,责不需要为backward()传入任何参数;否则需要传入一个与y同形的Tensor?

    1. 首先为了避免向量(甚至更高维张量)对张量求导,而转换成标量对张量求导;
    2. 不允许张量对张量求导,只允许标量与张量求导,求导的结果是和自变量同形的张量。这个地方说的就是不能让函数对函数求导(估计是一个意思吧)
    x = torch.tensor([1.0,2.0,3.0,4.0],requires_grad=True)
    y = 2*x
    z= y.view(2,2)
    print(z)
    
    tensor([[2., 4.],
            [6., 8.]], grad_fn=<ViewBackward>)
    

    现在的y不是一个标量,所以在调用backward()时需要传入一个和y同形的权重向量进行加权就和得到一个标量。

    v = torch.tensor([[1.0,1.0],[0.01,0.01]],dtype=torch.float)
    z.backward(v)   # 此时v就是与y同形的权重向量
    print(x.grad)   # x.grad是和x同形的张量
    
    
    tensor([2.0000, 2.0000, 0.0200, 0.0200])
    
    中断梯度追踪
    x = torch.tensor(1.0,requires_grad=True)
    y1 = x**2
    with torch.no_grad():
        y2 = x**3
    y3 = y1+y2
    print(x.requires_grad)
    print(y1,y1.requires_grad)   # 平方
    print(y2,y2.requires_grad)   # 标量,在with torch.no_grad未被追踪
    print(y3,y3.requires_grad)    # 求和
    print(y2,y2.is_leaf)  #  标量没有计算公式,y2也称为称为叶子节点,叶子节点对应grad_fn 是None
    
    
    True
    tensor(1., grad_fn=<PowBackward0>) True
    tensor(1.) False
    tensor(2., grad_fn=<AddBackward0>) True
    tensor(1.) True
    
    y3.backward()
    
    print(x.grad)
    
    tensor(2.)
    

    y3 = y1+y2 =x2+ y3,当x=1 时,dy3/dx应该是5,但是y2的定义被torch.no_grad()包裹的,
    所以与y2相关的梯度是不会被回传的,只有与y1有关的梯度才会回传,即x**2对x的梯度

    修改tensor的数字,不被autograd记录(即不会影响方向传播),可以对tensor.data 进行操作
    x = torch.ones(1,requires_grad=True)
    print(x.data) # 也是一个tensor
    print(x.data.requires_grad)  # 但是已经是独立于计算图之外
    y = 2*x
    
    x.data *=100 # 只改变了data属性值,不会记录在计算图,因此不会影响梯度传播
    y.backward()
    print(x)
    print(x.grad)
    
    tensor([1.])
    False
    tensor([100.], requires_grad=True)
    tensor([2.])
  • 相关阅读:
    Git CMD
    Guava Cache相关
    137.Single Number II---位运算---《剑指offer》40
    SpringMVC数据绑定
    StringUtils工具类的使用
    sql注入
    mapper配置文件中的动态SQL
    json相关注解和序列化与反序列化
    用户模块
    横向越权与纵向越权
  • 原文地址:https://www.cnblogs.com/onemorepoint/p/11749051.html
Copyright © 2011-2022 走看看