【问题标题】:Median of multiple arrays in JuliaJulia中多个数组的中位数
【发布时间】:2020-09-14 06:08:46
【问题描述】:

this question有关

我想知道如何沿巨大数组的特定维度计算中位数,例如大小为 (20, 1920, 1080, 3)。我不确定是否有任何实际用途,但我只是想检查一下中位数在 Julia 中的工作情况。

使用 numpy 计算 (3,1920,1080,3) 上的中位数需要 ~0.5 秒s。它在 zeros 数组上工作得非常快(在 (120, 1920, 1080,3) 上不到 2 秒),在真实图像上工作得不是那么快但很好(在 (120, 1920, 1080,3) 上 20 秒)。

Python 代码:

import cv2
import sys
import numpy as np
import time

ZEROES=True
N_IMGS=20

print("n_imgs:", N_IMGS)
print("use dummy data:", ZEROES)

imgs_paths = sys.argv[1:]
imgs_paths.sort()
imgs_paths_sparse = imgs_paths[::30]

imgs_paths = imgs_paths_sparse[N_IMGS]

if ZEROES:
    imgs_arr = np.zeros((N_IMGS,1080,1920,3), dtype=np.float32)
else:
    imgs = map(cv2.imread, imgs_paths)
    imgs_arr = np.array(list(imgs), dtype=np.float32)

start = time.time()
imgs_median = np.median(imgs_arr, 0)
end = time.time()
print("time:", end - start)
cv2.imwrite('/tmp/median.png', imgs_median)

在 julia 中,我只能计算 (3, 1920, 1080,3) 的中位数。之后我的earlyoom 进程由于大量使用的内存而杀死了 julia 进程。

我尝试了类似于我在max 上尝试的方法:

function median1(imgs_arr)
    a = imgs_arr
    b = reshape(cat(a..., dims=1), tuple(length(a), size(a[1])...))
    imgs_max = Statistics.median(b, dims=1)
    return imgs_max
end

或者更简单的情况:

import Statistics
a = zeros(3,1080,1920,3)
@time Statistics.median(a, dims=1)
 10.609627 seconds (102.64 M allocations: 2.511 GiB, 3.37% gc time)
...

因此,在 numpy 上需要 10 秒,而 0.5 秒。 我只有 4 个 CPU 内核,这不仅仅是并行化。

是否有或多或少简单的方法来优化它?

或者至少在不过度使用内存的情况下进行切片并逐个计算?

【问题讨论】:

  • 如果您在 Julia 中而不是仅在 Python 中设置问题,那么 Julia 人员会更容易提供帮助——很难知道 Julia 中的等效设置是什么。例如,Fredrik 的回答为所有图像使用了一个连续的块,这似乎忽略了单独加载图像的关键问题。不过,考虑到提出问题的方式,这似乎很公平,因为它并不具体。

标签: arrays julia median


【解决方案1】:

试试JuliennedArrays.jl

julia> a = zeros(3,1080,1920,3);

julia> using JuliennedArrays

julia> @time map(median, Slices(a,1));
  0.822429 seconds (6.22 M allocations: 711.915 MiB, 20.15% gc time)

正如 Stefan 在下面评论的那样,内置的 median 做同样的事情,但 要慢得多

julia> @time median(a, dims=1);
  7.450394 seconds (99.80 M allocations: 2.368 GiB, 4.47% gc time)

至少从julia> VERSION v"1.5.0-DEV.876"开始

【讨论】:

  • 你不需要 JuliennedArrays 因为数据在一个连续的数组中:using Statistics; median(a, dims=1) 做同样的事情。但是 JuliennedArrays 对于更复杂的情况来说是一个很棒的包。
  • 没错,median 可以为您完成,但median(a, dims=1) 的速度太可怕了,我及时更新了答案。
【解决方案2】:

很难知道单独加载图像这一事实是否是问题的关键部分,因为 Julia 中的问题设置缺失,而且 Julia 程序员很难遵循 Python 设置或知道我们需要多少来匹配它。您需要:

  1. 加载或移动图像数据,使它们实际上是同一数组的一部分,然后取其中值;

  2. 使不同数组中的一组空间上不相关的值在抽象上表现得好像它们是单个数组的一部分,然后通过足以处理此抽象的通用方法获取该集合的中值。

Fredrik 的回答隐含地假设您已经加载了图像数据,因此它们都是同一个连续数组的一部分。但是,如果是这种情况,那么您甚至不需要 JuliennedArrays,您只需使用 median 标准库中的 median 函数即可:

julia> a = rand(3, 1080, 1920, 3);

julia> using Statistics

julia> median(a, dims=1)
1×1080×1920×3 Array{Float64,4}:
[:, :, 1, 1] =
 0.63432  0.205958  0.216221  0.571541  …  0.238637  0.285947  0.901014

[:, :, 2, 1] =
 0.821851  0.486859  0.622313  …  0.917329  0.417657  0.724073

如果您可以像这样加载数据,这是最好的方法 - 这是迄今为止最有效的一组相同大小的图像的表示,并且使跨图像的矢量化操作变得简单高效。第一个维度是执行操作最有效的维度,因为 Julia 是列优先的,因此第一个维度(列)是连续存储的。

将图像放入连续内存的最佳方法是预先分配正确类型和尺寸的未初始化数组,然后使用一些就地 API 将数据读入数组。出于某种原因,您的 Julia 代码似乎已将图像加载为单个数组的向量,而您的 Python 代码似乎已将所有图像加载到单个数组中?

重塑和连接的方法是第二种方法的一种极端情况,即在应用矢量化中值运算之前一次性移动所有数据。显然,这涉及移动大量数据,效率非常低。

由于内存局部性,将单个数据切片复制到临时数组中并计算其中位数可能更有效。这可以通过数组推导很容易地完成:

julia> v_of_a = [rand(1080, 1920, 3) for _ = 1:3]
3-element Array{Array{Float64,3},1}:
 [0.7206652600431633 0.7675119703509619 … 0.7117084561740263 0.8736518021960584; 0.8038479801395197 0.3159392943734012 … 0.976319025405266 0.3278606124069767; … ; 0.7424260315304789 0.4748658164109498 … 0.9942311708400311 0.37048961459068086; 0.7832577306186075 0.13184454935145773 … 0.5895094390350453 0.5470111170897787]

[0.26401298651503025 0.9113932653115289 … 0.5828647778524962 0.752444909740893; 0.5673144007678044 0.8154276504227804 … 0.2667436824684424 0.4895443896447764; … ; 0.2641913584303701 0.16639100493266934 … 0.1860616855126005 0.04922131616483538; 0.4968214514330498 0.994935452055218 … 0.28097239922248685 0.4980189891952156]

julia> [median(a[i,j,k] for a in v_of_a) for i=1:1080, j=1:1920, k=1:3]
1080×1920×3 Array{Float64,3}:
[:, :, 1] =
 0.446895  0.643648  0.694714   …  0.221553   0.711708   0.225268
 0.659251  0.457686  0.672072      0.731218   0.449915   0.129987
 0.573196  0.328747  0.668702      0.355231   0.656686   0.303168
 0.243656  0.702642  0.45708       0.23415    0.400252   0.482792

【讨论】:

  • median(a, dims=1) 表达式已在问题中使用,以及创建连续数组的 MWE a = zeros(3,1080,1920,3)。并且在仍然比 numpy 版本慢 20 倍的 OP 中(这似乎在我的机器上得到了证实)
  • 抱歉,问题内容很多。我不清楚真正的意义是什么——它是如何在数组向量上做一个中值,如何有效地做到这一点,或者其他什么。通过阅读并回答了有关如何在数组向量上进行 max 的链接问题,我可能被引向了错误的方向。如果有人觉得它有用,我会留下答案。
猜你喜欢
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-13
  • 1970-01-01
相关资源
最近更新 更多