Numpy 和 Tensor

Numpy 和 Tensor

1、Numpy 与 Tensor

Tensor,它可以是零维(又称为标量或一个数)、一维、二维及多维的数组

其自称为神经网络界的 Numpy, 它与 Numpy 相似,它们共享内存,它们之间的转换非常方便和高效。

不过它们也有不同之处,最大的区别就是 Numpy 会把 ndarray 放在 CPU 中加速运算,而由 torch 产生的 tensor 会放在 GPU 中加速运算(假设当前环境有GPU)

1.1、Tensor 概述

  • 对 tensor 的操作很多,从接口的角度来划分,可以分为两类:
    1. torch.function,如 torch.sum、torch.add 等,
    2. tensor.function,如 tensor.view、tensor.add 等。
  • 如果从修改方式的角度,可以分为以下两类:
    1. 不修改自身数据,如 x.add(y),x 的数据不变,返回一个新的 tensor;
    2. 修改自身数据,如 x.add_(y)(运行符带下划线后缀),运算结果存在 x 中,x 被修改。

1.2、创建 Tensor

在这里插入图片描述

  • 注意 torch.Tensor 与 torch.tensor 的几点区别:
    1. torch.Tensor 是 torch.empty 和 torch.tensor 之间的一种混合,但是,当传入数据时,torch.Tensor 使用全局默认 dtype(FloatTensor),torch.tensor 从数据中推断数据类型;
    2. torch.tensor(1) 返回一个固定值 1,而 torch.Tensor(1) 返回一个大小为 1 的张量,它是随机初始化的值。

1.3、修改 Tensor 形状

在这里插入图片描述

  • torch.view 与 torch.reshape 的异同

    1. reshape() 可以由 torch.reshape(),也可由 torch.Tensor.reshape() 调用。view() 只可由 torch.Tensor.view() 来调用;
    2. 对于一个将要被 view 的 Tensor,新的 size 必须与原来的 size 与 stride 兼容。否则,在 view 之前必须调用contiguous() 方法;
    3. 同样也是返回与 input 数据量相同,但形状不同的 tensor。若满足 view 的条件,则不会 copy,若不满足,则会copy;
    4. 如果您只想重塑张量,请使用 torch.reshape。 如果您还关注内存使用情况并希望确保两个张量共享相同的数据,请使用 torch.view。

    1.4、索引操作

    在这里插入图片描述

1.5、广播机制

同 Numpy。

1.6、逐元素操作

这些操作均创建新的 tensor,如果需要就地操作,可以使用这些方法的下划线版本,例如 abs_。

在这里插入图片描述

1.7、归并操作

归并操作顾名思义,就是对输入进行归并或合计等操作,这类操作的输入输出形状一般不相同,而且往往是输入大于输出形状。归并操作可以对整个 tensor,也可以沿着某个维度进行归并。

在这里插入图片描述

  • 归并操作一般涉及一个 dim 参数,指定沿哪个维进行归并。另一个参数是 keepdim,说明输出结果中是否保留维度1,缺省情况是 False,即不保留

1.8、比较操作

在这里插入图片描述

1.9、矩阵操作

在这里插入图片描述

  • torch 的 dot 与 Numpy 的 dot 有点不同,torch 中 dot 对两个为 1D 张量进行点积运算,Numpy 中的 dot 无此限制
  • mm 是对 2D 的矩阵进行点积,bmm 对含 batch 的 3D 进行点积运算;
  • 转置运算会导致存储空间不连续,需要调用 contiguous 方法转为连续。

1.10、Pytorch 与 Numpy 比较

在这里插入图片描述


2、Tensor 与 Autograd

  • 在神经网络中,一个重要内容就是进行参数学习,而参数学习离不开求导,Pytorch 是如何进行求导的呢?

    torch.autograd 包就是用来自动求导的:

    autograd 包为张量上所有的操作提供了自动求导功能而 torch.Tensor 和 torch.Function 为 autograd 上的两个核心类,他们相互连接并生成一个有向非循环图

2.1、自动求导要点

  1. 创建叶子节点(leaf node)的 tensor使用 requires_grad 参数指定是否记录对其的操作,以便之后利用backward() 方法进行梯度求解requires_grad 参数缺省值为 False,如果要对其求导需设置为 True,与之有依赖关系的节点自动变为 True
  2. 可利用 requires_grad_() 方法修改 tensor 的 requires_grad 属性可以调用 .detach() 或 with torch.no_grad():将不再计算张量的梯度,跟踪张量的历史记录。这点在评估模型、测试模型阶段常常使用。
  3. 通过运算创建的 tensor(即非叶子节点会自动被赋于 grad_fn 属性该属性表示梯度函数叶子节点的 grad_fn为 None
  4. 最后得到的 tensor 执行 backward() 函数,此时自动计算各变量的梯度,并将累加结果保存到各自的 grad 属性中计算完成后,非叶子节点的梯度自动释放
  5. backward() 函数接受参数,该参数应和调用 backward() 函数的 Tensor 的维度相同,或者是可以 broadcast 的维度如果求导的 tensor 为标量(即一个数字),backward 中参数可省略
  6. 反向传播的中间缓存会被清空,如果需要进行多次反向传播,需要指定 backward 中的参数 retain_graph=True。多次反向传播时,梯度是累加的
  7. 非叶子节点的梯度 backward 调用后即被清空
  8. 可以通过用 torch.no_grad() 包裹代码块来阻止 autograd 去跟踪那些标记为 .requesgrad=True 的张量的历史记录。这步在测试阶段经常使用。

整个过程中,Pytorch 采用计算图的形式进行组织,该计算图为动态图,它的计算图在每次前向传播时,将重新构建

2.2、计算图

  • 计算图是一种有向无环图像用图形方式表示算子与变量之间的关系,直观高效。如下图所示,圆形表示变量,矩阵表示算子

    如表达式:z = wx + b,可写成两个表示式:y = wx,则 z = y + b,

    1. 其中 x、w、b 为变量,是用户创建的变量,不依赖于其他变量,故又称为叶子节点

      为计算各叶子节点的梯度,需要把对应的张量参数 requires_grad 属性设置为True,这样就可自动跟踪其历史记录。

    2. y、z 是计算得到的变量,非叶子节点,z 为根节点

    3. mul 和 add 是算子(或操作或函数)。

    由这些变量及算子,就构成一个完整的计算过程(或前向传播过程)

在这里插入图片描述

我们的目标是更新各叶子节点的梯度,根据复合函数导数的链式法则,不难算出各叶子节点的梯度。
∂ z ∂ x = ∂ z ∂ y ∂ y ∂ x = w \frac{\partial \mathrm{z}}{\partial \mathrm{x}}=\frac{\partial \mathrm{z}}{\partial \mathrm{y}} \frac{\partial \mathrm{y}}{\partial \mathrm{x}}=\mathrm{w} xz=yzxy=w

∂ z ∂ w = ∂ z ∂ y ∂ y ∂ w = x \frac{\partial \mathrm{z}}{\partial \mathrm{w}}=\frac{\partial \mathrm{z}}{\partial \mathrm{y}} \frac{\partial \mathrm{y}}{\partial \mathrm{w}}=\mathrm{x} wz=yzwy=x

∂ z ∂ b = 1 \frac{\partial \mathrm{z}}{\partial \mathrm{b}}=1 bz=1

Pytorch 调用 backward(),将自动计算各节点的梯度,这是一个反向传播过程,这个过程可用下图表示。

在反向传播过程中,autograd 沿着上图,从当前根节点 z 反向溯源,利用导数链式法则,计算所有叶子节点的梯度,其梯度值将累加到 grad 属性中对非叶子节点的计算操作(或 function)记录在 grad_fn 属性中,叶子节点的 grad_fn 值为 None

在这里插入图片描述


3、使用 Numpy 实现机器学习

  • 首先,我们用最原始的 Numpy 实现有关回归的一个机器学习任务,不用 Pytorch 中的包或类。这种方法代码可能多一点,但每一步都是透明的,有利于理解每步的工作原理。

    主要步骤包括:

    1. 首先,是给出一个数组 x,然后基于表达式:y=3x^2+2,加上一些噪音数据到达另一组数据 y;
    2. 然后,构建一个机器学习模型,学习表达式 y=wx^2+b 的两个参数 w,b。利用数组 x,y 的数据为训练数据;
    3. 最后,采用梯度梯度下降法,通过多次迭代,学习到 w、b 的值。
  1. 导入需要的库

    import numpy as np
     
    from matplotlib import pyplot as plt
    
  2. 生成输入数据 x 及目标数据 y

    设置随机数种子,生成同一个份数据,以便用多种方法进行比较:

    np.random.seed(100) 
    x = np.linspace(-1, 1, 100).reshape(100,1) 
    y = 3*np.power(x, 2) +2+ 0.2*np.random.rand(x.size).reshape(100,1) 
    
  3. 查看 x,y 数据分布情况

    # 画图
    plt.scatter(x, y)
    plt.show()
    
  4. 初始化权重参数

    # 随机初始化参数
    w1 = np.random.rand(1,1)
    b1 = np.random.rand(1,1) 
    
  5. 训练模型

    定义损失函数,假设批量大小为 100:
    L o s s = 1 2 ∑ i = 1 100 ( w x i 2 + b − y i ) 2 Loss=\frac{1}{2}\sum^{100}_{i=1}{(wx_i^2+b-y_i)^2} Loss=21i=1100(wxi2+byi)2
    对损失函数求导:
    ∂ L o s s ∂ w = ∑ i = 1 100 ( w x i 2 + b − y i ) x i 2 \frac{\partial{Loss}}{\partial{w}}=\sum^{100}_{i=1}{(wx_i^2+b-y_i)x_i^2} wLoss=i=1100(wxi2+byi)xi2

    ∂ L o s s ∂ b = ∑ i = 1 100 ( w x i 2 + b − y i ) \frac{\partial{Loss}}{\partial{b}}=\sum^{100}_{i=1}{(wx_i^2+b-y_i)} bLoss=i=1100(wxi2+byi)

    利用梯度下降法学习参数,学习率为 l r lr lr
    w 1 − = l r ∗ ∂ L o s s ∂ w w_1-=lr*\frac{\partial{Loss}}{\partial{w}} w1=lrwLoss

    b 1 − = l r ∗ ∂ L o s s ∂ b b_1-=lr*\frac{\partial{Loss}}{\partial{b}} b1=lrbLoss

    lr =0.001 # 学习率
     
    for i in range(800):
        # 前向传播
        y_pred = np.power(x,2)*w1 + b1
        # 定义损失函数
        loss = 0.5 * (y_pred - y) ** 2
        loss = loss.sum()
        #计算梯度
        grad_w=np.sum((y_pred - y)*np.power(x,2))
        grad_b=np.sum((y_pred - y))
        #使用梯度下降法,是loss最小
        w1 -= lr * grad_w
        b1 -= lr * grad_b
    
  6. 可视化结果

    plt.plot(x, y_pred,'r-',label='predict')
    plt.scatter(x, y,color='blue',marker='o',label='true') # true data
    plt.xlim(-1,1)
    plt.ylim(2,6)  
    plt.legend()
    plt.show()
    print(w1,b1)
    

4、使用 Tensor 及 antograd 实现机器学习

使用 Pytorch 的自动求导的一个包 antograd,利用这个包及对应的 Tensor,便可利用自动反向传播来求梯度,无需手工计算梯度。

  1. 导入需要的库

    import torch as t
    
    from matplotlib import pyplot as plt
    
  2. 生成训练数据,并可视化数据分布情况

    t.manual_seed(100) 
    dtype = t.float
    #生成x坐标数据,x为tenor,需要把x的形状转换为100x1
    x = t.unsqueeze(t.linspace(-1, 1, 100), dim=1) 
    #生成y坐标数据,y为tenor,形状为100x1,另加上一些噪音
    y = 3*x.pow(2) +2+ 0.2*t.rand(x.size())                 
     
    # 画图,把tensor数据转换为numpy数据
    plt.scatter(x.numpy(), y.numpy())
    plt.show()
    
  3. 初始化权重参数

    # 随机初始化参数,参数w,b为需要学习的,故需requires_grad=True
    w = t.randn(1,1, dtype=dtype,requires_grad=True)
    b = t.zeros(1,1, dtype=dtype, requires_grad=True)
    
  4. 训练模型

    lr =0.001 # 学习率
     
    for ii in range(800):
        # 前向传播,并定义损失函数loss
        y_pred = x.pow(2).mm(w) + b
        loss = 0.5 * (y_pred - y) ** 2
        loss = loss.sum()
        
        # 自动计算梯度,梯度存放在grad属性中
        loss.backward()
        
        # 手动更新参数,需要用torch.no_grad(),使上下文环境中切断自动求导的计算
        with t.no_grad():
            w -= lr * w.grad
            b -= lr * b.grad
        
        # 梯度清零
            w.grad.zero_()
            b.grad.zero_()
    
  5. 可视化训练结果

    plt.plot(x.numpy(), y_pred.detach().numpy(),'r-',label='predict')#predict
    plt.scatter(x.numpy(), y.numpy(),color='blue',marker='o',label='true') # true data
    plt.xlim(-1,1)
    plt.ylim(2,6)  
    plt.legend()
    plt.show()
            
    print(w, b)
    
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值