有一个 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 实现按位点积,但你可以看到
- BitVector 在这里并不可行。
- 小整数类型可以节省内存,但它们会使 Numpy 更难优化计算。