【问题标题】:如何根据并行 Python 中的某些条件进行采样?
【发布时间】:2022-01-23 04:45:51
【问题描述】:

假设我想根据条件并行采样。

例如,给出矩阵A。我想对p 对索引(i,j) 进行采样,这样A[i][j] != 5

import numpy as np
import random

A = np.random.randint(10, size=(5000, 5000)) # assume this is fixed

p = 400  # sample 400 index

res = set()
cnt = 0

while cnt < p:
    r, c = random.randint(0, A.shape[0]-1), random.randint(0, A.shape[0]-1)
    if A[r, c] != 5 and (r,c) not in res:
        res.add((r,c))
        cnt += 1

以上是我的尝试。但是,矩阵A 和样本数量p 可能非常大。我们可以并行吗?喜欢使用joblibmultiprocessing?或者有什么快速获取rowcol的方法?

【问题讨论】:

  • 您是否尝试过以下示例代码? numpy.org/doc/stable/reference/random/multithreading.html
  • 谢谢,由于我的采样有一些条件,我不怎么修改上面的示例代码。
  • 如果您需要速度,矢量化和缓存效率也很重要。您是否需要在两个指数中都进行抽样?你能系统地浏览一下这些指数吗?在随机点中对矩阵进行采样非常耗时。通过增加i 在循环中执行np.argwhere(A[i] != 5) 可能是最快的解决方案之一。这样就可以得到第一个p非5值。
  • 比较整数比从内存中加载值要快,后者可能要慢数百或数千倍。

标签: python random parallel-processing multiprocessing joblib


【解决方案1】:

您可以使用 Numba 来加速此代码。 Numba 可以使用即时编译器 (JIT) 在运行时生成快速(并行)函数。使用像np.int8 这样的较小数据类型可以节省一些内存空间并缩短执行时间。实际上,较小的数组可以更快地从 RAM 读取/写入 RAM。此外,它们更有可能适合 CPU 缓存,从而加速随机访问。虽然您可以并行化随机选取,但这非常困难,并且线程的创建可能比有关所选参数的实际计算更昂贵。尽管如此,Numba 可以通过(大部分)消除 Python 解释器的开销来大幅提高其速度。 这是生成的代码:

# Initial conditions

import numba as nb
import numpy as np
import random

@nb.njit('int8[:,:](int_, int_)', parallel=True)
def genArray(n, m):
    res = np.empty((n, m), dtype=np.int8)
    # Parallel loop
    for i in nb.prange(n):
        for j in range(m):
            res[i, j] = np.random.randint(10)
    return res

p = 400
A = genArray(5000, 5000)

# Actual computing code

@nb.njit('(int8[:,::1], int_)')
def genPosSet(A, p):
    maxi = A.shape[0]-1
    res = set()
    cnt = 0
    while cnt < p:
        r, c = random.randint(0, maxi), random.randint(0, maxi)
        if A[r, c] != 5 and (r,c) not in res:
            res.add((r,c))
            cnt += 1
    return res

res = genPosSet(A, p)

genPosSet 的这个实现在我的机器上需要 64 us,而初始函数需要 1350 us。因此,新的实现速度快了 21 倍

请注意,创建/删除线程(1 个线程/核心)并在它们之间共享工作的时间通常需要 10 到 1000 微秒。

请注意,如果p 不比A.size * prob 小很多,其中prob 是找到一个不同于5 的值的概率,那么当前的算法效率不是很高。在这种情况下,最好在选择随机位置之前过滤不同于 5 的值。如果p 不比A.size 小很多,那么最好的解决方案是打乱所有可以选择的位置,最后提取结果列表的p 第一个值。

【讨论】:

  • 这段代码并行化了矩阵的生成,但是矩阵被认为是给定的矩阵,速度增益大多是在那里实现的。另一方面,genPosSet 没有并行化,但是,numba 在单线程上的循环方面也做得很好。
  • @DanielTuzes “矩阵被认为是给定的矩阵”是什么意思?
  • 在 OP 的代码中:A = np.random.randint(10, size=(5000, 5000)) # assume this is fixed
  • @DanielTuzes 感谢您指出这一点。我首先了解到数组的维度是固定的,而不是变量已经预定义。我更新了答案以专注于随机挑选部分。
猜你喜欢
  • 2021-12-26
  • 2021-01-31
  • 2016-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-01
  • 2020-10-27
  • 1970-01-01
相关资源
最近更新 更多