| # 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. |