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

    本博文来自于 《动手学深度学习》 本博文只是记录学习笔记,方便日后查缺补漏,如有侵权,联系删除

    数据操作

    在深度学习中,通常会频繁地对数据进行操作。

    在MXNet中,NDArray是一个类,也是存储和变换数据的主要工具。为了简洁,常将NDArray实例直接称作NDArray。如果你之前用过NumPy,你会发现NDArray和NumPy的多维数组非常类似。然而,NDArray提供GPU计算和自动求梯度等更多功能,这些使NDArray更加适合深度学习。

    创建NDArray

    我们先介绍NDArray的最基本功能。

    首先从MXNet导入ndarray模块。这里的ndndarray的缩写形式。

    from mxnet import nd
    

    然后我们用arange函数创建一个行向量。

    x = nd.arange(12)
    x
    
    [ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9. 10. 11.]
    <NDArray 12 @cpu(0)>
    

    这时返回了一个NDArray实例,其中包含了从0开始的12个连续整数。从打印x时显示的属性<NDArray 12 @cpu(0)>可以看出,它是长度为12的一维数组,且被创建在CPU使用的内存上。其中“@cpu(0)”里的0没有特别的意义,并不代表特定的核。

    我们可以通过shape属性来获取NDArray实例的形状。

    x.shape
    
    (12,)
    

    我们也能够通过size属性得到NDArray实例中元素(element)的总数。

    x.size
    
    12
    

    下面使用reshape函数把行向量x的形状改为(3, 4),也就是一个3行4列的矩阵,并记作X。除了形状改变之外,X中的元素保持不变。

    X = x.reshape((3, 4))
    X
    
    [[ 0.  1.  2.  3.]
     [ 4.  5.  6.  7.]
     [ 8.  9. 10. 11.]]
    <NDArray 3x4 @cpu(0)>
    

    注意X属性中的形状发生了变化。上面x.reshape((3, 4))也可写成x.reshape((-1, 4))x.reshape((3, -1))。由于x的元素个数是已知的,这里的-1是能够通过元素个数和其他维度的大小推断出来的。

    接下来,我们创建一个各元素为0,形状为(2, 3, 4)的张量。实际上,之前创建的向量和矩阵都是特殊的张量。

    nd.zeros((2, 3, 4))
    
    [[[0. 0. 0. 0.]
      [0. 0. 0. 0.]
      [0. 0. 0. 0.]]
    
     [[0. 0. 0. 0.]
      [0. 0. 0. 0.]
      [0. 0. 0. 0.]]]
    <NDArray 2x3x4 @cpu(0)>
    

    类似地,我们可以创建各元素为1的张量。

    nd.ones((3, 4))
    
    [[1. 1. 1. 1.]
     [1. 1. 1. 1.]
     [1. 1. 1. 1.]]
    <NDArray 3x4 @cpu(0)>
    

    我们也可以通过Python的列表(list)指定需要创建的NDArray中每个元素的值。

    Y = nd.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
    Y
    
    [[2. 1. 4. 3.]
     [1. 2. 3. 4.]
     [4. 3. 2. 1.]]
    <NDArray 3x4 @cpu(0)>
    

    有些情况下,我们需要随机生成NDArray中每个元素的值。下面我们创建一个形状为(3, 4)的NDArray。它的每个元素都随机采样于均值为0、标准差为1的正态分布。

    nd.random.normal(0, 1, shape=(3, 4))
    
    [[ 1.1630785   0.4838046   0.29956347  0.15302546]
     [-1.1688148   1.558071   -0.5459446  -2.3556297 ]
     [ 0.54144025  2.6785064   1.2546344  -0.54877406]]
    <NDArray 3x4 @cpu(0)>
    

    运算

    NDArray支持大量的运算符(operator)。例如,我们可以对之前创建的两个形状为(3, 4)的NDArray做按元素加法。所得结果形状不变。

    X + Y
    
    [[ 2.  2.  6.  6.]
     [ 5.  7.  9. 11.]
     [12. 12. 12. 12.]]
    <NDArray 3x4 @cpu(0)>
    

    按元素乘法如下:

    X * Y
    
    [[ 0.  1.  8.  9.]
     [ 4. 10. 18. 28.]
     [32. 27. 20. 11.]]
    <NDArray 3x4 @cpu(0)>
    

    按元素除法如下:

    X / Y
    
    [[ 0.    1.    0.5   1.  ]
     [ 4.    2.5   2.    1.75]
     [ 2.    3.    5.   11.  ]]
    <NDArray 3x4 @cpu(0)>
    

    按元素做指数运算如下:

    Y.exp()
    
    [[ 7.389056   2.7182817 54.59815   20.085537 ]
     [ 2.7182817  7.389056  20.085537  54.59815  ]
     [54.59815   20.085537   7.389056   2.7182817]]
    <NDArray 3x4 @cpu(0)>
    

    除了按元素计算外,我们还可以使用dot函数做矩阵乘法。下面将XY的转置做矩阵乘法。由于X是3行4列的矩阵,Y转置为4行3列的矩阵,因此两个矩阵相乘得到3行3列的矩阵。

    nd.dot(X, Y.T)
    
    [[ 18.  20.  10.]
     [ 58.  60.  50.]
     [ 98. 100.  90.]]
    <NDArray 3x3 @cpu(0)>
    

    我们也可以将多个NDArray连结(concatenate)。下面分别在行上(维度0,即形状中的最左边元素)和列上(维度1,即形状中左起第二个元素)连结两个矩阵。可以看到,输出的第一个NDArray在维度0的长度((6))为两个输入矩阵在维度0的长度之和((3+3)),而输出的第二个NDArray在维度1的长度((8))为两个输入矩阵在维度1的长度之和((4+4))。

    nd.concat(X, Y, dim=0), nd.concat(X, Y, dim=1)
    
    (
     [[ 0.  1.  2.  3.]
      [ 4.  5.  6.  7.]
      [ 8.  9. 10. 11.]
      [ 2.  1.  4.  3.]
      [ 1.  2.  3.  4.]
      [ 4.  3.  2.  1.]]
     <NDArray 6x4 @cpu(0)>,
     
     [[ 0.  1.  2.  3.  2.  1.  4.  3.]
      [ 4.  5.  6.  7.  1.  2.  3.  4.]
      [ 8.  9. 10. 11.  4.  3.  2.  1.]]
     <NDArray 3x8 @cpu(0)>)
    

    使用条件判断式可以得到元素为0或1的新的NDArray。以X == Y为例,如果XY在相同位置的条件判断为真(值相等),那么新的NDArray在相同位置的值为1;反之为0。

    X == Y
    
    [[0. 1. 0. 1.]
     [0. 0. 0. 0.]
     [0. 0. 0. 0.]]
    <NDArray 3x4 @cpu(0)>
    

    NDArray中的所有元素求和得到只有一个元素的NDArray

    X.sum()
    
    [66.]
    <NDArray 1 @cpu(0)>
    

    我们可以通过asscalar函数将结果变换为Python中的标量。下面例子中X(L_2)范数结果同上例一样是单元素NDArray,但最后结果变换成了Python中的标量。

    X.norm().asscalar()
    
    22.494442
    

    我们也可以把Y.exp()X.sum()X.norm()等分别改写为nd.exp(Y)nd.sum(X)nd.norm(X)等。

    广播机制

    前面我们看到如何对两个形状相同的NDArray做按元素运算。当对两个形状不同的NDArray按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个NDArray形状相同后再按元素运算。

    先定义两个NDArray

    A = nd.arange(3).reshape((3, 1))
    B = nd.arange(2).reshape((1, 2))
    A, B
    
    (
     [[0.]
      [1.]
      [2.]]
     <NDArray 3x1 @cpu(0)>,
     
     [[0. 1.]]
     <NDArray 1x2 @cpu(0)>)
    

    由于AB分别是3行1列和1行2列的矩阵,如果要计算A + B,那么A中第一列的3个元素被广播(复制)到了第二列,而B中第一行的2个元素被广播(复制)到了第二行和第三行。如此,就可以对2个3行2列的矩阵按元素相加。

    A + B
    
    [[0. 1.]
     [1. 2.]
     [2. 3.]]
    <NDArray 3x2 @cpu(0)>
    

    索引

    NDArray中,索引(index)代表了元素的位置。NDArray的索引从0开始逐一递增。例如,一个3行2列的矩阵的行索引分别为0、1和2,列索引分别为0和1。

    在下面的例子中,我们指定了NDArray的行索引截取范围[1:3]。依据左闭右开指定范围的惯例,它截取了矩阵X中行索引为1和2的两行。

    X[1:3]
    
    [[ 4.  5.  6.  7.]
     [ 8.  9. 10. 11.]]
    <NDArray 2x4 @cpu(0)>
    

    我们可以指定NDArray中需要访问的单个元素的位置,如矩阵中行和列的索引,并为该元素重新赋值。

    X[1, 2] = 9
    X
    
    [[ 0.  1.  2.  3.]
     [ 4.  5.  9.  7.]
     [ 8.  9. 10. 11.]]
    <NDArray 3x4 @cpu(0)>
    

    当然,我们也可以截取一部分元素,并为它们重新赋值。在下面的例子中,我们为行索引为1的每一列元素重新赋值。

    X[1:2, :] = 12
    X
    
    [[ 0.  1.  2.  3.]
     [12. 12. 12. 12.]
     [ 8.  9. 10. 11.]]
    <NDArray 3x4 @cpu(0)>
    

    运算的内存开销

    在前面的例子里我们对每个操作新开内存来存储运算结果。举个例子,即使像Y = X + Y这样的运算,我们也会新开内存,然后将Y指向新内存。为了演示这一点,我们可以使用Python自带的id函数:如果两个实例的ID一致,那么它们所对应的内存地址相同;反之则不同。

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

    如果想指定结果到特定内存,我们可以使用前面介绍的索引来进行替换操作。在下面的例子中,我们先通过zeros_like创建和Y形状相同且元素为0的NDArray,记为Z。接下来,我们把X + Y的结果通过[:]写进Z对应的内存中。

    Z = Y.zeros_like()
    before = id(Z)
    Z[:] = X + Y
    id(Z) == before
    
    True
    

    实际上,上例中我们还是为X + Y开了临时内存来存储计算结果,再复制到Z对应的内存。如果想避免这个临时内存开销,我们可以使用运算符全名函数中的out参数。

    nd.elemwise_add(X, Y, out=Z)
    id(Z) == before
    
    True
    

    如果X的值在之后的程序中不会复用,我们也可以用 X[:] = X + Y 或者 X += Y 来减少运算的内存开销。

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

    NDArray和NumPy相互变换

    我们可以通过array函数和asnumpy函数令数据在NDArray和NumPy格式之间相互变换。下面将NumPy实例变换成NDArray实例。

    import numpy as np
    
    P = np.ones((2, 3))
    D = nd.array(P)
    D
    
    [[1. 1. 1.]
     [1. 1. 1.]]
    <NDArray 2x3 @cpu(0)>
    

    再将NDArray实例变换成NumPy实例。

    D.asnumpy()
    
    array([[1., 1., 1.],
           [1., 1., 1.]], dtype=float32)
    

    小结

    • NDArray是MXNet中存储和变换数据的主要工具。
    • 可以轻松地对NDArray创建、运算、指定索引,并与NumPy之间相互变换。

    练习

    • 运行本节中的代码。将本节中条件判别式X == Y改为X < YX > Y,看看能够得到什么样的NDArray
    • 将广播机制中按元素运算的两个NDArray替换成其他形状,结果是否和预期一样?
    不一定每天 code well 但要每天 live well
  • 相关阅读:
    权限管理
    用户和用户管理
    文本编辑器vim
    对于我的linux常用命令的说明
    linux常用命令6:关机重启命令
    linux常用命令:5网络命令
    linux常用命令:4文件压缩和解压命令
    linux常用命令:3文件搜索命令
    linux常用命令:2权限管理命令
    通过原型继承创建一个新对象
  • 原文地址:https://www.cnblogs.com/geekfx/p/13875259.html
Copyright © 2011-2022 走看看