zoukankan      html  css  js  c++  java
  • Pytorch学习-数据操作

    2.2 数据操作

    tensor一般可译作"张量",张量可以看作是一个多维数组,标量可以看作0维张量,向量可以看作1维张量,矩阵可以看作是二维张量。

    2.2.1 创建Tensor

    导入PyTorch

    import torch
    

    创建5×3的未初始化的Tensor:

    x = torch.empty(5,3)
    print(x)
    
    tensor([[-5.9280e-17,  1.0328e-42, -5.9279e-17],
            [ 1.0328e-42, -5.9299e-17,  1.0328e-42],
            [-5.9283e-17,  1.0328e-42, -5.9304e-17],
            [ 1.0328e-42, -5.9293e-17,  1.0328e-42],
            [-5.9293e-17,  1.0328e-42, -5.9287e-17]])
    

    创建一个5×3的随机初始化的Tensor

    x = torch.rand(5,3)
    print(x)
    
    tensor([[0.9341, 0.5139, 0.7338],
            [0.8216, 0.9009, 0.5679],
            [0.7110, 0.5658, 0.8897],
            [0.5174, 0.1536, 0.6354],
            [0.6097, 0.7780, 0.3942]])
    

    创建一个5×3的long型全0的Tensor

    x = torch.zeros(5,3,dtype = torch.long)
    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.5000, 3.0000])
    

    通过现有的Tensor来创建,此方法会默认重用输入Tensor的一些属性,例如数据类型,除非自定义数据类型。

    x = x.new_ones(5,3,dtype = torch.float64) #返回的tensor默认具有相同的torch.dtype和torch.device
    print(x)
    
    x = torch.randn_like(x,dtype = torch.float) #指定新的数据类型
    print(x)
    
    tensor([[1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.],
            [1., 1., 1.]], dtype=torch.float64)
    tensor([[-0.1907, -0.1420, -0.6396],
            [-0.9567,  0.4238, -0.6407],
            [ 0.3809,  1.2904,  2.0543],
            [-1.7290, -0.6046, -2.2271],
            [ 3.0284, -0.9338, -0.7631]])
    

    通过shape或者size()来获取Tensor的形状:

    print(x.size())
    print(x.shape)
    
    torch.Size([5, 3])
    torch.Size([5, 3])
    

    注:返回的size是一个tuple,支持所有tuple的操作

    更多构造Tensor的函数

    函数 功能
    Tensor(sizes) 基础构造函数
    tensor(data) 类似np.array的构造函数
    ones(sizes) 全1Tensor
    zeros(size) 全0Tensor
    eye(size) 对角线为1,其它为0
    arange(s,e,step) 从s到e,步长为step
    linspace(s,e,steps) 从s到e,均匀切分成steps份
    rand/randn(sizes) 均匀/标准分布
    normal(mean,std)/uniform(from,to) 正态分布/均匀分布
    randperm(m) 随机排列

    这些创建方法都可以在创建的时候指定数据类型dtype和存放device(cpu/gpu)

    2.2.2 操作

    介绍Tensor的各种操作

    算术操作

    在PyTorch中,同一种操作可能有很多种形式,下面用加法作为例子。

    #加法形式一
    y = torch.rand(5,3)
    print(x)
    print(x+y)
    
    tensor([[-0.1907, -0.1420, -0.6396],
            [-0.9567,  0.4238, -0.6407],
            [ 0.3809,  1.2904,  2.0543],
            [-1.7290, -0.6046, -2.2271],
            [ 3.0284, -0.9338, -0.7631]])
    tensor([[ 0.2407,  0.4575,  0.2434],
            [-0.7171,  0.7032,  0.2253],
            [ 0.8284,  2.2123,  2.3685],
            [-0.8832,  0.1701, -1.6400],
            [ 3.5063, -0.2672, -0.0373]])
    
    #加法形式二
    print(torch.add(x,y))
    
    tensor([[ 0.2407,  0.4575,  0.2434],
            [-0.7171,  0.7032,  0.2253],
            [ 0.8284,  2.2123,  2.3685],
            [-0.8832,  0.1701, -1.6400],
            [ 3.5063, -0.2672, -0.0373]])
    
    #指定输出
    result = torch.empty(5,3)
    print(result)
    torch.add(x,y,out=result)
    print(result)
    
    tensor([[4.1327e-39, 8.9082e-39, 9.8265e-39],
            [9.4592e-39, 1.0561e-38, 1.0929e-38],
            [1.0102e-38, 4.5918e-39, 1.0561e-38],
            [1.0561e-38, 1.0561e-38, 1.0745e-38],
            [1.0561e-38, 8.7245e-39, 9.6429e-39]])
    tensor([[ 0.2407,  0.4575,  0.2434],
            [-0.7171,  0.7032,  0.2253],
            [ 0.8284,  2.2123,  2.3685],
            [-0.8832,  0.1701, -1.6400],
            [ 3.5063, -0.2672, -0.0373]])
    
    # 加法形式三、inplace
    # adds x to y
    print(y)
    y.add_(x)
    print(y)
    
    tensor([[0.4314, 0.5995, 0.8830],
            [0.2396, 0.2793, 0.8660],
            [0.4475, 0.9219, 0.3143],
            [0.8458, 0.7747, 0.5871],
            [0.4779, 0.6666, 0.7258]])
    tensor([[ 0.2407,  0.4575,  0.2434],
            [-0.7171,  0.7032,  0.2253],
            [ 0.8284,  2.2123,  2.3685],
            [-0.8832,  0.1701, -1.6400],
            [ 3.5063, -0.2672, -0.0373]])
    

    索引

    使用索引访问Tensor的一部分

    注:索引出来的结果与原数据共享内存,修改一个,另一个也会跟着修改。

    y = x[0, : ]
    y += 1
    print(x)
    print(y)
    print(x[0,:]) #源tensor也被修改
    
    tensor([[ 2.8093,  2.8580,  2.3604],
            [-0.9567,  0.4238, -0.6407],
            [ 0.3809,  1.2904,  2.0543],
            [-1.7290, -0.6046, -2.2271],
            [ 3.0284, -0.9338, -0.7631]])
    tensor([2.8093, 2.8580, 2.3604])
    tensor([2.8093, 2.8580, 2.3604])
    

    更高级的选择数据函数:

    image

    改变形状

    view()来改变Tensor的形状

    y = x.view(15)
    z = x.view(-1,5) # -1所指的维度是x的最后一个维度值
    k = x.view(5,-1)
    print(x.size(),y.size(),z.size(),k.size())
    
    torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5]) torch.Size([5, 3])
    

    注: view()返回的新tensor与源tensor共享内存(其实是同一个tensor),也即更改其中的一个,另一个也会跟着改变。(顾名思义,view仅仅是改变了对张量的观察角度)

    x +=1
    print(x)
    print(y)
    
    tensor([[1.9341, 1.5139, 1.7338],
            [1.8216, 1.9009, 1.5679],
            [1.7110, 1.5658, 1.8897],
            [1.5174, 1.1536, 1.6354],
            [1.6097, 1.7780, 1.3942]])
    tensor([1.9341, 1.5139, 1.7338, 1.8216, 1.9009, 1.5679, 1.7110, 1.5658, 1.8897,
            1.5174, 1.1536, 1.6354, 1.6097, 1.7780, 1.3942])
    

    如果我们想返回一个真正新的副本(即不共享内容)

    应当使用reshape()改变形状

    但是此函数并不能保证返回的是其拷贝,所以不推荐使用

    推荐使用clone先创造一个副本然后再使用view

    x_cp = x.clone().view(15)
    x -= 1
    print(x)
    print(x_cp)
    
    tensor([[0.9341, 0.5139, 0.7338],
            [0.8216, 0.9009, 0.5679],
            [0.7110, 0.5658, 0.8897],
            [0.5174, 0.1536, 0.6354],
            [0.6097, 0.7780, 0.3942]])
    tensor([1.9341, 1.5139, 1.7338, 1.8216, 1.9009, 1.5679, 1.7110, 1.5658, 1.8897,
            1.5174, 1.1536, 1.6354, 1.6097, 1.7780, 1.3942])
    

    注:使用clone还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源Tensor

    另外一个常用的函数就是item(),它可以将一个标量Tensor转换成一个Python number:

    x = torch.randn(1)
    print(x)
    print(x.item())
    
    tensor([0.2996])
    0.2996445298194885
    

    线性代数

    PyTorch 还支持一些线性函数,这里提一下,免得用起来的时候自己造轮子。

    image

    2.2.3 广播机制

    当两个形状不同的Tensor按元素运算时,可能会触发广播broadcasting机制:先适当复制元素使这两个Tensor形状相同后再按元素运算。例如:

    x = torch.arange(1,3).view(1,2)
    print(x)
    y = torch.arange(1,4).view(3,1)
    print(y)
    print(x+y)
    
    tensor([[1, 2]])
    tensor([[1],
            [2],
            [3]])
    tensor([[2, 3],
            [3, 4],
            [4, 5]])
    

    x中第一行的2个元素被广播到了第二行和第三行,而y中第一列的3个元素被广播到了第二列。

    2.2.4 运算的内存开销

    索引、view不会开辟内存,y = x + y这样的运算会新开内存,然后将y指向新内存。

    为了验证,我们使用Python自带的id函数:如果两个实例的ID一致,那么它们所对应的内存地址相同,反之不同

    x = torch.tensor([1,2])
    y = torch.tensor([3,4])
    id_before = id(y)
    y = y + x
    print(id(y) == id_before) 
    
    False
    

    通过索引的替换操作指定结果到原y的内存

    例如:将x+y的结果通过[:]写进y对应的内存中。

    x = torch.tensor([1,2])
    y = torch.tensor([3,4])
    id_before = id(y)
    y[:] = y+x
    print(id(y) == id_before)
    
    tensor([1, 2])
    tensor([3, 4])
    True
    

    我们还可以使用运算符全名函数中out参数或者自加运算符+=(也即add_())达到上述效果,例如torch.add(x,y,out = y)y += x(y.add_(x))。

    x = torch.tensor([1,2])
    y = torch.tensor([3,4])
    id_before = id(y)
    torch.add(x,y,out =y ) # y += x,y.add_(x)
    print(id(y) == id_before)
    
    True
    

    2.2.5 Tensor和Numpy相互转换

    我们很容易用numpy()from_numpy()Tensor和Numpy中的数据相互转换。但是需要注意的一点是:这两个函数所产生的Tensor和Numpy中的数组共享相同的内存(所以他们之间的转换很快),改变其中一个时另一个也会改变!!!

    注:常用将Numpy中的array转换成Tensor的方法就是torch.tensor()。此方法总是会进行数据拷贝(消耗更多的时间和空间),所以返回的Tensor和原来的数据不再共享内存。

    Tensor转Numpy

    使用numpy()Tensor转换成Numpy数组

    a = torch.ones(5)
    b = a.numpy()
    print(a,b)
    
    a += 1
    print(a,b)
    b += 1
    print(a,b)
    
    tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.]
    tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]
    tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]
    

    Numpy 数组转Tensor

    使用from_numpy将numpy数组转换成Tensor

    import numpy as np
    
    a = np.ones(5)
    b = torch.from_numpy(a)
    print(a,b)
    
    a += 1
    print(a,b)
    b += 1
    print(a,b)
    
    [1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
    [2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
    [3. 3. 3. 3. 3.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
    

    所有在CPU上的Tensor(除了CharTensor)都支持与Numpy数组相互转换

    直接用torch.tensor()将Numpy转换成Tensor,需要注意的是该方法总是会进行数据拷贝,返回的Tensor和原来的数据不再共享内存

    c = torch.tensor(a)
    a += 1
    print(a,c)
    
    [4. 4. 4. 4. 4.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
    

    2.2.6 Tensor ON GPU

    用方法to()可以将Tensor在CPU和GPU之间相互移动

    #以下代码只有在PyTorch 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()还可以同时更改数据类型
    
    tensor([2, 3], device='cuda:0')
    tensor([2., 3.], dtype=torch.float64)
  • 相关阅读:
    singleton 创建static类型的对象
    记忆曲线 遗忘曲线
    创建classic 得到函数 调用函数
    abstract factory 创建相互关联的类
    log4j PatternLayout
    C#中override重写与new隐藏的区别,以及C#与Java的override区别 转
    iptables如何做内网的https端口映射 转
    得到一棵树 取自表内自递归(即ID 与ParentID)
    Common.TcpLibTcpClientT
    得到汉字的首拼音字符 ZT
  • 原文地址:https://www.cnblogs.com/MurasameLory-chenyulong/p/14764799.html
Copyright © 2011-2022 走看看