Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
There are several other basic matrix manipulations to highlight as you learn ND4J’s workings.
The transpose of a matrix is its mirror image. An element located in row 1, column 2, in matrix A will be located in row 2, column 1, in the transpose of matrix A, whose mathematical notation is A to the T, or A^T. Notice that the elements along the diagonal of a square matrix do not move – they are at the hinge of the reflection. In ND4J, transpose matrices like this:
And a long matrix like this
Looks like this when it is transposed
In fact, transpose is just an important subset of a more general operation: reshape.
Yes, matrices can be reshaped. You can change the number of rows and columns they have. The reshaped matrix has to fulfill one condition: the product of its rows and columns must equal the product of the row and columns of the original matrix. For example, proceeding columnwise, you can reshape a 3 by 4 matrix into a 2 by 6 matrix:
The array nd2 looks like this
Reshaping it is easy, and follows the same convention by which we gave it shape to begin with
Broadcast is advanced. It usually happens in the background without having to be called. The simplest way to understand it is by working with one long row vector, like the one above.
Broadcasting will actually take multiple copies of that row vector and put them together into a larger matrix. The first parameter is the number of copies you want “broadcast,” as well as the number of rows involved. In order not to throw a compiler error, make the second parameter of broadcast equal to the number of elements in your row vector.
Elementwise operations are more intuitive than vectorwise operations, because the elements of one matrix map clearly onto the other, and to obtain the result, you have to perform just one arithmetical operation.
With vectorwise matrix operations, you will have to first build intuition and also perform multiple steps. There are two basic types of matrix multiplication: inner (dot) product and outer product. The inner product results in a matrix of reduced dimensions, the outer product results in one of expanded dimensions. A helpful mnemonic: Expand outward, contract inward.
Unlike Hadamard products, which require that both matrices have equal rows and columns, inner products simply require that the number of columns of the first matrix equal the number of rows of the second. For example, this works
Notice a 1 x 2 row times a 2 x 1 column produces a scalar. This operation reduces the dimensions to 1,1. You can imagine rotating the row vector [1.0 ,2.0] clockwise to stand on its end, placed against the column vector. The two top elements are then multiplied by each other, as are the bottom two, and the two products are added to consolidate in a single scalar.
In ND4J, you would create the two vectors like this:
And multiply them like this
Notice ND4J code mirrors the equation in that nd * nd2 is row vector times column vector. The method is mmul, rather than the mul we used for elementwise operations, and the extra “m” stands for “matrix.”
Now let’s take the same operation, while adding an additional column to a new array we’ll call nd4.
Now let’s add an extra row to the first matrix, call it nd3, and multiply it by nd4
The equation will look like this
Taking the outer product of the two vectors we first worked with is as simple as reversing their order.
It turns out the multiplying nd2 by nd is the same as multiplying it by two nd’s stacked on top of each other. That’s an outer product. As you can see, outer products also require fewer operations, since they don’t combine two products into one element in the final matrix.
A few aspects of ND4J code should be noted here. Firstly, the method mmul takes two parameters.
which could be expressed like this
which is the same as this line
Using the second parameter to specify the nd-array to which the product should be assigned is a convention common in ND4J.
Elementwise Operations And Basic Usage
The basic operations of linear algebra are matrix creation, addition and multiplication. This guide will show you how to perform those operations with ND4J, as well as various advanced transforms.
The Java code below will create a simple 2 x 2 matrix, populate it with integers, and place it in the nd-array variable nd:
If you print out this array
you’ll see this
A matrix with two rows and two columns, which orders its elements by column and which we’ll call matrix nd.
A matrix that ordered its elements by row would look like this:
The simplest operations you can perform on a matrix are elementwise scalar operations; for example, adding the scalar 1 to each element of the matrix, or multiplying each element by the scalar 5. Let’s try it.
This line of code represents this operation:
and here is the result
There are two ways to perform any operation in ND4J, destructive and nondestructive; i.e. operations that change the underlying data, or operations that simply work with a copy of the data. Destructive operations will have an “i” at the end – addi, subi, muli, divi. The “i” means the operation is performed “in place,” directly on the data rather than a copy, while nd.add() leaves the original untouched.
Elementwise scalar multiplication looks like this:
And produces this:
Subtraction and division follow a similar pattern:
If you perform all these operations on your initial 2 x 2 matrix, you should end up with this matrix:
When performed with simple units like scalars, the operations of arithmetic are unambiguous. But working with matrices, addition and multiplication can mean several things. With vector-on-matrix operations, you have to know what kind of addition or multiplication you’re performing in each case.
First, we’ll create a 2 x 2 matrix, a column vector and a row vector.
Notice that the shape of the two vectors is specified with their final parameters. {2,1} means the vector is vertical, with elements populating two rows and one column. A simple {2} means the vector populates along a single row that spans two columns – horizontal. You’re first matrix will look like this
Here’s how you add a column vector to a matrix:
And here’s the best way to visualize what’s happening. The top element of the column vector combines with the top elements of each column in the matrix, and so forth. The sum matrix represents the march of that column vector across the matrix from left to right, adding itself along the way.
But let’s say you preserved the initial matrix and instead added a row vector.
Then your equation is best visualized like this:
In this case, the leftmost element of the row vector combines with the leftmost elements of each row in the matrix, and so forth. The sum matrix represents that row vector falling down the matrix from top to bottom, adding itself at each level.
So vector addition can lead to different results depending on the orientation of your vector. The same is true for multiplication, subtraction and division and every other vector operation.
In ND4J, row vectors and column vectors look the same when you print them out with
They will appear like this.
Don’t be fooled. Getting the parameters right at the beginning is crucial. addRowVector and addColumnVector will not produce different results when using the same initial vector, because they do not change a vector’s orientation as row or column.
To carry out scalar and vector elementwise operations, we basically pretend we have two matrices of equal shape. Elementwise scalar multiplication can be represented several ways.
So you see, elementwise operations match the elements of one matrix with their precise counterparts in another matrix. The element in row 1, column 1 of matrix nd will only be added to the element in row one column one of matrix c.
This is clearer when we start elementwise vector operations. We imaginee the vector, like the scalar, as populating a matrix of equal dimensions to matrix nd. Below, you can see why row and column vectors lead to different sums.
Column vector:
Row vector:
Now you can see why row vectors and column vectors produce different results. They are simply shorthand for different matrices.
Given that we’ve already been doing elementwise matrix operations implicitly with scalars and vectors, it’s a short hop to do them with more varied matrices:
Here’s how you can visualize that command:
Muliplying the initial matrix nd with matrix nd4 works the same way:
These toy matrices are a useful heuristic to introduce the ND4J interface as well as basic ideas in linear algebra. This framework, however, is built to handle billions of parameters in n dimensions (and beyond…).
The term of art for this particular matrix manipulation is a .
Onnx interop Key features and brief samples.
Nd4j allows execution of models via onnx runtime using nd4j's INDArray as a data structure. Leveraging the nd4j-onnxruntime interop is fairly simple. An onnx model can be loaded and executed as follows:
This outputs a result with a map of output names to the ndarray result from onnx.
In maven, add the following dependency:
Note, this depends on javacpp's onnxruntime bindings. This means onnx runtime's native binaries are managed by javacpp. Javacpp will bundle all native binaries for all platforms by default unless you specify a platform. You can do this by specifying a -Dplatform=your-platform-of-choice. You may find more here in the javacpp docs.
TVM Key features and brief samples.
Nd4j allows execution of models via javacpp's tvm bindings using nd4j's INDArray as a data structure. Leveraging the nd4j-tvm interop is fairly simple.
A TVM model can be loaded and executed as follows:
This outputs a result with a map of output names to the ndarray result from tvm.
In maven, add the following dependency:
Note, this depends on javacpp's tvm bindings. This means tvm's native binaries are managed by javacpp. Javacpp will bundle all native binaries for all platforms by default unless you specify a platform. You can do this by specifying a -Dplatform=your-platform-of-choice. You may find more here in the javacpp docs.
Tensorflow interop Key features and brief samples.
Nd4j allows execution of models via javacpp's tensorflow bindings using nd4j's INDArray as a data structure. Leveraging the nd4j-tensorflow interop is fairly simple.
Note, this is based on tensorflow 1.x. TF java 2 will be coming at a later time.
A tensorflow model can be loaded and executed as follows:
This outputs a result with a map of output names to the ndarray result from tensorflow.
In maven, add the following dependency:
Note, this depends on javacpp's tensorflow bindings. This means tensorflow's native binaries are managed by javacpp. Javacpp will bundle all native binaries for all platforms by default unless you specify a platform. You can do this by specifying a -Dplatform=your-platform-of-choice. You may find more here in the javacpp docs.