zoukankan      html  css  js  c++  java
  • [PyTorch 学习笔记] 1.3 张量操作与线性回归

    本章代码:https://github.com/zhangxiann/PyTorch_Practice/blob/master/lesson1/linear_regression.py

    张量的操作

    拼接

    torch.cat()

    torch.cat(tensors, dim=0, out=None)
    

    功能:将张量按照 dim 维度进行拼接

    • tensors: 张量序列
    • dim: 要拼接的维度

    代码示例:

    t = torch.ones((2, 3))
    t_0 = torch.cat([t, t], dim=0)
    t_1 = torch.cat([t, t], dim=1)
    print("t_0:{} shape:{}
    t_1:{} shape:{}".format(t_0, t_0.shape, t_1, t_1.shape))
    

    输出是:

    t_0:tensor([[1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.]]) shape:torch.Size([4, 3])
    t_1:tensor([[1., 1., 1., 1., 1., 1.],
            [1., 1., 1., 1., 1., 1.]]) shape:torch.Size([2, 6])
    

    torch.stack()

    torch.stack(tensors, dim=0, out=None)
    

    功能:将张量在新创建的 dim 维度上进行拼接

    • tensors: 张量序列
    • dim: 要拼接的维度

    代码示例:

    t = torch.ones((2, 3))
    # dim =2
    t_stack = torch.stack([t, t, t], dim=2)
    print("
    t_stack.shape:{}".format(t_stack.shape))
    # dim =0
    t_stack = torch.stack([t, t, t], dim=0)
    print("
    t_stack.shape:{}".format(t_stack.shape))
    

    输出为:

    t_stack.shape:torch.Size([2, 3, 3])
    t_stack.shape:torch.Size([3, 2, 3])
    

    第一次指定拼接的维度 dim =2,结果的维度是 [2, 3, 3]。后面指定拼接的维度 dim =0,由于原来的 tensor 已经有了维度 0,因此会把tensor 往后移动一个维度变为 [1,2,3],再拼接变为 [3,2,3]。

    切分

    torch.chunk()

    torch.chunk(input, chunks, dim=0)
    

    功能:将张量按照维度 dim 进行平均切分。若不能整除,则最后一份张量小于其他张量。

    • input: 要切分的张量
    • chunks: 要切分的份数
    • dim: 要切分的维度

    代码示例:

    a = torch.ones((2, 7))  # 7
    list_of_tensors = torch.chunk(a, dim=1, chunks=3)   # 3
    for idx, t in enumerate(list_of_tensors):
    print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))
    
    

    输出为:

    第1个张量:tensor([[1., 1., 1.],
            [1., 1., 1.]]), shape is torch.Size([2, 3])
    第2个张量:tensor([[1., 1., 1.],
            [1., 1., 1.]]), shape is torch.Size([2, 3])
    第3个张量:tensor([[1.],
            [1.]]), shape is torch.Size([2, 1])
    

    由于 7 不能整除 3,7/3 再向上取整是 3,因此前两个维度是 [2, 3],所以最后一个切分的张量维度是 [2,1]。

    torch.split()

    torch.split(tensor, split_size_or_sections, dim=0)
    

    功能:将张量按照维度 dim 进行平均切分。可以指定每一个分量的切分长度。

    • tensor: 要切分的张量
    • split_size_or_sections: 为 int 时,表示每一份的长度,如果不能被整除,则最后一份张量小于其他张量;为 list 时,按照 list 元素作为每一个分量的长度切分。如果 list 元素之和不等于切分维度 (dim) 的值,就会报错。
    • dim: 要切分的维度

    代码示例:

    t = torch.ones((2, 5))
    list_of_tensors = torch.split(t, [2, 1, 2], dim=1)
    for idx, t in enumerate(list_of_tensors):
    print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))
    

    结果为:

    第1个张量:tensor([[1., 1.],
            [1., 1.]]), shape is torch.Size([2, 2])
    第2个张量:tensor([[1.],
            [1.]]), shape is torch.Size([2, 1])
    第3个张量:tensor([[1., 1.],
            [1., 1.]]), shape is torch.Size([2, 2])
    

    索引

    torch.index_select()

    torch.index_select(input, dim, index, out=None)
    

    功能:在维度 dim 上,按照 index 索引取出数据拼接为张量返回。

    • input: 要索引的张量
    • dim: 要索引的维度
    • index: 要索引数据的序号

    代码示例:

    # 创建均匀分布
    t = torch.randint(0, 9, size=(3, 3))
    # 注意 idx 的 dtype 不能指定为 torch.float
    idx = torch.tensor([0, 2], dtype=torch.long)
    # 取出第 0 行和第 2 行
    t_select = torch.index_select(t, dim=0, index=idx)
    print("t:
    {}
    t_select:
    {}".format(t, t_select))
    

    输出为:

    t:
    tensor([[4, 5, 0],
            [5, 7, 1],
            [2, 5, 8]])
    t_select:
    tensor([[4, 5, 0],
            [2, 5, 8]])
    

    torch.mask_select()

    torch.masked_select(input, mask, out=None)
    

    功能:按照 mask 中的 True 进行索引拼接得到一维张量返回。

    • 要索引的张量
    • mask: 与 input 同形状的布尔类型张量

    代码示例:

    t = torch.randint(0, 9, size=(3, 3))
    mask = t.le(5)  # ge is mean greater than or equal/   gt: greater than  le  lt
    # 取出大于 5 的数
    t_select = torch.masked_select(t, mask)
    print("t:
    {}
    mask:
    {}
    t_select:
    {} ".format(t, mask, t_select))
    

    结果为:

    t:
    tensor([[4, 5, 0],
            [5, 7, 1],
            [2, 5, 8]])
    mask:
    tensor([[ True,  True,  True],
            [ True, False,  True],
            [ True,  True, False]])
    t_select:
    tensor([4, 5, 0, 5, 1, 2, 5]) 
    

    最后返回的是一维张量。

    变换

    torch.reshape()

    torch.reshape(input, shape)
    

    功能:变换张量的形状。当张量在内存中是连续时,返回的张量和原来的张量共享数据内存,改变一个变量时,另一个变量也会被改变。

    • input: 要变换的张量
    • shape: 新张量的形状

    代码示例:

    # 生成 0 到 8 的随机排列
    t = torch.randperm(8)
    # -1 表示这个维度是根据其他维度计算得出的
    t_reshape = torch.reshape(t, (-1, 2, 2))
    print("t:{}
    t_reshape:
    {}".format(t, t_reshape))
    

    结果为:

    t:tensor([5, 4, 2, 6, 7, 3, 1, 0])
    t_reshape:
    tensor([[[5, 4],
             [2, 6]],
    
            [[7, 3],
             [1, 0]]])
    

    在上面代码的基础上,修改原来的张量的一个元素,新张量也会被改变。

    代码示例:

    # 修改张量 t 的第 0 个元素,张量 t_reshape 也会被改变
    t[0] = 1024
    print("t:{}
    t_reshape:
    {}".format(t, t_reshape))
    print("t.data 内存地址:{}".format(id(t.data)))
    print("t_reshape.data 内存地址:{}".format(id(t_reshape.data)))
    

    结果为:

    t:tensor([1024,    4,    2,    6,    7,    3,    1,    0])
    t_reshape:
    tensor([[[1024,    4],
             [   2,    6]],
    
            [[   7,    3],
             [   1,    0]]])
    t.data 内存地址:2636803119936
    t_reshape.data 内存地址:2636803119792
    

    torch.transpose()

    torch.transpose(input, dim0, dim1)
    

    功能:交换张量的两个维度。常用于图像的变换,比如把c*h*w 变换为h*w*c

    • input: 要交换的变量
    • dim0: 要交换的第一个维度
    • dim1: 要交换的第二个维度

    代码示例:

    #把 c * h * w 变换为 h * w * c
    t = torch.rand((2, 3, 4))
    t_transpose = torch.transpose(t, dim0=1, dim1=2)    # c*h*w     h*w*c
    print("t shape:{}
    t_transpose shape: {}".format(t.shape, t_transpose.shape))
    
    

    结果为:

    t shape:torch.Size([2, 3, 4])
    t_transpose shape: torch.Size([2, 4, 3])
    

    torch.t()

    功能:2 维张量转置,对于 2 维矩阵而言,等价于torch.transpose(input, 0, 1)

    torch.squeeze()

    torch.squeeze(input, dim=None, out=None)
    

    功能:压缩长度为 1 的维度。

    • dim: 若为 None,则移除所有长度为 1 的维度;若指定维度,则当且仅当该维度长度为 1 时可以移除。

    代码示例:

        # 维度 0 和 3 的长度是 1
        t = torch.rand((1, 2, 3, 1))
        # 可以移除维度 0 和 3
        t_sq = torch.squeeze(t)
        # 可以移除维度 0
        t_0 = torch.squeeze(t, dim=0)
        # 不能移除 1
        t_1 = torch.squeeze(t, dim=1)
        print("t.shape: {}".format(t.shape))
        print("t_sq.shape: {}".format(t_sq.shape))
        print("t_0.shape: {}".format(t_0.shape))
        print("t_1.shape: {}".format(t_1.shape))
    

    结果为:

    t.shape: torch.Size([1, 2, 3, 1])
    t_sq.shape: torch.Size([2, 3])
    t_0.shape: torch.Size([2, 3, 1])
    t_1.shape: torch.Size([1, 2, 3, 1])
    

    torch.unsqueeze()

    torch.unsqueeze(input, dim)
    

    功能:根据 dim 扩展维度,长度为 1。

    张量的数学运算

    主要分为 3 类:加减乘除,对数,指数,幂函数 和三角函数。

    这里介绍一下常用的几种方法。

    torch.add()

    torch.add(input, other, out=None)
    torch.add(input, other, *, alpha=1, out=None)
    

    功能:逐元素计算 input + alpha * other。因为在深度学习中经常用到先乘后加的操作。

    • input: 第一个张量
    • alpha: 乘项因子
    • other: 第二个张量

    torch.addcdiv()

    torch.addcdiv(input, tensor1, tensor2, *, value=1, out=None)
    

    计算公式为:out ${i}=operatorname{input}{i}+$ value $ imes frac{ ext { tensor } 1_{i}}{ ext { tensor } 2_{i}}$

    torch.addcmul()

    torch.addcmul(input, tensor1, tensor2, *, value=1, out=None)
    

    计算公式为:out ${i}=$ input ${i}+$ value $ imes$ tensor $1_{i} imes$ tensor $2_{i}$

    线性回归

    线性回归是分析一个变量 ($y$) 与另外一 (多) 个变量 ($x$) 之间的关系的方法。一般可以写成 $y=wx+b$。线性回归的目的就是求解参数$w, b$。

    线性回归的求解可以分为 3 步:

    1. 确定模型:$y=wx+b$
    2. 选择损失函数,一般使用均方误差 MSE:$frac{1}{m} sum_{i=1}{m}left(y_{i}-hat{y}_{i} ight){2}$。其中 $ hat{y}_{i} $ 是预测值,$y$ 是真实值。
    3. 使用梯度下降法求解梯度 (其中 $lr$ 是学习率),并更新参数:
      • $w = w - lr * w.grad$
      • $b = b - lr * b.grad$

    代码如下:

    import torch
    import matplotlib.pyplot as plt
    torch.manual_seed(10)
    
    lr = 0.05  # 学习率
    
    # 创建训练数据
    x = torch.rand(20, 1) * 10  # x data (tensor), shape=(20, 1)
    # torch.randn(20, 1) 用于添加噪声
    y = 2*x + (5 + torch.randn(20, 1))  # y data (tensor), shape=(20, 1)
    
    # 构建线性回归参数
    w = torch.randn((1), requires_grad=True) # 设置梯度求解为 true
    b = torch.zeros((1), requires_grad=True) # 设置梯度求解为 true
    
    # 迭代训练 1000 次
    for iteration in range(1000):
    
        # 前向传播,计算预测值
        wx = torch.mul(w, x)
        y_pred = torch.add(wx, b)
    
        # 计算 MSE loss
        loss = (0.5 * (y - y_pred) ** 2).mean()
    
        # 反向传播
        loss.backward()
    
        # 更新参数
        b.data.sub_(lr * b.grad)
        w.data.sub_(lr * w.grad)
    
        # 每次更新参数之后,都要清零张量的梯度
        w.grad.zero_()
        b.grad.zero_()
    
        # 绘图,每隔 20 次重新绘制直线
        if iteration % 20 == 0:
    
            plt.scatter(x.data.numpy(), y.data.numpy())
            plt.plot(x.data.numpy(), y_pred.data.numpy(), 'r-', lw=5)
            plt.text(2, 20, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color':  'red'})
            plt.xlim(1.5, 10)
            plt.ylim(8, 28)
            plt.title("Iteration: {}
    w: {} b: {}".format(iteration, w.data.numpy(), b.data.numpy()))
            plt.pause(0.5)
    
            # 如果 MSE 小于 1,则停止训练
            if loss.data.numpy() < 1:
                break
    

    训练的直线的可视化如下:


    在 80 次的时候,Loss 已经小于 1 了,因此停止了训练。

    参考资料


    如果你觉得这篇文章对你有帮助,不妨点个赞,让我有更多动力写出好文章。

    我的文章会首发在公众号上,欢迎扫码关注我的公众号张贤同学


  • 相关阅读:
    9.11 eventbus
    9.10,,,实现new instanceof apply call 高阶函数,偏函数,柯里化
    9.9 promise实现 写完了传到gitee上面了,这里这个不完整
    9.5cors配置代码
    9.5 jsonp 实现
    9.5 http tcp https总结
    9.3 es6 class一部分 and es5 class 发布订阅
    8.30 cookie session token jwt
    8.30vue响应式原理
    warning: LF will be replaced by CRLF in renard-wx/project.config.json. The file will have its original line endings in your working directory
  • 原文地址:https://www.cnblogs.com/zhangxiann/p/13529604.html
Copyright © 2011-2022 走看看