MXNet中NDArray数据相关操作

目录

绪论

前言

2.2.1. 创建NDArray

2.2.2. 运算

2.2.3. broadcasting机制

2.2.4. 索引

2.2.5. 运算的内存开销

2.2.6. NDArray和NumPy相互变换

2.2.7. 小结

2.2.8. 练习

2.2.8. 答案


绪论

在深度学习中,我们通常会频繁地对数据进行操作。作为深度学习的基础,本节将介绍如何对CPU内存中的数据的基本操作。

MXNet中,NDArray是一个类,也是存储和变换数据的主要工具。NDArray和NumPy的多维数组非常类似。然而,NDArray提供GPU计算和自动求梯度等更多功能,这些使NDArray更加适合深度学习。

前言

这里作者是使用juputer notebook来学习,对比于pycharm和它的控制台来说,对于较大型的项目用pycharm可以实现分文件管理,pycharm的控制台适合运行单行代码,jupyter notebook因为对于单个模块运行之后都会有一个输出直观看到结果;

2.2.1. 创建NDArray

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

from mxnet import nd

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

x = nd.arange(12)

#这里x的作用相当于print(x)

x               

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

可以看到nd.arange创建了一个从0开始的12个连续整数,类型 NDArray ,长度 12,创建在cpu内存上

shape属性来获取NDArray实例的形状。

x.shape

Out[3]:
(12,)

size属性得到NDArray实例中元素(element)的总数。

x.size

 Out[4]:

12

reshape函数把行向量x的形状改为(3, 4),也就是一个3行4列的矩阵,并记作X。(注意前面是小写x,后面是大写X)

X = x.reshape((3, 4))
#这里X的作用相当于print(X)
X
Out[5]:
[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]]

小技巧:上面x.reshape((3, 4))也可写成x.reshape((-1, 4))x.reshape((3, -1))。由于x的元素个数是已知的12,这里的-1是能够通过元素个数和其他维度的大小推断出来的。

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

nd.zeros((2, 3, 4))

Out[6]:
[[[0. 0. 0. 0.]
  [0. 0. 0. 0.]
  [0. 0. 0. 0.]]

 [[0. 0. 0. 0.]
  [0. 0. 0. 0.]
  [0. 0. 0. 0.]]]

可以理解成2层,每一层都是3*4的矩阵。

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

nd.ones((3, 4))

Out[7]:

[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]

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

Y = nd.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])

Y

 Out[8]:

[[2. 1. 4. 3.]
 [1. 2. 3. 4.]
 [4. 3. 2. 1.]]

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

nd.random.normal(0, 1, shape=(3, 4))

 Out[9]:

[[ 2.2122064   0.7740038   1.0434405   1.1839255 ]
 [ 1.8917114  -1.2347414  -1.771029   -0.45138445]
 [ 0.57938355 -1.856082   -1.9768796  -0.20801921]]

2.2.2. 运算

NDArray支持大量的运算符(operator)。例如,我们可以对之前创建的X和Y形状为(3, 4)NDArray做按元素运算。

X:
[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]]

 Y:

[[2. 1. 4. 3.]
 [1. 2. 3. 4.]
 [4. 3. 2. 1.]]

按元素加法:

X + Y

 Out[10]:

[[ 2.  2.  6.  6.]
 [ 5.  7.  9. 11.]
 [12. 12. 12. 12.]]

按元素乘法(也叫阿达玛(Hadamard)积)如下:

X * Y

 Out[11]:

[[ 0.  1.  8.  9.]
 [ 4. 10. 18. 28.]
 [32. 27. 20. 11.]]

按元素除法如下:

X / Y
Out[12]:
[[ 0.    1.    0.5   1.  ]
 [ 4.    2.5   2.    1.75]
 [ 2.    3.    5.   11.  ]]

 按元素做指数运算如下:

Y.exp()

 Out[13]:

[[ 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函数做矩阵乘法:

nd.dot(X, Y.T)
Out[14]:
[[ 18.  20.  10.]
 [ 58.  60.  50.]
 [ 98. 100.  90.]]

将多个NDArray连接(concatenate)。

#维度dim = 0或者1,在行上连接即上下连接或者在列上左右连接
nd.concat(X, Y, dim=0), nd.concat(X, Y, dim=1)

 Out[15]:

(
 [[ 0.  1.  2.  3.]
  [ 4.  5.  6.  7.]
  [ 8.  9. 10. 11.]
  [ 2.  1.  4.  3.]
  [ 1.  2.  3.  4.]
  [ 4.  3.  2.  1.]],

 [[ 0.  1.  2.  3.  2.  1.  4.  3.]
  [ 4.  5.  6.  7.  1.  2.  3.  4.]
  [ 8.  9. 10. 11.  4.  3.  2.  1.]]

使用条件判断式,如果XY在相同位置的条件判断为真(值相等),那么新的NDArray在相同位置的值为1;反之为0。

X == Y

 Out[16]:

[[0. 1. 0. 1.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]

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

X.sum()

 Out[17]:

[66.]

通过asscalar函数将结果变换为Python中的标量。

X.norm().asscalar()

 Out[18]:

22.494442

norm():矩阵X的Frobenius范数,为该矩阵元素平方和的平方根。

2.2.3. broadcasting机制

当对两个形状不同的NDArray按元素运算时,可能会触发broadcasting机制。

A = nd.arange(3).reshape((3, 1))

B = nd.arange(2).reshape((1, 2))

A,B

Out[19]:
(
 [[0.]
  [1.]
  [2.]],

 [[0. 1.]]

计算A+B,那么A中第一列的3个元素复制到第二列,而B中第一行的2个元素复制到第二行和第三行。如此,就可以对2个3行2列的矩阵按元素相加。

A + B

 Out[20]:

[[0. 1.]
 [1. 2.]
 [2. 3.]]

2.2.4. 索引

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

X:
[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]]

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

X[1, 2] = 9
X

Out[21]:
[[ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]]

指定NDArray中需要访问的单个元素的位置

X[1, 2] = 9

X

 Out[22]:

[[ 0.  1.  2.  3.]
 [ 4.  5.  9.  7.]
 [ 8.  9. 10. 11.]]

截取一部分元素,并为它们重新赋值。

X[1:2, :] = 12

X

Out[23]:
[[ 0.  1.  2.  3.]
 [12. 12. 12. 12.]
 [ 8.  9. 10. 11.]]

2.2.5. 运算的内存开销

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

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

 Out[24]:

False

这里学过指针的同学应该就比较清楚。

如果想指定结果到特定内存,我们可以使用前面介绍的索引来进行替换操作。

Z = Y.zeros_like()

before = id(Z)

Z[:] = X + Y

id(Z) == before

 Out[25]:

True

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

nd.elemwise_add(X, Y, out=Z)

id(Z) == before

Out[26]:
True

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

before = id(X)
X += Y
id(X) == before
Out[27]:
True

2.2.6. NDArray和NumPy相互变换

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

import numpy as np

P = np.ones((2, 3))
D = nd.array(P)
D
Out[28]:
[[1. 1. 1.]
 [1. 1. 1.]]
<NDArray 2x3 @cpu(0)>

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

D.asnumpy()

 Out[29]:

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

2.2.7. 小结

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

2.2.8. 练习

  • 运行本节中的代码。将本节中条件判别式X == Y改为X < YX > Y,看看能够得到什么样的NDArray
  • 将broadcasting中按元素运算的两个NDArray替换成其他形状,结果是否和预期一样?

2.2.8. 答案

将本节中条件判别式X == Y改为X < YX > Y。

X < Y

[[1. 0. 1. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]

X > Y

[[0. 0. 0. 0.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]

将broadcasting中按元素运算的两个NDArray替换成其他形状。

A = nd.arange(8).reshape((4, 2))
B = nd.arange(4).reshape((4, 1))
A, B

(
 [[0. 1.]
  [2. 3.]
  [4. 5.]
  [6. 7.]],
 
 [[0.]
  [1.]
  [2.]
  [3.]]
 <NDArray 4x1 @cpu(0)>)

A + B

[[ 0.  1.]
 [ 3.  4.]
 [ 6.  7.]
 [ 9. 10.]]

触发broadcasting的条件是,如果是列复制,那么就应该只有一列。同理,如果是行复制的话,原数组应该只有一行,不然会报错。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值