blob: 981e01d50a63ae654659b89c6137517cbbba262e [file] [log] [blame]
# NDArray API
The NDArray package (`mxnet.ndarray`) contains tensor operations similar to `numpy.ndarray`. The syntax is also similar, except for some additional calls for dealing with I/O and multiple devices.
Topics:
* [Create NDArray](#create-ndarray)
* [NDArray Operations](#ndarray-operations)
* [NDArray API Reference](http://mxnet.io/api/scala/docs/index.html#ml.dmlc.mxnet.NDArray)
## Create NDArray
Create `mxnet.ndarray` as follows:
```scala
scala> import ml.dmlc.mxnet._
scala> // all-zero array of dimension 100x50
scala> val a = NDArray.zeros(100, 50)
scala> // all-one array of dimension 256x32x128x1
scala> val b = NDArray.ones(256, 32, 128, 1)
scala> // initialize array with contents, you can specify dimensions of array using Shape parameter while creating array.
scala> val c = NDArray.array(Array(1, 2, 3, 4, 5, 6), shape = Shape(2, 3))
```
This is similar to the way you use `numpy`.
## NDArray Operations
We provide some basic ndarray operations, like arithmetic and slice operations.
### Arithmetic Operations
```scala
scala> import ml.dmlc.mxnet._
scala> val a = NDArray.zeros(100, 50)
scala> a.shape
ml.dmlc.mxnet.Shape = (100,50)
scala> val b = NDArray.ones(100, 50)
scala> // c and d will be calculated in parallel here!
scala> val c = a + b
scala> val d = a - b
scala> // inplace operation, b's contents will be modified, but c and d won't be affected.
scala> b += d
```
### Multiplication/Division Operations
```scala
scala> import ml.dmlc.mxnet._
//Multiplication
scala> val ndones = NDArray.ones(2, 1)
scala> val ndtwos = ndones * 2
scala> ndtwos.toArray
Array[Float] = Array(2.0, 2.0)
scala> (ndones * ndones).toArray
Array[Float] = Array(1.0, 1.0)
scala> (ndtwos * ndtwos).toArray
Array[Float] = Array(4.0, 4.0)
scala> ndtwos *= ndtwos // inplace
scala> ndtwos.toArray
Array[Float] = Array(4.0, 4.0)
//Division
scala> val ndones = NDArray.ones(2, 1)
scala> val ndzeros = ndones - 1f
scala> val ndhalves = ndones / 2
scala> ndhalves.toArray
Array[Float] = Array(0.5, 0.5)
scala> (ndhalves / ndhalves).toArray
Array[Float] = Array(1.0, 1.0)
scala> (ndones / ndones).toArray
Array[Float] = Array(1.0, 1.0)
scala> (ndzeros / ndones).toArray
Array[Float] = Array(0.0, 0.0)
scala> ndhalves /= ndhalves
scala> ndhalves.toArray
Array[Float] = Array(1.0, 1.0)
```
### Slice Operations
```scala
scala> import ml.dmlc.mxnet._
scala> val a = NDArray.array(Array(1f, 2f, 3f, 4f, 5f, 6f), shape = Shape(3, 2))
scala> val a1 = a.slice(1)
scala> assert(a1.shape === Shape(1, 2))
scala> assert(a1.toArray === Array(3f, 4f))
scala> val a2 = arr.slice(1, 3)
scala> assert(a2.shape === Shape(2, 2))
scala> assert(a2.toArray === Array(3f, 4f, 5f, 6f))
```
### Dot Product
```scala
scala> import ml.dmlc.mxnet._
scala> val arr1 = NDArray.array(Array(1f, 2f), shape = Shape(1, 2))
scala> val arr2 = NDArray.array(Array(3f, 4f), shape = Shape(2, 1))
scala> val res = NDArray.dot(arr1, arr2)
scala> res.shape
ml.dmlc.mxnet.Shape = (1,1)
scala> res.toArray
Array[Float] = Array(11.0)
```
### Save and Load NDArray
You can use MXNet functions to save and load a list or dictionary of NDArrays from file systems, as follows:
```scala
scala> import ml.dmlc.mxnet._
scala> val a = NDArray.zeros(100, 200)
scala> val b = NDArray.zeros(100, 200)
scala> // save list of NDArrays
scala> NDArray.save("/path/to/array/file", Array(a, b))
scala> // save dictionary of NDArrays to AWS S3
scala> NDArray.save("s3://path/to/s3/array", Map("A" -> a, "B" -> b))
scala> // save list of NDArrays to hdfs.
scala> NDArray.save("hdfs://path/to/hdfs/array", Array(a, b))
scala> val from_file = NDArray.load("/path/to/array/file")
scala> val from_s3 = NDArray.load("s3://path/to/s3/array")
scala> val from_hdfs = NDArray.load("hdfs://path/to/hdfs/array")
```
The good thing about using the `save` and `load` interface is that you can use the format across all `mxnet` language bindings. They also already support Amazon S3 and HDFS.
### Multi-Device Support
Device information is stored in the `mxnet.Context` structure. When creating NDArray in MXNet, you can use the context argument (the default is the CPU context) to create arrays on specific devices as follows:
```scala
scala> import ml.dmlc.mxnet._
scala> val cpu_a = NDArray.zeros(100, 200)
scala> cpu_a.context
ml.dmlc.mxnet.Context = cpu(0)
scala> val ctx = Context.gpu(0)
scala> val gpu_b = NDArray.zeros(Shape(100, 200), ctx)
scala> gpu_b.context
ml.dmlc.mxnet.Context = gpu(0)
```
Currently, we *do not* allow operations among arrays from different contexts. To manually enable this, use the `copyto` member function to copy the content to different devices, and continue computation:
```scala
scala> import ml.dmlc.mxnet._
scala> val x = NDArray.zeros(100, 200)
scala> val ctx = Context.gpu(0)
scala> val y = NDArray.zeros(Shape(100, 200), ctx)
scala> val z = x + y
mxnet.base.MXNetError: [13:29:12] src/ndarray/ndarray.cc:33:
Check failed: lhs.ctx() == rhs.ctx() operands context mismatch
scala> val cpu_y = NDArray.zeros(100, 200)
scala> y.copyto(cpu_y)
scala> val z = x + cpu_y
```
## Next Steps
* See [KVStore API](kvstore.md) for multi-GPU and multi-host distributed training.