【问题标题】:Fast operations between binary numpy arrays二进制 numpy 数组之间的快速操作
【发布时间】:2020-03-12 17:24:20
【问题描述】:

我想在仅包含 -11 的 numpy 数组之间执行内存和时间高效的操作。

例子:

>>> m1
array([[-1,  1,  1, -1],
       [ 1, -1, -1, -1]], dtype=int8)
>>> m2
array([-1,  1, -1,  1], dtype=int8)
>>> np.dot(m1, m2)
array([ 0, -2], dtype=int8)

在这种情况下,二进制变量由np.int8类型变量表示,这已经比np.int32好一点。

有没有办法用一位大小的变量来表示这些值并执行快速操作?我需要做点积和加法。

【问题讨论】:

  • 这些向量的元素乘法(作为点积运算的一部分)可以表示为位或字节的异或运算。为了将 -1 和 1 相加,我建议为每个可能的字节值构建一个查找表。查找向量的每个字节的值,然后对这些值求和。只有部分填充的字节需要特别注意(或更多查找表)。
  • 添加到底是什么意思?只是像点积一样求和?
  • 是的,我的意思是总和。它通常是两个相同大小的数组之间的元素之和。然后我对结果应用符号函数。因此,需要求和的符号,而不是求和本身的结果。

标签: python arrays numpy


【解决方案1】:

有一个 BitVector 库,它允许您使用一个不错的 API 将位更密集,尽管与 numpy 不同。使用它可能比在 numpy 之上实现操作更容易。虽然应该可以通过bitwise XOR.

如果您的 -1 在位向量中表示为 0,而 1 表示为 1,则点积是两个向量中具有相同值的位置数减去值不同的位置数。

你通过异或得到不同的位置:

>>> m1_1 = BitVector(bitstring='0110')
>>> m2 = BitVector(bitstring='0101')
>>> xor = m1_1 ^ m2
>>> print(xor)
0011

然后你对这个向量求和,但你必须考虑到这里的 0 表示 1,1 表示 -1。因此,我们将从设置为 0 的位数中减去设置为 1 的位数:

>>> bits_zero = xor.length() - xor.count_bits()
>>> bits_zero - xor.count_bits()
0
>>> # Or just
>>> xor.length() - 2 * xor.count_bits()
0

如果您的向量大小与整数大小一致(即 32、64 的倍数),那么将这种方法移植到 Numpy 应该很简单。否则,您将不得不特别对待最后一个 int。

编辑:正如@Michael Butscher 在评论中所写,您将错过 Numpy 中的函数 count_bits。逐个字节,查找表确实小而高效。

注意:虽然这应该更节省内存,但不确定它是否会带来任何加速。你必须做你的基准测试。

编辑:基准测试结果

我刚刚对纯 numpy(将位存储在 int)与 BitVector 进行了计时。

vector_len = 64*1024
matrix_rows = 1024
# I tested various data types
dtype = np.int8

m1 = 2 * np.random.randint(2, dtype=dtype, size=[matrix_rows, vector_len]) - 1
m2 = 2 * np.random.randint(2, dtype=dtype, size=vector_len) - 1

# this is being timed:
dot = m1.dot(m2)

m1_bv = [BitVector(bitlist = (row + 1) / 2) for row in m1]
m2_bv = BitVector(bitlist = (m2 + 1) / 2)

# this is being timed:
dot_bv = [vector_len - 2 * (m1_row ^ m2_bv).count_bits() for m1_row in m1_bv]

结果是(64 位英特尔笔记本电脑处理器):

  • BitVector: 可怕 - 1 分 7 秒 ± 1.51 秒
  • int8:192 毫秒 ± 3.39 毫秒
  • int16:192 毫秒 ± 1.6 毫秒
  • int32:40.6 毫秒 ± 228 微秒
  • int64:48.6 毫秒 ± 307

我还没有用 Numpy 实现按位点积,但你可以看到

  1. BitVector 在这里并不可行。
  2. 小整数类型可以节省内存,但它们会使 Numpy 更难优化计算。

【讨论】:

  • 鉴于现代处理器的 64 位方向,您可能无法通过位级处理获得太多速度。
猜你喜欢
  • 2015-12-20
  • 2016-12-20
  • 2012-03-06
  • 2018-07-25
  • 2015-08-26
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
相关资源
最近更新 更多