java ND4J阵列及其形状:将数据放入列表

nuypyhwy  于 2023-10-14  发布在  Java
关注(0)|答案(3)|浏览(108)

考虑下面的代码,它使用ND4J library来创建一个更简单版本的“moons”测试数据集:

  1. val n = 100
  2. val n1: Int = n/2
  3. val n2: Int = n-n1
  4. val outerX = Nd4j.getExecutioner.execAndReturn(new Cos(Nd4j.linspace(0, Math.PI, n1)))
  5. val outerY = Nd4j.getExecutioner.execAndReturn(new Sin(Nd4j.linspace(0, Math.PI, n1)))
  6. val innerX = Nd4j.getExecutioner.execAndReturn(new Cos(Nd4j.linspace(0, Math.PI, n2))).mul(-1).add(1)
  7. val innerY = Nd4j.getExecutioner.execAndReturn(new Sin(Nd4j.linspace(0, Math.PI, n2))).mul(-1).add(1)
  8. val X: INDArray = Nd4j.vstack(
  9. Nd4j.concat(1, outerX, innerX), // 1 x n
  10. Nd4j.concat(1, outerY, innerY) // 1 x n
  11. ) // 2 x n
  12. val y: INDArray = Nd4j.hstack(
  13. Nd4j.zeros(n1), // 1 x n1
  14. Nd4j.ones(n2) // 1 x n2
  15. ) // 1 x n
  16. println(s"# y shape: ${y.shape().toList}") // 1x100
  17. println(s"# y data length: ${y.data().length()}") // 100
  18. println(s"# X shape: ${X.shape().toList}") // 2x100
  19. println(s"# X row 0 shape: ${X.getRow(0).shape().toList}") // 1x100
  20. println(s"# X row 1 shape: ${X.getRow(1).shape().toList}") // 1x100
  21. println(s"# X row 0 data length: ${X.getRow(0).data().length()}") // 200 <- !
  22. 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个元素,但这看起来并不优雅。

ev7lccsx

ev7lccsx1#

.data()给你一个直线。参见:http://nd4j.org/tensor
数组的形状只是底层数据缓冲器的视图。我通常不建议你在没有充分理由的情况下做你想做的事情。所有数据都存储在堆外。那本书很贵。
在堆上做任何数学题都是不好的。这里唯一的用例是集成。我建议尽可能直接在数组上操作。从序列化到索引的一切都为您处理。
如果你真的需要它来进行某种集成,使用guava,你可以在一行中完成它:Doubles.asList(arr.data().dup().asDouble());
其中,ndarray是要操作的ndarray。

trnvg8h3

trnvg8h32#

是的,事实证明,带有ND 4J的.data()并不适合用于非常严肃的事情。这对我想做的事情来说有点遗憾:编写并不真正依赖于ND 4J的单元测试,以及它如何在内部处理数据。
作为这里问题的另一个例子,考虑以下代码:

  1. import org.nd4j.linalg.factory.Nd4j
  2. object foo extends App {
  3. val x = Nd4j.create(Array[Double](1,2, 3,4, 5,6), Array(3,2))
  4. // 1,2
  5. // 3,4
  6. // 5,6
  7. println(x)
  8. val xArr = x.data().asDouble().toList
  9. // 1,2, 3,4, 5,6 - row-wise
  10. println(xArr)
  11. val w = Nd4j.create(Array[Double](10,20,30, 40,50,60), Array(2,3))
  12. // 10,20,30
  13. // 40,50,60
  14. println(w)
  15. val wArr = w.data().asDouble().toList
  16. // 10,20,30, 40,50,60 - row-wise
  17. println(wArr)
  18. val wx = w.mmul(x)
  19. /*
  20. * 10,20,30 1,2 10*1+20*3+30*5 10*2+20*4+30*6 220 280
  21. * 40,50,60 3,4 = 40*1+50*3+60*5 40*2+50*4+60*6 = 490 640
  22. * 5,6
  23. */
  24. println(wx)
  25. val wxArr = wx.data().asDouble().toList
  26. // 220, 490, 280, 640 - column-wise
  27. println(wxArr)
  28. val wxTArr = wx.transpose().data().asDouble().toList
  29. // 220, 490, 280, 640 - still column-wise
  30. println(wxTArr)
  31. val wxTIArr = wx.transposei().data().asDouble().toList
  32. // 220, 490, 280, 640 - still column-wise
  33. println(wxTIArr)
  34. }

正如你所看到的,ND 4 J基本上在内部做它想要的事情,当你使用.data()时,它会简单地给予它的内部表示;这个表示不会被任何转置或任何你要求它做的事情改变,因为这些实际上并没有移动底层数据。
这一切都很好,但我想做的基本上是:创建一个Scala普通双精度数列表;给予我的自定义库的东西;让图书馆做好自己的事情。获取其输出并将其转换为另一个Scala双精度列表;验证这些双精度数是我期望它计算的。相反,我必须做的是把预期的东西放在ND 4J数组中,这样我就可以正确地将其与实际输出进行比较,所以我的测试现在依赖于ND 4J,这是我的库的内部技术选择。
无论如何,这是一个相对较小的抱怨,教训是,避免.data(),而不是如果你使用ND 4J,使用它在整个(即使你认为有点不雅)。

展开查看全部
1zmg4dgp

1zmg4dgp3#

如果不检查NDArray的order+形状,使用.data()确实看起来很危险。'c'代表逐行,'f'(fortran)代表逐列
你可以在创建数组时强制排序:

  1. INDArray array = Nd4j.create(new double[] {1,2,3,4,5,6}, new int[]{3,2}, 'f');

你可以在检索双[]扁平数组时强制排序:

  1. double[] data = Nd4j.toFlattened('f', array).toDoubleVector();

参考文献:https://deeplearning4j.konduit.ai/nd4j/reference

相关问题