线性代数的基本运算是矩阵的生成、加法和乘法。本指南将向你展示如何使用ND4J以及各种高级转换执行这些操作。
下面的Java代码将创建一个简单的2×2矩阵,用整数填充它,并将其放置在ND数组变量ND:
INDArray nd = Nd4j.create(new float[]{1,2,3,4},new int[]{2,2});
如果你打印出这个数组
System.out.println(nd);
你将看到这个
[[1.0 ,3.0]
[2.0 ,4.0]
]
有两行两列的矩阵,按列排列元素,我们称之为矩阵nd。
按行排列其元素的矩阵如下所示:
[[1.0 ,2.0]
[3.0 ,4.0]
]
可以对矩阵执行的最简单的操作是元素级标量操作;例如,将标量1添加到矩阵的每个元素,或将每个元素乘以标量5。让我们试试。
nd.add(1);
此行代码表示此操作:
[[1.0 + 1 ,3.0 + 1]
[2.0 + 1,4.0 + 1]
]
这里是结果
[[2.0 ,4.0]
[3.0 ,5.0]
]
在ND4J中执行任何操作有两种方法:破坏性和非破坏性;即更改基础数据的操作,或仅使用数据副本的操作。破坏性操作的最后会有一个“i”——addi, subi, muli, divi。“i”表示直接对数据执行“就地”操作,而不是对副本操作,而nd.add()则保持原始操作不变。
元素级标量乘法如下:
nd.mul(5);
然后结果如下:
[[10.0 ,20.0]
[15.0 ,25.0]
]
减法和除法遵循类似的模式:
nd.subi(3);
nd.divi(2);
如果在初始的2 x 2矩阵上执行所有这些操作,则应以该矩阵结束:
[[3.5 ,8.5]
[6.0 ,11.0]
]
当使用简单的单位(如scalars)执行时,算术运算是明确的。但是使用矩阵、加法和乘法可以意味着一些事情。对于矩阵上的向量运算,你必须知道在每种情况下执行的是哪种加法或乘法。
首先,我们将创建一个2 x 2矩阵,一个列向量和一个行向量。
INDArray nd = Nd4j.create(new float[]{1,2,3,4},new int[]{2,2});
INDArray nd2 = Nd4j.create(new float[]{5,6},new int[]{2,1}); //列向量
INDArray nd3 = Nd4j.create(new float[]{5,6},new int[]{2}); //行向量
请注意,这两个向量的形状是用它们的最终参数指定的。{2,1}表示向量是垂直的,元素填充两行一列。简单{2}表示向量沿跨越两列的单行填充–水平。
你的第一个矩阵看起来像这个样子:
[[1.00, 2.00],
[3.00, 4.00]]
下面是如何将列向量添加到矩阵中:
nd.addColumnVector(nd2);
这是最好的可视化方式。列向量的顶部元素与矩阵中每个列的顶部元素组合,依此类推。求和矩阵表示该列向量从左到右穿过矩阵的行距,并沿此方向添加自身。
[1.0 ,2.0] [5.0] [6.0 ,7.0]
[3.0 ,4.0] + [6.0] = [9.0 ,10.0]
但是假设你保留了初始矩阵,并且是添加了一个行向量。
nd.addRowVector(nd3);
那么你的等式最好的可视化看起来是这样的:
[1.0 ,2.0] [6.0 ,8.0]
[3.0 ,4.0] + [5.0 ,6.0] = [8.0 ,10.0]
在这种情况下,行向量的最左边的元素与矩阵中每行的最左边的元素结合在一起,以此类推。求和矩阵表示从上到下沿矩阵下降的行向量,并在每个层次添加自己。
因此矢量相加可以根据矢量的方向产生不同的结果。对于乘法、减法、除法和其他所有的向量运算也是如此。
在nd4j中,行向量和列向量在打印时看起来是一样的
System.out.println(nd);
它们看起来是这样的
[5.0 ,6.0]
别被愚弄了。在开始时获得正确的参数是至关重要的。当使用相同的初始向量时,addRowVector和addColumnVector不会产生不同的结果,因为它们不会更改作为行或列向量的方向。
为了执行标量和向量元素操作,我们基本上假设有两个形状相同的矩阵。元素的标量乘法可以用几种方法表示。
[1.0 ,3.0] [c , c] [1.0 ,3.0] [1c ,3c]
c * [2.0 ,4.0] = [c , c] * [2.0 ,4.0] = [2c ,4c]
所以你看到,元素级操作将一个矩阵的元素与其在另一个矩阵中的精确对应元素相匹配。在1行1列的矩阵nd中的元素,将只添加到1行1列的c矩阵的元素中。
当我们启动元素级向量操作这是明确的。我们想象向量,像标量一样,填充一个与矩阵nd维数相等的矩阵。下面,你可以看到为什么行和列向量会导致不同的和。
列向量:
[1.0 ,3.0] [5.0] [1.0 ,3.0] [5.0 ,5.0] [6.0 ,8.0]
[2.0 ,4.0] + [6.0] = [2.0 ,4.0] + [6.0 ,6.0] = [8.0 ,10.0]
行向量:
[1.0 ,3.0] [1.0 ,3.0] [5.0 ,6.0] [6.0 ,9.0]
[2.0 ,4.0] + [5.0 ,6.0] = [2.0 ,4.0] + [5.0 ,6.0] = [7.0 ,10.0]
现在你可以看到为什么行向量和列向量产生不同的结果。它们只是不同矩阵的简写。
考虑到我们已经用标量和向量隐式地进行了元素矩阵运算,用更为多样的矩阵进行这些运算只需很短的时间:
INDArray nd4 = Nd4j.create(new float[]{5,6,7,8},new int[]{2,2});
nd.add(nd4);
以下是如何可视化该命令:
[1.0 ,3.0] [5.0 ,7.0] [6.0 ,10.0]
[2.0 ,4.0] + [6.0 ,8.0] = [8.0 ,12.0]
将初始矩阵nd与矩阵nd4相乘的工作方式相同:
nd.muli(nd4);
[1.0 ,3.0] [5.0 ,7.0] [5.0 ,21.0]
[2.0 ,4.0] * [6.0 ,8.0] = [12.0 ,32.0]
这种特殊的矩阵操作的术语是哈达马积。
这些小矩阵是一个有用的启发式的介绍ND4J接口以及线性代数的基本思想。然而,这个框架的构建是为了处理N维(甚至更高)上的数十亿个参数。
接下来,我们将讨论更复杂的矩阵运算。