首页 > 解决方案 > Numpy Dot Product - 形状相同的形状错误

问题描述

完全陷入了一个相当愚蠢的问题:我正在尝试计算对象之间某些属性的点积,但不断收到值错误 - 形状不匹配 - 但形状相同 (2,1) 和 (2,1 ),因为数组只是同一类的不同实例的属性:

class MyClass(Object):
      def __init__(self, a,b, x,y):
          self.prop_1 = np.array((a,b))
          self.prop_2 = np.array((x,y))

其中所有 a、b、x 和 y 都是标量。然后再往下我正在尝试

def MyFunction(Obj1, Obj2):
    results = np.dot(Obj1.prop_1 - Obj2.prop_1, Obj2.prop_2 - Obj2.prop_3)

不断抛出值错误

ValueError: shapes (2,1) and (2,1) not aligned: 1 (dim 1) != 2 (dim 0)

从数学上讲,这个点积应该没问题 - 但错误消息的最后一点暗示我必须转置其中一个数组。我非常感谢对 numpy 形状解释的简短解释以避免这种错误!

编辑:

认为我措辞有点错误。当我通过(案例a)启动我的对象时

a,b = np.random.rand(2)
x,y = np.random.rand(2)
MyClass(a, b, x, y)

一切都像魅力一样。但是,如果我以(案例b)开始

a = np.random.rand(1)
b = np.random.rand(1)
x = np.random.rand(1)
y = np.random.rand(1)
MyClass(a, b, x, y)

由于形状不匹配,后来的点积无法工作。

我注意到,在情况 b 中,每个单独的值都是形状(1,) 的,我很清楚,将其中的两个值组合起来会产生形状(2,1)而不是()情况 a 的形状 - 但是为什么这两种声明变量的方式会导致不同的形状?

正如你所知道的,我对 Python 比较陌生,并认为这只是一种执行多项任务的巧妙方法 - 事实证明它背后有一些进一步的推理,我很想听听。

标签: pythonnumpy

解决方案


第1部分

问题是您的数组是成熟的二维矩阵,而不是np.dot理解它的意义上的一维“向量”。为了让你的乘法工作,你需要(a)将你的向量转换为向量:

np.dot(a.reshape(-1), b.reshape(-1))

(b) 设置矩阵乘法以使维度起作用。请记住,两个 Nx1 矩阵的点积是 A T B:

np.dot(a.T, b)

或 (c),使用np.einsum显式设置总和的维度:

np.einsum('ij,ij->j', a, b).item()

对于所有使用 的示例dot,您可以使用np.matmul(或等效的@运算符)或np.tensordot,因为您有二维数组。

通常,在使用dot. 表格单元格是einsum下标

                                      A
      |         1D        |          2D         |                ND             |
   ---+-------------------+---------------------+-------------------------------+
   1D | i,i->             | ij,j->i             | a...yz,z->a...y               |
   ---+-------------------+---------------------+-------------------------------+
B  2D | i,ij->j           | ij,jk->ik           | a...xy,yz->a...xz             |
   ---+-------------------+---------------------+-------------------------------+
   ND | y,a...xyz->a...xz | ay,b...xyz->ab...xz | a...mxy,n...wyz->a...mxn...wz |
   ---+-------------------+---------------------+-------------------------------+

基本上,dot沿最后两个维度遵循矩阵乘法的正常规则,但前导维度始终是组合的。如果您希望数组 > 2D 的前导维度一起广播(即,将矩阵堆栈中的相应元素相乘,而不是所有可能的组合),请使用matmulor@代替。

第2部分

当您将输入初始化为 时a, b = np.random.rand(2),您将数组的两个元素解压缩为标量:

>>> a, b = np.random.rand(2)
>>> a
0.595823752387523
>>> type(a)
numpy.float64
>>> a.shape
()

numpy.ndarray请注意,在这种情况下类型不是。但是,当您这样做时a = np.random.rand(1),结果是一个包含一个元素的一维数组:

>>> a = np.random.rand(1)a
>>> a
array([0.21983553])
>>> type(a)
numpy.ndarray
>>> a.shape
(1,)

当您从 numpy 数组创建 numpy 数组时,结果是一个 2D 数组:

>>> np.array([1, 2]).shape
(2,)
>>> np.array([np.array([1]), np.array([2])]).shape
(2, 1)

展望未来,您有两个选择。您可以对输入更加小心,也可以在创建数组后对其进行清理。

您可以扩展您输入的数组:

ab = np.random.rand(2)
xy = np.random.rand(2)
MyClass(*ab, *xy)

或者,您可以在创建阵列后将其展平/散开:

def __init__(self, a, b, x, y):
     self.prop_1 = np.array([a, b]).ravel()
     self.prop_2 = np.array([x, y]).ravel()

您可以使用....reshape(-1)而不是...ravel().


推荐阅读