ND4J 数组及其形状:将数据放入列表
ND4J arrays & their shapes: getting data into a list
考虑以下代码,它使用 ND4J library to create a simpler version of the "moons" test data set:
val n = 100
val n1: Int = n/2
val n2: Int = n-n1
val outerX = Nd4j.getExecutioner.execAndReturn(new Cos(Nd4j.linspace(0, Math.PI, n1)))
val outerY = Nd4j.getExecutioner.execAndReturn(new Sin(Nd4j.linspace(0, Math.PI, n1)))
val innerX = Nd4j.getExecutioner.execAndReturn(new Cos(Nd4j.linspace(0, Math.PI, n2))).mul(-1).add(1)
val innerY = Nd4j.getExecutioner.execAndReturn(new Sin(Nd4j.linspace(0, Math.PI, n2))).mul(-1).add(1)
val X: INDArray = Nd4j.vstack(
Nd4j.concat(1, outerX, innerX), // 1 x n
Nd4j.concat(1, outerY, innerY) // 1 x n
) // 2 x n
val y: INDArray = Nd4j.hstack(
Nd4j.zeros(n1), // 1 x n1
Nd4j.ones(n2) // 1 x n2
) // 1 x n
println(s"# y shape: ${y.shape().toList}") // 1x100
println(s"# y data length: ${y.data().length()}") // 100
println(s"# X shape: ${X.shape().toList}") // 2x100
println(s"# X row 0 shape: ${X.getRow(0).shape().toList}") // 1x100
println(s"# X row 1 shape: ${X.getRow(1).shape().toList}") // 1x100
println(s"# X row 0 data length: ${X.getRow(0).data().length()}") // 200 <- !
println(s"# X row 1 data length: ${X.getRow(1).data().length()}") // 100
在倒数第二行,令人惊讶的是,X.getRow(0).data().length()
是 200 而不是 100。经检查,这是因为 data()
返回的结构包含整个矩阵,即两行,连接在一起。
如何将 X 矩阵的实际第一行放入 Java(或 Scala)List
?我可以只取 200 个元素的前 100 个项目 "first row",但这似乎不太优雅。
.data() 给你一个直线。
参见:http://nd4j.org/tensor
数组的形状只是底层数据缓冲区的一个视图。
我通常不建议在没有充分理由的情况下做你想做的事情。所有数据都存储在堆外。那个副本很贵。
堆上不适合做任何类型的数学运算。这里唯一的用例是集成。我建议尽可能直接对阵列进行操作。从序列化到索引的一切都为您处理。
如果您真的需要它来进行某种集成,请使用番石榴,您可以在一行中完成:
Doubles.asList(arr.data().dup().asDouble());
其中 arr 是要操作的 ndarray。
是的,事实证明 .data()
ND4J 不应该用于任何非常严肃的事情。这对我尝试做的事情来说有点遗憾:编写不真正依赖 ND4J 及其内部处理数据方式的单元测试。
作为此处问题的另一个示例,请考虑以下代码:
import org.nd4j.linalg.factory.Nd4j
object foo extends App {
val x = Nd4j.create(Array[Double](1,2, 3,4, 5,6), Array(3,2))
// 1,2
// 3,4
// 5,6
println(x)
val xArr = x.data().asDouble().toList
// 1,2, 3,4, 5,6 - row-wise
println(xArr)
val w = Nd4j.create(Array[Double](10,20,30, 40,50,60), Array(2,3))
// 10,20,30
// 40,50,60
println(w)
val wArr = w.data().asDouble().toList
// 10,20,30, 40,50,60 - row-wise
println(wArr)
val wx = w.mmul(x)
/*
* 10,20,30 1,2 10*1+20*3+30*5 10*2+20*4+30*6 220 280
* 40,50,60 3,4 = 40*1+50*3+60*5 40*2+50*4+60*6 = 490 640
* 5,6
*/
println(wx)
val wxArr = wx.data().asDouble().toList
// 220, 490, 280, 640 - column-wise
println(wxArr)
val wxTArr = wx.transpose().data().asDouble().toList
// 220, 490, 280, 640 - still column-wise
println(wxTArr)
val wxTIArr = wx.transposei().data().asDouble().toList
// 220, 490, 280, 640 - still column-wise
println(wxTIArr)
}
如您所见,ND4J 基本上在内部做它想做的事情,当您使用 .data()
时,它只会给您它的内部表示;这种表示不会被任何转置或您要求它做的任何其他事情改变,因为它们实际上并没有移动底层数据。
一切都很好,但我想做的基本上是:制作普通双打的 Scala 列表;把它交给我的自定义库;要求图书馆做它的事;获取它的输出并将其转换为另一个 Scala 双精度列表;验证这些双打是我期望它计算的。相反,我要做的是将预期的内容放入 ND4J 数组中,以便我可以将其与实际输出进行正确比较,因此我的测试现在依赖于 ND4J,这是我的库的内部技术选择。
无论如何,这是一个相对较小的抱怨,教训是,避免 .data()
如果您使用的是 ND4J,请始终使用它(即使您认为这有点不雅)。
考虑以下代码,它使用 ND4J library to create a simpler version of the "moons" test data set:
val n = 100
val n1: Int = n/2
val n2: Int = n-n1
val outerX = Nd4j.getExecutioner.execAndReturn(new Cos(Nd4j.linspace(0, Math.PI, n1)))
val outerY = Nd4j.getExecutioner.execAndReturn(new Sin(Nd4j.linspace(0, Math.PI, n1)))
val innerX = Nd4j.getExecutioner.execAndReturn(new Cos(Nd4j.linspace(0, Math.PI, n2))).mul(-1).add(1)
val innerY = Nd4j.getExecutioner.execAndReturn(new Sin(Nd4j.linspace(0, Math.PI, n2))).mul(-1).add(1)
val X: INDArray = Nd4j.vstack(
Nd4j.concat(1, outerX, innerX), // 1 x n
Nd4j.concat(1, outerY, innerY) // 1 x n
) // 2 x n
val y: INDArray = Nd4j.hstack(
Nd4j.zeros(n1), // 1 x n1
Nd4j.ones(n2) // 1 x n2
) // 1 x n
println(s"# y shape: ${y.shape().toList}") // 1x100
println(s"# y data length: ${y.data().length()}") // 100
println(s"# X shape: ${X.shape().toList}") // 2x100
println(s"# X row 0 shape: ${X.getRow(0).shape().toList}") // 1x100
println(s"# X row 1 shape: ${X.getRow(1).shape().toList}") // 1x100
println(s"# X row 0 data length: ${X.getRow(0).data().length()}") // 200 <- !
println(s"# X row 1 data length: ${X.getRow(1).data().length()}") // 100
在倒数第二行,令人惊讶的是,X.getRow(0).data().length()
是 200 而不是 100。经检查,这是因为 data()
返回的结构包含整个矩阵,即两行,连接在一起。
如何将 X 矩阵的实际第一行放入 Java(或 Scala)List
?我可以只取 200 个元素的前 100 个项目 "first row",但这似乎不太优雅。
.data() 给你一个直线。 参见:http://nd4j.org/tensor
数组的形状只是底层数据缓冲区的一个视图。 我通常不建议在没有充分理由的情况下做你想做的事情。所有数据都存储在堆外。那个副本很贵。
堆上不适合做任何类型的数学运算。这里唯一的用例是集成。我建议尽可能直接对阵列进行操作。从序列化到索引的一切都为您处理。
如果您真的需要它来进行某种集成,请使用番石榴,您可以在一行中完成: Doubles.asList(arr.data().dup().asDouble());
其中 arr 是要操作的 ndarray。
是的,事实证明 .data()
ND4J 不应该用于任何非常严肃的事情。这对我尝试做的事情来说有点遗憾:编写不真正依赖 ND4J 及其内部处理数据方式的单元测试。
作为此处问题的另一个示例,请考虑以下代码:
import org.nd4j.linalg.factory.Nd4j
object foo extends App {
val x = Nd4j.create(Array[Double](1,2, 3,4, 5,6), Array(3,2))
// 1,2
// 3,4
// 5,6
println(x)
val xArr = x.data().asDouble().toList
// 1,2, 3,4, 5,6 - row-wise
println(xArr)
val w = Nd4j.create(Array[Double](10,20,30, 40,50,60), Array(2,3))
// 10,20,30
// 40,50,60
println(w)
val wArr = w.data().asDouble().toList
// 10,20,30, 40,50,60 - row-wise
println(wArr)
val wx = w.mmul(x)
/*
* 10,20,30 1,2 10*1+20*3+30*5 10*2+20*4+30*6 220 280
* 40,50,60 3,4 = 40*1+50*3+60*5 40*2+50*4+60*6 = 490 640
* 5,6
*/
println(wx)
val wxArr = wx.data().asDouble().toList
// 220, 490, 280, 640 - column-wise
println(wxArr)
val wxTArr = wx.transpose().data().asDouble().toList
// 220, 490, 280, 640 - still column-wise
println(wxTArr)
val wxTIArr = wx.transposei().data().asDouble().toList
// 220, 490, 280, 640 - still column-wise
println(wxTIArr)
}
如您所见,ND4J 基本上在内部做它想做的事情,当您使用 .data()
时,它只会给您它的内部表示;这种表示不会被任何转置或您要求它做的任何其他事情改变,因为它们实际上并没有移动底层数据。
一切都很好,但我想做的基本上是:制作普通双打的 Scala 列表;把它交给我的自定义库;要求图书馆做它的事;获取它的输出并将其转换为另一个 Scala 双精度列表;验证这些双打是我期望它计算的。相反,我要做的是将预期的内容放入 ND4J 数组中,以便我可以将其与实际输出进行正确比较,因此我的测试现在依赖于 ND4J,这是我的库的内部技术选择。
无论如何,这是一个相对较小的抱怨,教训是,避免 .data()
如果您使用的是 ND4J,请始终使用它(即使您认为这有点不雅)。