我正在为 ruby-fann 创建输入,出于性能原因在 narray 中进行尽可能多的操作。通常我正在处理 2D 200x200 浮点数组,并且需要重复处理 1000 次。
仅使用 NArray,我可以获得可接受的性能。但是,据我所知,我已经完成了一些我想做的操作,但我无法让 NArray 批量执行操作。这意味着我最终使用 Ruby 的循环控件来处理单个 NArray 条目。这对我的代码性能有直接和有害的影响,我想知道我有哪些解决方法或方法。我有能力但不希望分叉 NArray 并为我的工作添加一些功能。这不适合我,因为我需要的功能不够通用,无法进入该库。
我可能会考虑编写一个以某种方式直接使用 NArray 的本机扩展 - 欢迎提供有关如何做到这一点的指针,我不确定如何从另一个中引用一个本机扩展的 gem。
如果我能以不同的方式构建代码,以使 Ruby 部分更快,或利用任何 NArray 功能或相关库,我也将不胜感激任何见解或反馈。
我的两段慢代码非常相似,因此发布了一个问题。
1) 将浮点矩阵限制在一个范围内
我目前在做什么(简化):
# In reality, nn_input contains real-world data, and I want to
# re-normalise it, clipping high values to a maximum of 1.0
nn_input = NArray.float(200,200).random
nn_input *= 1.1
# The test for "anything needs clipping" is fast, the 200x200 loop is somewhat slower!
if (nn_input.gt 1.0).sum > 0
(0...200).each do |x|
(0...200).each do |y|
nn_input[x, y] = 1.0 if nn_input[x, y] > 1.0
end
end
end
2)根据平均值将大矩阵下采样到较小的矩阵(想想“图像重新调整大小”)
我目前在做什么(简化):
# In reality, nn_input contains real-world data, and I want to
# downsize it, re-sampling a 200x200 array to a 20x20 one
large_input = NArray.float(200,200).random
small_output = NArray.float(20,20)
(0...20).each do |x|
(0...20).each do |y|
small_output[x, y] = large_input[x*10..x*10+9,y*10..y*10+9].mean
end
end
我mean
在第二个例子中使用了 NArray 的方法,它比第一个例子的问题要小,我最终为每个项目执行了一个小的 Ruby 循环 40000 次(因此整个数据集超过 2 亿次!)
在 masa16 回复之后,这是一个非常快速的 irb 基准测试,显示了速度的差异:
irb
1.9.3-p327 :001 > require 'narray'
=> true
1.9.3-p327 :002 > t0 = Time.now; 250.times { nn_input = NArray.float(200,200).random() * 1.1; (0...200).each {|x| (0...200).each { |y| nn_input[x,y]=1.0 if nn_input[x,y]> 1.0 }} }; Time.now - t0
=> 9.329647
1.9.3-p327 :003 > t0 = Time.now; 250.times { nn_input = NArray.float(200,200).random() * 1.1; nn_input[nn_input.gt 1.0] = 1.0; }; Time.now - t0
=> 0.764973
因此,对于那个快 10 倍的小代码段,并且我通常运行的不是 250 次,而是 50,000 次,这为我节省了 30 分钟到 1 小时的运行时间,而这在之前需要 3 到 4 小时。