知识图谱

DL4J中文文档/ND4J/概述

2019-01-26  本文已影响37人  hello风一样的男子

本用户指南旨在解释(并提供示例)ND4J中的主要功能。

简介

NDArray本质上是一个n维数组:即一个具有一定维数的数字矩形数组。

一些你应该熟悉的概念:

就索引而言这里有一些事情需要知道。首先,维度0是行,维度1是列:因此INDArray.size(0)是行的数量,INDArray.size(1)是列的数量,索引是0开始的:因此行有从0到INDArray.size(0)-1的索引,对于其他维度,依此类推。

在本文档中,我们将使用NDArray术语来描述n维数组的一般概念;术语NDArray具体指的是ND4J定义的Java接口。实际上,这两个术语可以互换使用。

NDArrays:它们在内存中是如何存储的?

接下来的几段描述了ND4J背后的一些架构。理解这一点并不是使用ND4J所必需的,但它可能有助于你了解幕后发生的事情。NDArrays作为一个单一的扁平的数字数组(或者更一般地说,作为单个连续的内存块)存储在内存中,因此与典型的Java多维数组(例如,浮点[]][2] [][][][]有很大的不同。

物理上,INDArray背后的数据是堆外存储的:也就是说,它存储在Java虚拟机(JVM)之外。这具有许多优点,包括性能、与高性能BLAS库的互操作性以及避免JVM在高性能计算中的一些缺点(例如,由于整数索引,Java数组限于2 ^ 31 - 1(21亿4000万)个元素)。

在编码方面,可以按C(行主要)或Fortran(列主要)顺序对NDArray进行编码。有关行与列主顺序的更多详细信息,请参阅维基百科。ND4J可以同时使用C和F顺序数组的组合。大多数用户只能使用默认的数组排序,但请注意,如果需要,可以对给定的数组使用特定的排序。

下图显示了简单的3x3(2d)NDArray是如何存储在内存中的,

C vs. F order image.gif

在如下的数组,我们有:

视图:当两个或更多NDArrays引用相同的数据

NDArrays中的一个关键概念是两个NDArrays实际上可以指向内存中相同的底层数据。通常,我们有一个数组引用另一个数组的某个子集,而这只发生在某些操作(如INDArray.get(), INDArray.transpose(), INDArray.getRow()等)中。这是一个强大的概念,值得理解。

这主要有两个动机:

  1. 有相当大的性能优势,尤其是在避免复制数组方面
  2. 我们在NDArrays上如何执行操作方面获得了很大的权力。

考虑一个简单的操作,比如一个大型(10000x1000)矩阵上的矩阵转置。使用视图,我们可以在不执行任何复制的情况下(即,大O标记法中的O(1))在恒定时间内执行矩阵转置,从而避免复制所有数组元素的相当大的成本。当然,有时我们确实想制作一份副本-这时我们可以使用INDArray.dup()来获取副本。例如,要获得转置矩阵的副本,请使用INDArray out = myMatrix.transpose().dup()。在这个dup()调用之后,原始数组myMatrix和数组out之间将没有链接(因此,对其中一个的更改不会影响另一个)。

所以看看视图是如何强大的,考虑一个简单的任务:将1.0添加到更大数组的第一行,myarray。我们可以很容易地做到这一点,在一行中:

myArray.getRow(0).addi(1.0)

让我们来分析一下这里发生的事情。首先,getRow(0)操作返回一个INDArray,是原始数据的视图。注意myarray和 myArray.getRow(0)都指向内存中的相同区域:

getRow(0) image.gif

然后,在addi(1.0)被执行后,我们有如下状态

getRow(0).addi(1.0) image.gif

如我们所见,对myArray.getRow(0)返回的NDArray的更改将反映在原始数组myArray中;同样,对myArray的更改将反映在行向量中。

创建NDArrays

0,1和标量值初始化数组

创建数组最常用的两种方法是:

数组的形状被指定为整数。例如,要创建一个由3行5列组成的零填充数组,请使用 Nd4j.zeros(3,5)

这些通常可以与其他操作结合使用其他值创建数组。例如,要创建一个填充了10的数组:

INDArray tens = Nd4j.zeros(3,5).addi(10)

上面的初始化工作分为两个步骤:首先分配一个3x5数组,用零填充,然后向每个值加10。

随机数组

Nd4j提供了一些创建INDArrays的方法,其中内容是伪随机数。

要在0到1范围内生成统一的随机数,请使用Nd4j.rand(int nRows, int nCols)(对于二维数组)或Nd4j.rand(int[]) (对于3个或更多维度)。

同样,要生成平均零和标准偏差为1的高斯随机数,请使用 Nd4j.randn(int nRows, int nCols) or Nd4j.randn(int[])。

对于可重复性(即设置nd4j的随机数生成器种子),可以使用Nd4j.getRandom().setSeed(long)

从Java数组创建NDArrays

ND4J为从Java float和double组创建数组提供了方便的方法。

要从1D Java数组创建1D NDArray,请使用:

对于 2维数组, 使用 Nd4j.create(float[][])Nd4j.create(double[][]).

为了从具有3个或多个维度的Java原始数组(double [][][]等)创建NDArrays,一种方法是使用以下:

double[] flat = ArrayUtil.flattenDoubleArray(myDoubleArray);
int[] shape = ...;  //这是数组形状
INDArray myArr = Nd4j.create(flat,shape,'c');

image.gif

从其它NDArrays创建NDArrays

从其它数组创建数组的方法主要有三种:

对于第二种情况,您可以使用getRow(), get()等。有关详细信息,请参见获取和设置NDArrays的部分。

合并NDArrays的两种方法是Nd4j.hstack(INDArray...)Nd4j.vstack(INDArray...)

hstack(水平堆叠)将具有相同行数的多个矩阵作为参数,并将它们水平堆叠以生成新的数组。但是,输入NDArrays可以有不同数量的列。

示例:

int nRows = 2;
int nColumns = 2;
//创建值为0的INDArray
INDArray zeros = Nd4j.zeros(nRows, nColumns);
//创建所有值为1的INDArray 
INDArray ones = Nd4j.ones(nRows, nColumns);
//水平堆叠
INDArray hstack = Nd4j.hstack(ones,zeros);
System.out.println("### HSTACK ####");
System.out.println(hstack);

image.gif

输出:

### HSTACK ####
[[1.00, 1.00, 0.00, 0.00],
[1.00, 1.00, 0.00, 0.00]]

image.gif

vstack (垂直堆叠) 是hstack的垂直等效值。输入数组的列数必须相同。

示例:

int nRows = 2;
int nColumns = 2;
// 创建值为0的INDArray
INDArray zeros = Nd4j.zeros(nRows, nColumns);
// 创建所有值为1的INDArray
INDArray ones = Nd4j.ones(nRows, nColumns);
//垂直堆叠
INDArray vstack = Nd4j.vstack(ones,zeros);
System.out.println("### VSTACK ####");
System.out.println(vstack);

image.gif

输出:

### VSTACK ####
[[1.00, 1.00],
 [1.00, 1.00],
 [0.00, 0.00],
 [0.00, 0.00]]

image.gif

ND4J.concat 沿维度组合数组。

示例:

int nRows = 2;
int nColumns = 2;
//创建一个所有值为0的INDArray
INDArray zeros = Nd4j.zeros(nRows, nColumns);
//创建一个所有值为1的INDArray
INDArray ones = Nd4j.ones(nRows, nColumns);
// 在0维上连接
INDArray combined = Nd4j.concat(0,zeros,ones);
System.out.println("### COMBINED dimension 0####");
System.out.println(combined);
//在1维上连接
INDArray combined2 = Nd4j.concat(1,zeros,ones);
System.out.println("### COMBINED dimension 1 ####");
System.out.println(combined2);

image.gif

输出:

### COMBINED dimension 0####
[[0.00, 0.00],
 [0.00, 0.00],
 [1.00, 1.00],
 [1.00, 1.00]]
### COMBINED dimension 1 ####
[[0.00, 0.00, 1.00, 1.00],
 [0.00, 0.00, 1.00, 1.00]]

image.gif

ND4J.pad 用于填充数组

示例:

int nRows = 2;
int nColumns = 2;
// 创建一个所有值为1的INDArray
INDArray ones = Nd4j.ones(nRows, nColumns);
// 填充INDArray
INDArray padded = Nd4j.pad(ones, new int[]{1,1}, Nd4j.PadMode.CONSTANT );
System.out.println("### Padded ####");
System.out.println(padded);

image.gif

输出:

### Padded ####
[[0.00, 0.00, 0.00, 0.00],
 [0.00, 1.00, 1.00, 0.00],
 [0.00, 1.00, 1.00, 0.00],
 [0.00, 0.00, 0.00, 0.00]]

image.gif

另一种偶尔有用的方法是Nd4j.diag(INDArray in)。此方法有两种用途,具体取决于中的参数:

混杂的NDArray创建方法

要创建大小为N的单位矩阵(对角线上的值都为1,其它值都为零),可以使用Nd4j.eye(N)

要使用元素[a, a+1, a+2, ..., b]创建行向量,可以使用linspace命令:

Nd4j.linspace(a, b, b-a+1)

Linspace可以与reshape操作结合使用以获得其他形状。例如,如果要一个包含5行和5列、值为1到25(包括1到25)的二维NDArray,可以使用以下内容:

Nd4j.linspace(1,25,25).reshape(5,5)

获取和设置单个值

对于INDArray,可以使用要获取或设置的元素的索引来获取或设置值。对于维度为n数组(即具有n个维度的数组),需要n个索引。

注意:单独获取或设置值(例如,在for循环中一次一个值)在性能方面通常是一个坏主意。如果可能,尝试使用其他一次对大量元素进行操作的INDArray方法。

要从二维数组中获取值,可以使用:INDArray.getDouble(int row, int column)

对于任何维度的数组,可以使用INDArray.getDouble(int...)。例如,要获取索引i、j、k处的值,请使用 INDArray.getDouble(i,j,k)

要设置值,请使用putScalar方法之一:

这里,int[]是索引,double/float/int是要放置在该索引上的值。

在某些情况下可能有用的一些附加功能是NDIndexIterator类。NDIndexIterator允许你按定义的顺序获取索引(具体来说,对于rank为3的数组,C顺序遍历顺序为[0,0,0]、[0,0,1]、[0,0,2]、…、[0,1,0]、…等)。

要迭代二维数组中的值,可以使用:

NdIndexIterator iter = new NdIndexIterator(nRows, nCols);
while (iter.hasNext()) {
    int[] nextIndex = iter.next();
    double nextVal = myArray.getDouble(nextIndex);
    //用这个值做些事情
}

image.gif

获取和设置NDArrays的部分值

getRow() 与 putRow()

要从INDArray中获取单行,可以使用INDArray.getRow(int)。这显然会返回一个行向量。这里需要注意的是,这一行是一个视图:对返回行的更改将影响原始数组。这有时非常有用(例如:myArr.getRow(3).addi(1.0) 将1.0添加到较大数组的第三行);如果需要行的副本,请使用getRow(int).dup()

同样,要获取多行,请使用INDArray.getRows(int...)。这将返回一个行已堆叠的数组;但是请注意,这将是原始行的副本(而不是视图),由于NDArrays存储在内存中的方式,因此此处不可能有视图。

对于设置单行,可以使用 myArray.putRow(int rowIdx,INDArray row)。这会将myarray的rowidxth行设置为包含在indarray行中的值。

子数组 : get(), put() 与 NDArrayIndex

Get:

更强大和通用的方法是使用INDArray.get(NDArrayIndex...)。此功能允许你基于某些索引获取任意子数组。这也许最好用一些例子来解释:

要获取单行(和所有列),可以使用:
myArray.get(NDArrayIndex.point(rowIdx), NDArrayIndex.all())

要获取行(行A(含)到行(不含)和所有列的范围,可以使用:
myArray.get(NDArrayIndex.interval(a,b), NDArrayIndex.all())

要获取所有行和第二列,可以使用:
myArray.get(NDArrayIndex.all(),NDArrayIndex.interval(0,2,nCols))

尽管上面的示例仅适用于二维数组,但NDArrayIndex方法扩展到3个或更多维度。对于三维,你将提供3个NDArrayIndex对象,而不是两个,如上所述。
请注意,NDArrayIndex.interval(...), .all().point(int)方法始终返回底层数组的视图。因此,.get()返回的数组更改将反映在原始数组中。

Put:

同样的NDArrayIndex方法也用于将元素放入另一个数组:在本例中,你使用INDArray.put(INDArrayIndex[], INDArray toPut)方法。显然,NDArray toput的大小必须与提供的索引所暗示的大小匹配。

还要注意myArray.put(NDArrayIndex[],INDArray other)在功能上等同于执行myArray.get(INDArrayIndex...).assign(INDArray other)。同样,这是因为.get(INDArrayIndex...)返回底层数组的视图,而不是副本。

沿维张量

(注:与当前版本相比,0.4-rc3.8及更早版本的ND4J返回的沿维度张量结果略有不同)。

沿维张量是一种强大的技术,但一开始可能有点难以理解。沿维张量(以下简称为TAD)背后的思想是得到一个低阶子数组,它是原始数组的视图。

“沿维张量”方法采用两个参数:

The simplest case is a tensor along a single row or column of a 2d array. Consider the following diagram (where dimension 0 (rows) are indexed going down the page, and dimension 1 (columns) are indexed going across the page):

最简单的情况是沿二维数组的单个行或列的张量。考虑下面的关系图(其中维度0(行)在页面下方被索引,维度1(列)在页面上方被索引):

Tensor Along Dimension image.gif

注意,在所有情况下,一个维的tensorAlongDimension调用的输出都是行向量。

为了理解我们为什么得到这个输出:考虑上图中的第一个案例。在那里,我们取第0(第一)个张量沿维数0(维数0为行);当我们沿维数0移动时,值(1,5,2)在一条线上,因此输出。同样地,tensorAlongDimension(1,1)是第二个(index=1)沿维数1的张量;当我们沿维数1移动时,值(5,3,5)在一条线上。

TAD操作也可以沿多个维度执行。例如,通过指定两个维度来执行TAD操作,我们可以使用它从3D(或4D或5D…)数组中获取二维子数组。同样,通过指定3个维度,我们可以使用它从4d或更高维度获得3d。

有两件事我们需要知道的输出,为TAD操作是有用的。

首先,我们需要我们能得到的张量的数量,对于一组给定的维度。为了确定这一点,我们可以使用“沿维度的张量数量”方法,即INDArray.tensorssAlongDimension(int... dimensions)。此方法只返回沿指定维度的张量数。在上面的例子中,我们有:

(在后两种情况下,请注意,沿维度的张量将提供与原始数组相同的数组输出,即,我们从二维数组获得二维输出)。

一般来说,张量的个数是由剩余尺寸的乘积给出的,张量的形是由原形状中指定维度的大小给出的。

以下是一些例子:

切分

[本节:即将到来。]

在NDArrays上进行操作

ND4J有操作的概念,你可能想用一个INDArray来做(或去做)很多事情。例如,ops用于应用tanh操作、添加标量或执行元素操作。

ND4J定义了五种类型的操作:

以及两种执行方法:

在讨论这些操作的细节之前,让我们先考虑一下就地和复制操作之间的区别。

许多操作既有就地操作,也有复制操作。假设我们要添加两个数组。Nd4j为此定义了两种方法:INDArray.add(INDArray)INDArray.addi(INDArray)。前者(add)是一个复制操作;后者是一个就地操作-i在addi中表示就地操作。此约定(…i表示就地,没有 i表示复制)适用于通过INDArray接口可访问的其他操作。

假设我们有两个INDArrays x和y,我们做INDArray z = x.add(y)INDArray z = x.addi(y)操作。这些操作的结果如下所示。

Add image.gif

Addi image.gif

注意,使用x.add(y)操作时,不会修改原始数组x。相比之下,对于就地版本x.addi(y),数组x被修改。在添加操作的两个版本中,都会返回包含结果的INDArray。但是请注意,在addi操作的情况下,结果数组us实际上只是原始数组x。

标量操作

标量操作是一种元素操作,它也接受一个标量(即一个数字)。标量操作的例子有add、max、multiple、set和divide操作(有关完整列表,请参见上一链接)。

许多方法,例如INDArray.addi(Number)INDArray.divi(Number)实际上在后台执行标量操作,因此,在可用时,使用这些方法更方便。

要更直接地执行标量操作,可以使用以下示例:

Nd4j.getExecutioner().execAndReturn(new ScalarAdd(myArray,1.0))

请注意,myarray是通过此操作修改的。如果这不是你想要的,请使用 myArray.dup()

与其余的操作不同,标量操作没有对沿维度执行它们的合理解释。

转换操作

转换操作是诸如元素方向的对数、余弦、tanh、校正线性等操作。其他示例包括加法、减法和复制操作。转换操作通常以元素为导向的方式使用(例如,对每个元素使用tanh),但情况并非总是如此——例如,softmax通常沿维度执行。

要直接(在完整的NDArray上)执行元素相关的tanh操作,可以使用:

INDArray tanh = Nd4j.getExecutioner().execAndReturn(new Tanh(myArr))与上面提到的标量操作一样,使用上面方法的转换操作是就地操作:即修改了NDArray myArr,并且返回的数组tanh实际上是与输入myarr相同的对象。同样,如果需要副本,可以使用myArr.dup()

Transforms类还定义了一些方便的方法,例如:INDArray tanh = Transforms.tanh(INDArray in,boolean copy);这相当于使用 Nd4j.getExecutioner()的方法。

累加(缩减)操作

在执行累加时,在整个NDArray上执行累加与在特定维度(或维度)上执行累加之间有一个关键区别。在第一种情况下(对整个数组执行),只返回一个值。在第二种情况下(沿维度累加),将返回一个新的NDArray。

要获取数组中所有值的总和,请执行以下操作:

double sum = Nd4j.getExecutioner().execAndReturn(new Sum(myArray)).getFinalResult().doubleValue();

或同等(更方便)

double sum = myArray.sumNumber().doubleValue();

还可以沿维度执行累加操作。例如,要获取每列中所有值的总和(每列=沿维度0或“每行中的值”),可以使用:

INDArray sumOfColumns = Nd4j.getExecutioner().exec(new Sum(myArray),0);

或同等,

INDArray sumOfColumns = myArray.sum(0)

假设这是在3x3输入数组上执行的。从视觉上看,这个维度0操作的求和操作如下:

Sum along dimension 0 image.gif

请注意,这里输入的形状为[3,3](3行,3列),输出的形状为[1,3](即,我们的输出是行向量)。如果我们改为沿着维度1进行操作,我们将得到一个形状为[3,1]且值为(12,13,11)的列向量。

沿维度的累加也概括为具有3个或更多维度的NDArrays。

索引累加操作

索引累加操作与累加操作非常相似。区别在于它们返回的是一个整数索引,而不是一个双精度值。
索引累加操作的示例有IMax(argmax)、IMin(argmin)和IAMax(绝对值的argmax)。
要获取数组中最大值的索引,请执行以下操作:

int idx = Nd4j.getExecutioner().execAndReturn(new IAMax(myArray)).getFinalResult();

索引累加操作通常在沿维度执行时最有用。例如,要获取每列(每列=沿维度0)中最大值的索引,可以使用:

INDArray idxOfMaxInEachColumn = Nd4j.getExecutioner().exec(new IAMax(myArray),0);

假设这是在3x3输入数组上执行的。从视觉上看,沿维度0操作的argmax/IAMax操作如下:

Argmax / IAMax image.gif

与上述累加运算一样,输出的形状为[1,3]。同样,如果我们沿着维度1进行操作,我们将得到一个形状为[3,1],值为(1,0,2)的列向量。

广播与向量操作

ND4J还定义了广播和向量操作。
一些更有用的操作是向量操作,如addRowVector和muliColumnVector。
例如,考虑操作 x.addRowVector(y),其中x是矩阵,而y是行向量。在这种情况下,addRowVector操作将行向量y添加到矩阵x的每一行,如下所示。

addRowVector image.gif

与其他操作一样,有就地和复制版本。这些操作还有列-列版本,例如addColumnVector,它将列向量添加到原始INDArray的每一列。

布尔索引:根据条件有选择地应用操作

[本节:即将到来]

Link: Boolean Indexing Unit Tests

工作间

工作间是ND4J的一个特性,通过更有效的内存分配和管理,可以提高性能。具体来说,工作空间是为周期性工作负载而设计的,例如训练神经网络,因为它们允许堆外内存重用(而不是在循环的每次迭代中持续分配和释放内存)。净效果是提高性能和减少内存使用。
有关工作间的详细信息,请参见以下链接:

工作空间:范围恐慌

有时,对于工作区,你可能会遇到一个异常,例如:

org.nd4j.linalg.exception.ND4JIllegalStateException: Op [set] Y argument uses leaked workspace pointer from workspace [LOOP_EXTERNAL]
For more details, see the ND4J User Guide: nd4j.org/userguide#workspaces-panic

image.gif

org.nd4j.linalg.exception.ND4JIllegalStateException: Op [set] Y argument uses outdated workspace pointer from workspace [LOOP_EXTERNAL]
For more details, see the ND4J User Guide: nd4j.org/userguide#workspaces-panic

image.gif

理解范围恐慌异常

简而言之:这些异常意味着在工作区中分配的INDArray被错误地使用(例如,缺陷或某些方法的错误实现)。这有两个原因:

  1. INDArray已从定义的工作区“泄漏”出去。
  2. INDArray在正确的工作区内使用,但来自上一次迭代。

在这两种情况下,INDArray指向的底层堆外内存都已失效,无法再使用。
导致工作区泄漏的事件序列示例:

  1. 工作间 W 被打开
  2. INDArray X 分配在了工作间W
  3. 工作间 W 被关闭, 因此X的内存不再有效。
  4. INDArray X在某些操作中使用,导致异常。

导致过期工作间指针的事件序列示例:

  1. 工作间 W被打开 (第1次迭代)
  2. INDArray X 在工作间W中分配 (第1次迭代)
  3. 工作间 W 被关闭 (第1次迭代)
  4. 工作间 W被打开 (第2次迭代)
  5. INDArray X (来自第1次迭代) 在其它操作中被使用,抛出异常。

范围恐慌异常的解决方法和修复方法

根据原因,有两种基本解决方案。
第一。如果已经实现了一些自定义代码(或者正在手动使用工作间),这通常表示代码中存在错误。通常,您有两种选项:

  1. 使用INDArray.detach()方法从所有工作间分离INDArray。其结果是,返回的数组不再与工作区关联,可以在任何工作区内部或外部自由使用。
  2. 首先不要在工作间中分配数组。您可以使用:try(MemoryWorkspace scopedOut = Nd4j.getWorkspaceManager().scopeOutOfWorkspaces()){ <your code here> }暂时“关闭”工作间。其结果是,try块中的任何新数组(例如,通过nd4j.create创建的)都不会与工作区关联,并且可以在工作区之外使用。
  3. 使用INDArray.leverage()leverageTo(String)migrate()方法之一,将数组移动/复制到父工作间。有关更多详细信息,请参见这些方法的javadoc。

第二,如果你使用工作间为Deeplearning4j的一部分,并且没有实现任何自定义功能(即,你没有编写自己的层、数据管道等),那么(在遇到这种情况的时候),这很可能表示底层库中存在一个bug,通常应该通过GitHub报告问题。一个可能的解决方法是使用以下代码禁用工作区:

.trainingWorkspaceMode(WorkspaceMode.NONE)
.inferenceWorkspaceMode(WorkspaceMode.NONE)

image.gif

如果异常是由于数据管道中的问题造成的,则可以在AsyncShieldDataSetIteratorAsyncShieldMultiDataSetIterator中尝试包装 DataSetIteratorMultiDataSetIterator

无论是哪种原因,如果你确定你的代码是正确的,最后的解决方案是尝试禁用范围恐慌。请注意,这是不推荐的,如果存在合法问题,可能会导致JVM崩溃。为了实现禁用范围恐慌,请在执行代码之前使用Nd4j.getExecutioner().setProfilingMode(OpExecutioner.ProfilingMode.DISABLED)

高级和混杂话题

设置数据类型

ND4J目前允许用浮点或双精度值来支持INDArrays。默认值为单精度(浮点)。要将nd4j全局用于数组的顺序设置为双精度,可以使用:

Nd4j.setDataType(DataBuffer.Type.DOUBLE);

image.gif

注意,这应该在使用ND4J操作或创建数组之前完成。
或者,可以在启动JVM时设置属性:

-Ddtype=double

image.gif

变形

[本节:即将到来]

扁平化

扁平化是给定数组的一些遍历顺序,将一个或多个INDArrays转换为单个平面数组(行向量)的过程。
ND4J为此提供了以下方法:

Nd4j.toFlattened(char order, INDArray... arrays)
Nd4j.toFlattened(char order, Collection<INDArray>)

image.gif

Nd4j还提供了带有默认顺序的重载toFlattened方法。参数order必须是“c”或“f”,并定义从数组中获取值的顺序:参数order为c导致使用数组索引对数组进行扁平化,其顺序为[0,0,0]、[0,0,1]等(对于三维数组),而参数order为f导致按[0,0,0]、[1,0,0]等顺序获取值。

置换

[本节:即将到来]

sortRows/sortColumns

[本节:即将到来]

直接访问BLAS操作

[本节:即将到来]

系列化

ND4J提供了许多格式的INDArrays序列化。下面是一些二进制和文本序列化的示例:

import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.serde.binary.BinarySerde;

import java.io.*;
import java.nio.ByteBuffer;

INDArray arrWrite = Nd4j.linspace(1,10,10);
INDArray arrRead;

//1\. 二进制格式
//   Close the streams manually or use try with resources.
try (DataOutputStream sWrite = new DataOutputStream(new FileOutputStream(new File("tmp.bin")))) {
    Nd4j.write(arrWrite, sWrite);
    }

try (DataInputStream sRead = new DataInputStream(new FileInputStream(new File("tmp.bin")))) {
    arrRead = Nd4j.read(sRead);
    }

//2.使用java.nio.ByteBuffer的二进制格式;
ByteBuffer buffer = BinarySerde.toByteBuffer(arrWrite);
arrRead = BinarySerde.toArray(buffer);

//3\. 文本格式
Nd4j.writeTxt(arrWrite, "tmp.txt");
arrRead = Nd4j.readTxt("tmp.txt");

// 读取csv格式:
// 被声明的writeNumpy方法 
arrRead =Nd4j.readNumpy("tmp.csv", ", ");

image.gif

The nd4j-serde directory provides packages for Aeron, base64, camel-routes, gsom, jackson and kryo.

速查:ND4J方法概述

本节以摘要形式列出了ND4J中最常用的操作。关于其中大部分的更多详细信息,可以在本页后面找到。
在本节中,假设arr、arr1等为INDArrays。

创建NDArrays:

确定INDArray的大小/尺寸:

以下方法由INDArray接口定义:

获取和设置单个值:

标量操作:标量操作接受一个double/float/int值,并对每个值执行一个操作,就像元素操作一样,有就地操作和复制操作。

元素操作:注意:有复制(添加、mul等)和就地(addi、muli)操作。前者:arr1未修改。后者中:arr1被修改

缩减操作(SUM等);请注意,这些操作对整个数组进行操作。调用.doubleValue()从返回的数字中获取一个双精度值。

线性代数操作:

获取更大的NDArray的一部分:注意:所有这些方法都返回

元素转换 (Tanh, Sigmoid, Sin, Log etc):

常见问题解答:常见问题

Q: ND4J支持稀疏数组吗?

目前:不支持,未来计划支持。

Q: 是否可以动态增大或缩小INDArray上的大小?

在当前版本的ND4J中,这是不可能的。不过,我们将来可能会添加此功能。

有两种可能的解决办法:

  1. 分配一个新数组并进行复制(例如.put()操作)
  2. 最初,预先分配一个大于所需的NDArray,然后对该数组的视图进行操作。然后,由于你需要一个更大的数组,请在原始预分配的数组上获得更大的视图。
上一篇下一篇

猜你喜欢

热点阅读