问题相关的 Numpy

数据挖掘 Python 深度学习 麻木的
2021-10-12 01:56:42

使用 Numpy,计算大小为 10 的向量与大小为 (5, 10) 的矩阵中的每一行的内积的最佳方法是什么?

3个回答

这里有几种方法,使用一些虚拟数据:

In [1]: import numpy as np

In [2]: a = np.random.randint(0, 10, (10,))

In [3]: b = np.random.randint(0, 10, (5, 10))

In [4]: a
Out[4]: array([4, 1, 0, 6, 3, 3, 6, 6, 1, 8])

In [5]: b
Out[5]: 
array([[9, 0, 6, 1, 1, 1, 4, 7, 4, 7],
       [5, 8, 8, 3, 4, 8, 7, 3, 0, 4],
       [2, 2, 5, 3, 9, 6, 1, 5, 8, 3],
       [2, 0, 4, 3, 5, 3, 3, 4, 3, 3],
       [3, 3, 6, 4, 7, 5, 8, 6, 7, 3]])

由于您要求的维度,为了计算内积(也称为标量积和点积),我们需要转置矩阵b以计算出维度。对于长度为 10 的向量,numpy 赋予它 shape (10,)所以它似乎有 10 行,没有列,但它有点模棱两可。Numpy 基本上会做它必须做的事情以使维度工作。我们可以通过使用将其强制转换为(10, 1)向量a.reshape((10, 1)),但这不是必需的。矩阵有一个定义的第二维,所以我们有一个 shape (5, 10)为了将这两个形状相乘,我们需要在中间匹配相同的尺寸。这意味着制作(10,) * (10, 5). 对矩阵执行转置会反转维度给我们(10, 5)那些内心10然后 s 将消失并给我们留下一个(1, 5)向量。


话虽如此,我们可以使用以下任何方法来获得等效的答案:

  1. 标准标准点积:

    In [7]: a.dot(b.T)
    Out[7]: array([174, 174, 141, 119, 190])
    
  2. 方便的 numpy 表示法:

    In [6]: a @ b.T
    Out[6]: array([174, 174, 141, 119, 190])
    
  3. 高效的“爱因斯坦符号”,Ricci 微积分的一个子集(我让感兴趣的读者在线搜索以获取更多信息):

    In [8]: np.einsum('i,ij->j', a, b.T)
    Out[8]: array([174, 174, 141, 119, 190])
    
  4. 就像shadowstalker的评论一样

    In [9]: np.array([np.dot(a, r) for r in b])
    Out[9]: array([174, 174, 141, 119, 190])
    

如果您的矩阵的维度(100, 100)或更小,那么该@方法可能是最快和最优雅的。然而,一旦你开始进入让你怀疑你的笔记本电脑是否会处理它的矩阵(例如使用 shape (10000, 10000)) - 那么是时候阅读关于爱因斯坦符号和numpy 中的惊人模块的文档这个博客了!einsum

好的,让我们进行一个 numpy 测试!

使用 Numpy,计算大小为 10 的向量与大小为 (5, 10) 的矩阵中的每一行的内积的最佳方法是什么?

  1. np.dot(vector1, matrix1)
  2. np.array([np.dot(row, vector1) for row in matrix1])
  3. np.matmul(vector1, matrix1)
  4. np.dot(矩阵 1,向量 1)
  5. np.sum(matrix1 * vector1, axis=1)

回答

简而言之,

最佳答案是 4,这是因为它是计算成本最低的方法,只需一步即可完成。

但是,如果您想知道原因,请继续阅读……


要回答这个问题,让我们看看哪些有效,哪些无效。记住问题要求“计算内积的最佳方法”,所以应该有不止一种方法有效,对吧?

现在让我们用 numpy 实际创建一个向量和一个矩阵

np.random.seed(2)

vector1 = np.random.randint(10, size=(1,10))[0]
print('Vector 1\n', vector1)

matrix1 = np.random.randint(10, size=(5,10))
print('\nMatrix 1\n', matrix1)

我们将得到这个输出

Vector 1
 [8 8 6 2 8 7 2 1 5 4]

Matrix 1
 [[4 5 7 3 6 4 3 7 6 1]
  [3 5 8 4 6 3 9 2 0 4]
  [2 4 1 7 8 2 9 8 7 1]
  [6 8 5 9 9 9 3 0 0 2]
  [8 8 2 9 6 5 6 6 6 3]]

惊人的!

现在让我们玩一下每个可能的答案,看看哪些适合,哪些不适合!

但在我们这样做之前,让我们看看矩阵乘法是如何工作的。

要将两个矩阵与 [ROWS x COLUMNS] 相乘,第一个矩阵的列的大小必须与第二个矩阵的行大小匹配,因此我们需要如下所示的场景。

[A,B] * [B,N]

生成的矩阵将具有两个矩阵的外部维度的维度因此从上面的示例中,我们可以相乘,因为两个矩阵的内部维度匹配,我们将获得一个具有两个外部维度的新矩阵矩阵。

在这种情况下,我们得到的矩阵将是 [A,N] 维。

1. np.dot(vector1, matrix1)

OUTPUT: (EXCEPTION)
shapes (10,) and (5,10) not aligned: 10 (dim 0) != 5 (dim 0)

我们这里有一个 **exception **,所以 (1) 不可能是正确的。我们不能有点积,因为 [1,10] * [5,10] 不符合矩阵乘法的内部要求。

2. np.array([np.dot(row, vector1) for row in matrix1])

OUTPUT: 
 [243 225 211 309 301]

在这里它似乎工作

对于每一行,我们的矩阵都有一个“for”循环。所以 5 次,我们将矩阵的每个元素乘以向量的等效元素,然后将它们相加。

每次运行我们都会得到一个数字,所以在 5 次之后,我们将用我们的产品创建一个大小为 [1,5] 的向量。

3. np.matmul(vector1, matrix1)

OUTPUT: 
 matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 5 is different from 10)

我们这里有一个例外,所以(3)不可能是正确的。

在这里,我们尝试在 [1,10]*[5,10] 处进行矩阵乘法。内部尺寸不匹配,我们遇到与 (1) 相同的问题。

4. np.dot(matrix1, vector1)

OUTPUT:
 [243 225 211 309 301]

在这里它似乎工作

我们乘以 [5,10]*[10,1] 矩阵!生成的矩阵是 [5,1] 矩阵,并给出与答案 (2) 的情况相同的结果。

然而,这个答案比答案 (2) 更好,因为它在计算上更便宜,因为在答案 2 中我们创建了一个 for 循环,而这里我们通过一次代数计算得到结果。

5. np.sum(matrix1 * vector1, axis=1)

OUTPUT:
 [243 225 211 309 301]

在这里它似乎工作

我们再次得到与 (2) 和 (4) 相同的结果。

但是我们再次在这里执行多个步骤。

首先通过做 一个r一世X1*veCr1 我们最终得到了这个矩阵

[[32 40 42  6 48 28  6  7 30  4]
 [24 40 48  8 48 21 18  2  0 16]
 [16 32  6 14 64 14 18  8 35  4]
 [48 64 30 18 72 63  6  0  0  8]
 [64 64 12 18 48 35 12  6 30 12]]

然后通过在轴 1(y 轴)求和,我们将每个行号添加到单个数字(很像 for 循环的解决方案 2)。

那么最好的解决方案是什么?

您还记得,问题不只是询问哪些选项会产生一些结果,而是最好使用哪个选项。到目前为止的答案2,4, 和5似乎工作。

最佳答案是 4,这是因为它是计算成本最低的方法,只需一步即可完成。

为自己运行!

import numpy as np

print("With Numpy, what’s the best way to compute the inner product of a vector of size 10 with each row in a matrix of size (5, 10)?")

np.random.seed(2)

print('\nSamples:')

vector1 = np.random.randint(10, size=(1,10))[0]
print('\nVector 1\n', vector1)

matrix1 = np.random.randint(10, size=(5,10))
print('\nMatrix 1\n', matrix1)

print('\n\nPossible Answers:')

#========================


print('\nA) np.dot(vector1, matrix1)', end='')
try: print(' - WORKING \n', np.dot(vector1, matrix1))
except Exception as e:   print(' - EXCEPTION \n', e)


#========================  

print('\nB) np.array([np.dot(row, vector1) for row in matrix1])', end='')
try:
  print(' - WORKING \n', np.array([np.dot(row, vector1) for row in matrix1]))
except Exception as e: 
  print(' - EXCEPTION \n', e)

#========================  

print('\nC) np.matmul(vector1, matrix1)', end='')
try:
  print(' - WORKING \n', np.matmul(vector1, matrix1))
except Exception as e: 
  print(' - EXCEPTION \n', e)

#========================


print('\nD) np.dot(matrix1, vector1)', end='')
try:
  print(' - WORKING \n', np.dot(matrix1, vector1))
except Exception as e: 
  print(' - EXCEPTION \n', e)

#========================  

print('\nE) np.sum(matrix1 * vector1, axis=1)', end='')
try:
  print(' - WORKING \n', np.sum(matrix1 * vector1, axis=1))
except Exception as e: 
  print(' - EXCEPTION \n', e)


print('\n=======================================')

print("As the question asks, we shouldn't just look for a working result, but for the best result, and it seems to be (D) as it gets the best result with a single math step, thus computationally more efficient ")

据我所知,最快的方法是:

np.dot(matrix, vector)

这是一个专门为此编写和优化的函数。不建议一个一个相乘再自己加,显然会花更多的时间。