Manipulate data the MXNet way with ndarray


In [1]:
import mxnet as mx

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

Getting started


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


Out[3]:
[[0.000e+00 0.000e+00 0.000e+00 0.000e+00]
 [0.000e+00 0.000e+00 3.173e-42 0.000e+00]
 [0.000e+00 0.000e+00 0.000e+00 0.000e+00]]
<NDArray 3x4 @cpu(0)>

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


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

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


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

In [6]:
# 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[6]:
[[-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 [7]:
# Shape of the array
y.shape


Out[7]:
(3, 4)

In [8]:
x.shape


Out[8]:
(3, 4)

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


Out[9]:
12

Operations


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


Out[10]:
[[-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 [11]:
# Element-wise multiplication
x * y


Out[11]:
[[-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 [12]:
# Exponentiation
mx.nd.exp(y)


Out[12]:
[[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 [13]:
# Dot product
mx.nd.dot(x, y.T)


Out[13]:
[[-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 [14]:
# 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): 2246009058360
id(y): 2246009060544

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


id(y): 2246009060544
id(y): 2246009060544

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


Out[16]:
[[1.693556  3.1128774 0.3690076 2.8928642]
 [3.3134842 2.9426415 1.8894006 2.423489 ]
 [2.771004  3.5796072 3.8124368 4.0448427]]
<NDArray 3x4 @cpu(0)>

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


id(x): 2246009059088
id(x): 2246009059088

Slicing


In [18]:
x


Out[18]:
[[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 [19]:
# Single-dimensional slicing
x[1:3]


Out[19]:
[[4.313484  3.9426415 2.8894005 3.423489 ]
 [3.771004  4.579607  4.812437  5.0448427]]
<NDArray 2x4 @cpu(0)>

In [20]:
# Multi-dimensional slicing

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


Out[21]:
[[3.9426415 2.8894005]]
<NDArray 1x2 @cpu(0)>

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

In [23]:
x


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

Broadcasting


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


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

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


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

In [26]:
x + y


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

Broadcasting prefers to duplicate along the left most axis.


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

In [28]:
x + y


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

Converting from MXNet NDArray to NumPy


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


Out[29]:
numpy.ndarray

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


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

Managing context


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


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

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


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

In [33]:
x_gpu + z


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

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


Out[34]:
gpu(0)

In [35]:
x.context


Out[35]:
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 [36]:
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): 2246009170576
id(z): 2246009181128
id(z): 2246009181128

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