zoukankan      html  css  js  c++  java
  • 数据操作

    数据操作 - 基于 pytorch

    为了更好地完成我们需求的任务,我们需要一些方法来存储和操作数据。让我们自己动手来处理综合的数据,需要引入N维数组(n-dimensional array)的概念,它也被称为张量(tensor)

    如果你使用过 NumPy,就会发现这篇博客的内容何其相似。其实用什么框架无所谓,张量类型(tensor class)(其中还有 MXNet 的 ndarray 和 PyTorch 和 TensorFlow 中的 Tensor )都是和 NumPy 的 ndarray 有着非常类似的特征。

    首先,GPU 将会在对计算工作起到非常好的加速支持,然而 NumPy 仅支持 CPU 计算;其次, tensor 类型可以自动地支持这两者。这些属性让 tensor 类型更适合深度学习。

    首先我们引入库文件

    注意我们引入的是 torch 而不是 pytorch

    import torch
    

    张量代表了数值数组,对于一个轴,张量代表了数学中的向量(vector),对于两个轴,其代表了矩阵(matrix),对于二个轴以上的张量,没有特别的数学名称。

    我们可以先使用 arange 函数创建一个行向量,参数中传递了行向量的数值范围,它们默认是 int64 类型。每一个值被称为向量的元素。除非特别说明,新创建的张量将会存储在内存中并且是基于 CPU 计算。

    x = torch.arange(12)
    x
    

    我们可以通过 shape 属性来获得张量的形状,即每个轴上的长度

    x.shape
    

    如果我们只是想查看张量元素的数量,即 shape 元素的乘积,我们可以使用 numel 方法

    x.numel()
    
    12
    

    为了改变张量的形状而不修改其中的值,我们可以使用 reshape 函数,下面的例子中,我们将行向量 x 转换成了一个 (3 imes 4) 的矩阵,返回的新张量内包含的值是相同的;注意,原张量的大小是不变的

    X = x.reshape(3, 4)
    X
    
    tensor([[ 0,  1,  2,  3],
            [ 4,  5,  6,  7],
            [ 8,  9, 10, 11]])
    

    对于, reshape 的每个维度是可以不必须指定的,如果目标是一个矩阵,我们只需要知道宽度,高度将会被隐含地给出。可以在参数传递时将 -1 传递在维度的位置表示让张量自动地计算;上面的函数调用还可以写成 x.reshape(3, -1) 或者 x.reshape(-1, 4)

    通常我们想让我嫩的矩阵初始化为 0 或 1,其他的一些常数,或者服从某些指定分布的随机数。

    torch.zeros((2, 3, 4))
    
    tensor([[[0., 0., 0., 0.],
             [0., 0., 0., 0.],
             [0., 0., 0., 0.]],
    
            [[0., 0., 0., 0.],
             [0., 0., 0., 0.],
             [0., 0., 0., 0.]]])
    
    torch.ones((2, 3, 4))
    
    tensor([[[1., 1., 1., 1.],
             [1., 1., 1., 1.],
             [1., 1., 1., 1.]],
    
            [[1., 1., 1., 1.],
             [1., 1., 1., 1.],
             [1., 1., 1., 1.]]])
    

    通常我们需要使用随机值初始化神经网络,我们可以创建一个张量,其所有元素服从标准高斯(正态)分布,均值为 0,方差为 1

    torch.randn(3, 4)
    
    tensor([[ 0.0701, -0.5225, -1.0090,  0.3266],
            [-1.2237,  0.1912,  1.7305, -1.3328],
            [-1.0946, -0.3725,  0.2093, -0.2414]])
    

    我们还可以将 python 中的列表(list)的中的每一个值用来创建一个张量,下面的例子中,外部的 list 代表 0 轴,里面的 list 代表 1 轴

    torch.tensor([[1, 3, 5, 7], [11, 13, 17, 19], [2, 4, 6, 8]])
    
    tensor([[ 1,  3,  5,  7],
            [11, 13, 17, 19],
            [ 2,  4,  6,  8]])
    

    运算

    x = torch.tensor([1.0, 2, 4, 8])
    y = torch.tensor([2, 2, 2, 2])
    x + y, x - y, x * y, x / y, x ** y  # The ** operator is exponentiation
    
    (tensor([ 3.,  4.,  6., 10.]),
     tensor([-1.,  0.,  2.,  6.]),
     tensor([ 2.,  4.,  8., 16.]),
     tensor([0.5000, 1.0000, 2.0000, 4.0000]),
     tensor([ 1.,  4., 16., 64.]))
    
    torch.exp(x)
    
    tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])
    

    如果想要连结(concatenate)多个张量在一起,我们将张量放在列表中传入函数,并且指定按哪个轴连结,比如 axis 0 表示 shape 的第一个元素,axis 1 是第二个元素,以此类推;在下面的例子中,第一个返回值是一个 (6 imes 4) 的矩阵,因为是按照 axis 0 连结,两个张量的 shape 的第一个元素都是 3,所以合并后的 axis 0 的值是 6

    X = torch.arange(12, dtype=torch.float32).reshape((3, 4))
    Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
    torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
    
    (tensor([[ 0.,  1.,  2.,  3.],
             [ 4.,  5.,  6.,  7.],
             [ 8.,  9., 10., 11.],
             [ 2.,  1.,  4.,  3.],
             [ 1.,  2.,  3.,  4.],
             [ 4.,  3.,  2.,  1.]]),
     tensor([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],
             [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],
             [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]]))
    

    有时候,我们需要通过逻辑语句构造一个二进制值的张量,例如 X == Y,返回的张量的每一个元素都是 0 或 1

    X == Y
    
    tensor([[False,  True, False,  True],
            [False, False, False, False],
            [False, False, False, False]])
    

    计算张量的总和,将返回一个只有一个元素的张量

    X.sum()
    
    tensor(66.)
    

    索引和切片

    就像其它的 Python 数组对象,张量中的元素可以使用索引访问。通常在 Python 数组中,第一个元素下标从 0 开始到数组长度减 1 的索引。作为标准的 Python 列表,也可以通过元素的相对位置,通过负索引访问最后的元素。

    X[-1], X[1:3]
    
    (tensor([ 8.,  9., 10., 11.]),
     tensor([[ 4.,  5.,  6.,  7.],
             [ 8.,  9., 10., 11.]]))
    

    也可以通过索引对元素进行修改

    X[1, 2] = 9
    X
    
    tensor([[ 0.,  1.,  2.,  3.],
            [ 4.,  5.,  9.,  7.],
            [ 8.,  9., 10., 11.]])
    

    如果想要对多个元素分配相同的值,可以简单地使用索引指派。[0:2, :] 表示第 1 行和第 2 行,: 表示 axis 1 (column) 的所有元素。同样地,索引的操作也可以用来向量和多于 2 维的张量上。

    X[0:2, :] = 12
    X
    
    tensor([[12., 12., 12., 12.],
            [12., 12., 12., 12.],
            [ 8.,  9., 10., 11.]])
    

    广播机制

    在上面的运算符部分,我们可以看到两个相同 shape 的张量可以按照元素对元素(elementwise)的运算符计算得到新的相同 shape 的张量;在某些条件下,即使我们张量的 shape 不同,我们仍然可以通过广播机制(broadcasting mechanism)对两个向量进行按元素对元素运算;它的工作过程如下:首先,将两个张量元素都复制到合适的相同大小,然后将转换后的有着相同 shape 的两个向量进行 elementwise 运算。

    a = torch.arange(3).reshape((3, 1))
    b = torch.arange(2).reshape((1, 2))
    a, b
    
    (tensor([[0],
             [1],
             [2]]),
     tensor([[0, 1]]))
    

    因为 ab 分别是 (3 imes 1)(1 imes 2) 的矩阵, 它们的 shape 并匹配,如果我们对其进行相加操作,我们将触发广播机制在这两个矩阵上,将其增大为 (3 imes 2) 的矩阵,将矩阵 a 将相应的列和 b 相应的行复制到合适的大小

    a + b
    
    tensor([[0, 1],
            [1, 2],
            [2, 3]])
    

    内存开销

    运行操作符将导致在内存中新开辟一部分空间存储新的运行结果,比如我们执行 Y = X + Y 语句,我们将开辟新内存空间,然后将 Y 重新指向这个内存地址。

    before = id(Y)
    Y = X + Y
    id(Y) == before
    
    False
    

    首先,我们不希望不断地在内存中申请非必需的空间,在机器学习中,我们可能有成千上百 M 的参数需要去更新,我们希望在原内存空间中更新这些参数,并且一般将会由多个变量指向相同的参数,如果将参数更新,其它的指针可能还指向原先参数的旧空间地址。

    我们可以简单地在内存原地址操作数据,我们可以使用切片符号对之前已经申请了的空间进行更新或使用,例如 Y[:] = <expression> 语句

    Z = torch.zeros_like(Y)
    print('id(Z): ', id(Z))
    Z[:] = X + Y
    print('id(Z): ', id(Z))
    
    id(Z):  2122309357184
    id(Z):  2122309357184
    

    如果 X 的值在之后无需使用,可以使用 X += Y 语句在内存上操作

    before = id(X)
    X += Y
    id(X) == before
    
    True
    

    转换为其它 Python 对象

    可将张量转换为 NumPy 的 ndarray 对象,转换之后的对象是不共享内存的,这使得我们在进行计算时,你不想停止运算,等待着查看 NumPy 包是否想要在相同的内存做其它操作。

    A = X.numpy()  # 将 tensor 转换为 NumPy 的 ndarray
    B = torch.tensor(A)  # 将 ndarray 转换为 Tensor
    type(A), type(B)
    
    (numpy.ndarray, torch.Tensor)
  • 相关阅读:
    Thinking in Java Reading Note(9.接口)
    Thinking in java Reading Note(8.多态)
    Thinking in Java Reading Note(7.复用类)
    SQL必知必会
    Thinking in Java Reading Note(5.初始化与清理)
    Thinking in Java Reading Note(2.一切都是对象)
    鸟哥的Linux私房菜笔记(1.基础)
    Thinking in Java Reading Note(1.对象导论)
    CoreJava2 Reading Note(2:I/O)
    CoreJava2 Reading Note(1:Stream)
  • 原文地址:https://www.cnblogs.com/geekfx/p/13897761.html
Copyright © 2011-2022 走看看