Manipulate data the MXNet way with ndarray


In [31]:
import mxnet as mx

In [13]:
# Set a random seed
mx.random.seed(1)

Getting started


In [4]:
# Create an NDArray without any values initialized.
x = mx.nd.empty(shape=(3, 4))
x


Out[4]:
[[7.0064923e-45 0.0000000e+00 4.9394557e-24 8.0154272e-43]
 [0.0000000e+00 0.0000000e+00 9.1842503e-41 0.0000000e+00]
 [5.0398430e-24 8.0154272e-43 0.0000000e+00 0.0000000e+00]]
<NDArray 3x4 @cpu(0)>

In [6]:
# Create an NDArray of zeros
x = mx.nd.zeros(shape=(3, 5))
x


Out[6]:
[[0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]]
<NDArray 3x5 @cpu(0)>

In [8]:
# Create an NDArray of ones
x = mx.nd.ones(shape=(3, 4))
x


Out[8]:
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]
<NDArray 3x4 @cpu(0)>

In [14]:
# Create an NDArray of randomly sampled values
# Example: Values drawn from a standard normal distribution with zero mean and unit variance
# loc: mean of the distribution
# scale: standard deviation of the distribution
y = mx.nd.random_normal(loc=0, scale=1, shape=(3, 4))
y


Out[14]:
[[-1.306444    0.11287735 -2.6309924  -0.10713575]
 [ 0.31348404 -0.05735846 -1.1105994  -0.5765109 ]
 [-0.22899596  0.57960725  0.8124368   1.0448428 ]]
<NDArray 3x4 @cpu(0)>

In [17]:
# Shape of the array
y.shape


Out[17]:
(3, 4)

In [20]:
x.shape


Out[20]:
(3, 4)

In [18]:
# Size of the array, which is equal to the product 
y.size


Out[18]:
12

Operations


In [21]:
# Element-wise addition
x + y


Out[21]:
[[-0.30644405  1.1128774  -1.6309924   0.8928642 ]
 [ 1.3134841   0.94264156 -0.1105994   0.4234891 ]
 [ 0.771004    1.5796072   1.8124368   2.0448427 ]]
<NDArray 3x4 @cpu(0)>

In [22]:
# Element-wise multiplication
x * y


Out[22]:
[[-1.306444    0.11287735 -2.6309924  -0.10713575]
 [ 0.31348404 -0.05735846 -1.1105994  -0.5765109 ]
 [-0.22899596  0.57960725  0.8124368   1.0448428 ]]
<NDArray 3x4 @cpu(0)>

In [23]:
# Exponentiation
mx.nd.exp(y)


Out[23]:
[[0.27078122 1.1194946  0.07200696 0.8984037 ]
 [1.3681836  0.94425553 0.32936147 0.5618553 ]
 [0.7953317  1.7853371  2.2533925  2.8429518 ]]
<NDArray 3x4 @cpu(0)>

In [24]:
# Dot product
mx.nd.dot(x, y.T)


Out[24]:
[[-3.931695  -1.4309847  2.207891 ]
 [-3.931695  -1.4309847  2.207891 ]
 [-3.931695  -1.4309847  2.207891 ]]
<NDArray 3x3 @cpu(0)>

In-place operations

In order to perform efficient in-place operations, we should avoid:

y = y + x

Instead, we should use:

y[:] = y + x

In [27]:
# Incorrect
print('id(y):', id(y))
y = y + x
print('id(y):', id(y))

# They have different IDs, which means they memory for variable y is allocated twice!


id(y): 2457459340400
id(y): 2457459339784

In [28]:
# Correct
print('id(y):', id(y))
y[:] = y + x
print('id(y):', id(y))


id(y): 2457459339784
id(y): 2457459339784

In [29]:
# Element-wise addition
mx.nd.elemwise_add(x, y, out=y)


Out[29]:
[[2.6935558 4.1128774 1.3690076 3.8928642]
 [4.313484  3.9426415 2.8894005 3.423489 ]
 [3.771004  4.579607  4.812437  5.0448427]]
<NDArray 3x4 @cpu(0)>

In [30]:
# Another accepted notation is '+='
print('id(x):', id(x))
x += y
print('id(x):', id(x))


id(x): 2457454349560
id(x): 2457454349560

Slicing


In [32]:
x


Out[32]:
[[3.6935558 5.1128774 2.3690076 4.892864 ]
 [5.313484  4.9426413 3.8894005 4.423489 ]
 [4.7710037 5.579607  5.812437  6.0448427]]
<NDArray 3x4 @cpu(0)>

In [34]:
# Single-dimensional slicing
x[1:3]


Out[34]:
[[5.313484  4.9426413 3.8894005 4.423489 ]
 [4.7710037 5.579607  5.812437  6.0448427]]
<NDArray 2x4 @cpu(0)>

In [35]:
# Multi-dimensional slicing

In [37]:
x[1:2, 1:3]


Out[37]:
[[4.9426413 3.8894005]]
<NDArray 1x2 @cpu(0)>

In [40]:
# Assigning new values
x[1:2,1:3] = 5

In [41]:
x


Out[41]:
[[3.6935558 5.1128774 2.3690076 4.892864 ]
 [5.313484  5.        5.        4.423489 ]
 [4.7710037 5.579607  5.812437  6.0448427]]
<NDArray 3x4 @cpu(0)>

Broadcasting


In [43]:
x = mx.nd.ones(shape=(3, 3))
x


Out[43]:
[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
<NDArray 3x3 @cpu(0)>

In [45]:
y = mx.nd.arange(3)
y


Out[45]:
[0. 1. 2.]
<NDArray 3 @cpu(0)>

In [46]:
x + y


Out[46]:
[[1. 2. 3.]
 [1. 2. 3.]
 [1. 2. 3.]]
<NDArray 3x3 @cpu(0)>

Broadcasting prefers to duplicate along the left most axis.


In [47]:
y = y.reshape(shape=(3, 1))

In [48]:
x + y


Out[48]:
[[1. 1. 1.]
 [2. 2. 2.]
 [3. 3. 3.]]
<NDArray 3x3 @cpu(0)>

Converting from MXNet NDArray to NumPy


In [49]:
a = x.asnumpy()
type(a)


Out[49]:
numpy.ndarray

In [51]:
y = mx.nd.array(a)
type(y)


Out[51]:
mxnet.ndarray.ndarray.NDArray

Managing context


In [53]:
# An array initialized on the GPU
z = mx.nd.ones(shape=(3, 3), ctx=mx.gpu(0))
z


Out[53]:
[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
<NDArray 3x3 @gpu(0)>

In [55]:
# Copying an NDArray from one context to another
x_gpu = x.copyto(mx.gpu(0))
x_gpu


Out[55]:
[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
<NDArray 3x3 @gpu(0)>

In [56]:
x_gpu + z


Out[56]:
[[2. 2. 2.]
 [2. 2. 2.]
 [2. 2. 2.]]
<NDArray 3x3 @gpu(0)>

In [57]:
# Checking the context of an NDArray
x_gpu.context


Out[57]:
gpu(0)

In [58]:
x.context


Out[58]:
cpu(0)

Important!

Make sure you are not copying (using 'copyto') an NDArray from the existing context to the same context. It will create an overhead!


In [62]:
print('id(z):', id(z))
z = z.copyto(mx.gpu(0))
print('id(z):', id(z))
z = z.as_in_context(mx.gpu(0))
print('id(z):', id(z))
print(z)


id(z): 2457492935736
id(z): 2457492980624
id(z): 2457492980624

[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
<NDArray 3x3 @gpu(0)>