【问题标题】:Numpy matrix multiplication but instead of multiplying it XOR's elementsNumpy 矩阵乘法,但不是将其乘以 XOR 的元素
【发布时间】:2021-04-08 14:50:57
【问题描述】:

如标题中所述,我想进行计算,而不是将相应的元素相乘,而是对它们进行二进制 XOR,然后将它们相加。 举例说明:

编辑:上面的大图是计算,但我们开始了:从左边的第一行 [1 0 1] 和顶部矩阵的第一列 [1 0 0]。 1 XOR 1 = 0, 0 XOR 0 = 0, 1 XOR 0 = 1。将它们全部相加 0 + 0 + 1 = 1。左起第一行 [1 0 1],第二列 [0 0 0]:1 XOR 0 = 1, 0 XOR 0 = 0, 1 XOR 0 = 1. 将它们全部相加 1 + 0 + 1 = 2. 以此类推

在 numpy 中可以做到这一点吗?

【问题讨论】:

标签: python algorithm numpy matrix


【解决方案1】:

试试这个:

M1 = np.array([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
M2 = np.array([[1, 0, 1], [0, 0, 1], [1, 1, 1]])
(M1 ^ M2[:,None]).sum(-1)

输出:

array([[1, 2, 2],
       [2, 1, 1],
       [2, 3, 3]])

编辑

如果要预分配内存:

intermediary = np.empty((3,3,3), dtype=np.int32)
np.bitwise_xor(M1, M2[:,None], out=intermediary).sum(-1)

【讨论】:

  • 虽然解决方案看起来很短,但对于巨大的矩阵,它可能会溢出内存,并且 OP 说他有巨大的矩阵,例如对于两个 1000x1000 大小(1MB 大小)的矩阵,它会产生大小为 1000x1000x1000 的中间矩阵( 1GB!!!)。
  • 按照现代标准,1GB 并不是那么大
  • 您可以指定out 关键字,这将阻止内部内存分配。
  • @MadPhysicist 我只是想象 1000x1000 矩阵,OP 可能有 10000x10000 (100MB) 矩阵,那么中间矩阵将是 10000x10000x10000 (1 TeraByte!!!)。
  • @Kevin。如果您还没有分配 N**3 大小的数组,这有什么帮助?
【解决方案2】:

这只是对 Artys 答案的更长评论。有几件事可以加快 Numba 功能。

提高性能的步骤

import numpy as np, numba

m1 = np.random.randint(low=0, high=1,size=1_000_000).reshape(1_000,1_000)
m2 = np.random.randint(low=0, high=1,size=1_000_000).reshape(1_000,1_000)

#@Arty
@numba.njit(cache = True)
def matxor_1(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    for i in range(mr.shape[0]):
        for j in range(mr.shape[1]):
            mr[i, j] = np.sum(m1[:, j] ^ m2[i, :])
    return mr

%timeit matxor_1(m1, m2)
#1.06 s ± 9.39 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

#Aligned memory acces (real transpose the ascontiguousarray is important)
@numba.njit(cache = True)
def matxor_2(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    m1_T=np.ascontiguousarray(m1.T)

    for i in range(mr.shape[0]):
        for j in range(mr.shape[1]):
            mr[i, j] = np.sum(m1_T[j, :] ^ m2[i, :])
    return mr

%timeit matxor_2(m1, m2)
#312 ms ± 7.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

#Writing out the inner loop
@numba.njit(fastmath=True,cache = True)
def matxor_3(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    m1_T=np.ascontiguousarray(m1.T)

    for i in range(mr.shape[0]):
        for j in range(mr.shape[1]):
            acc=0
            for k in range(m2.shape[1]):
                acc+=m1_T[j, k] ^ m2[i, k]
            mr[i, j] = acc
    return mr

%timeit matxor_3(m1, m2)
#125 ms ± 3.85 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

#Parallelization
@numba.njit(fastmath=True,cache = True,parallel=True)
def matxor_4(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    m1_T=np.ascontiguousarray(m1.T)

    for i in numba.prange(mr.shape[0]):
        for j in range(mr.shape[1]):
            acc=0
            for k in range(m2.shape[1]):
                acc+=m1_T[j, k] ^ m2[i, k]
            mr[i, j] = acc
    return mr

%timeit matxor_4(m1, m2)
#23.8 ms ± 711 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
print(np.allclose(matxor_1(m1, m2),matxor_2(m1, m2)))
#True
print(np.allclose(matxor_1(m1, m2),matxor_3(m1, m2)))
#True
print(np.allclose(matxor_1(m1, m2),matxor_4(m1, m2)))
#True

【讨论】:

  • 不错的分析和改进,谢谢! +1 点赞
  • @Arty 如果您只是将改进步骤整合到您的答案中,然后我会删除此答案,也许会很好。如前所述,这只是一个更长的评论。
  • 谢谢!融入我的答案。如果有问题和/或需要更正,请编辑我的答案。
【解决方案3】:

您可以只组合两个循环和 Numpy 1D xor-sum,如下所示:

Try it online!

import numpy as np
m1 = np.array([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
m2 = np.array([[1, 0, 1], [0, 0, 1], [1, 1, 1]])
mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
for i in range(mr.shape[0]):
    for j in range(mr.shape[1]):
        mr[i, j] = np.sum(m1[:, j] ^ m2[i, :])
print(mr)

输出:

[[1 2 2]
 [2 1 1]
 [2 3 3]]

正如@MadPhysicist 建议的那样,您可以使用 Numba JIT-optimizer (pip install numba) 来提升上面的代码,并且您将获得非常快的代码来进行操作,并且内存消耗很少:

Try it online!

import numpy as np, numba
@numba.njit(cache = True)
def matxor(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    for i in range(mr.shape[0]):
        for j in range(mr.shape[1]):
            mr[i, j] = np.sum(m1[:, j] ^ m2[i, :])
    return mr
m1 = np.array([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
m2 = np.array([[1, 0, 1], [0, 0, 1], [1, 1, 1]])
print(matxor(m1, m2))

由于@max9111 建议和编码的以下重大改进,上面的 Numba 代码也可以提升多达 44 倍:

import numpy as np, numba

m1 = np.random.randint(low=0, high=1,size=1_000_000).reshape(1_000,1_000)
m2 = np.random.randint(low=0, high=1,size=1_000_000).reshape(1_000,1_000)

#@Arty
@numba.njit(cache = True)
def matxor_1(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    for i in range(mr.shape[0]):
        for j in range(mr.shape[1]):
            mr[i, j] = np.sum(m1[:, j] ^ m2[i, :])
    return mr

%timeit matxor_1(m1, m2)
#1.06 s ± 9.39 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

#Aligned memory acces (real transpose the ascontiguousarray is important)
@numba.njit(cache = True)
def matxor_2(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    m1_T=np.ascontiguousarray(m1.T)

    for i in range(mr.shape[0]):
        for j in range(mr.shape[1]):
            mr[i, j] = np.sum(m1_T[j, :] ^ m2[i, :])
    return mr

%timeit matxor_2(m1, m2)
#312 ms ± 7.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

#Writing out the inner loop
@numba.njit(fastmath=True,cache = True)
def matxor_3(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    m1_T=np.ascontiguousarray(m1.T)

    for i in range(mr.shape[0]):
        for j in range(mr.shape[1]):
            acc=0
            for k in range(m2.shape[1]):
                acc+=m1_T[j, k] ^ m2[i, k]
            mr[i, j] = acc
    return mr

%timeit matxor_3(m1, m2)
#125 ms ± 3.85 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

#Parallelization
@numba.njit(fastmath=True,cache = True,parallel=True)
def matxor_4(m1, m2):
    mr = np.empty((m2.shape[0], m1.shape[1]), dtype = np.int64)
    m1_T=np.ascontiguousarray(m1.T)

    for i in numba.prange(mr.shape[0]):
        for j in range(mr.shape[1]):
            acc=0
            for k in range(m2.shape[1]):
                acc+=m1_T[j, k] ^ m2[i, k]
            mr[i, j] = acc
    return mr

%timeit matxor_4(m1, m2)
#23.8 ms ± 711 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
print(np.allclose(matxor_1(m1, m2),matxor_2(m1, m2)))
#True
print(np.allclose(matxor_1(m1, m2),matxor_3(m1, m2)))
#True
print(np.allclose(matxor_1(m1, m2),matxor_4(m1, m2)))
#True

【讨论】:

  • 不幸的是它们很大:
  • @Kyatt 取决于有多大,他们的最大尺寸是多少?上面这段代码非常接近最优,因为大部分时间都花在 xor-sum 内,而不是循环内,所以大部分时间都在 Numpy 内,你不能让它更快。
  • @Kyatt 例如,对于 1000 x 1000 的矩阵大小是完全可以的,并且将在几分之一秒内计算出来。试试你的数据。
  • 哦,好吧,不同大小的矩阵呢?
  • 用 numba njit 编译它,你在速度和内存消耗上都赢了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-31
  • 2014-11-13
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多