python - 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 比较陌生,并认为这只是一种执行多项任务的巧妙方法 - 事实证明它背后有一些进一步的推理,我很想听听。
解决方案
第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 的前导维度一起广播(即,将矩阵堆栈中的相应元素相乘,而不是所有可能的组合),请使用matmul
or@
代替。
第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()
.
推荐阅读
- oauth - 如何将 JWT 附加到 JMS 消息?
- azure-devops - 如何授予权限以管理 Azure dev-ops 中的测试计划
- sqlite - 如何在我的飞镖代码 sqlite 中解决这个问题
- python - 多级索引中的 Groupby 时间箱
- typescript - 扩展递归定义的接口成员
- delphi - 将父对象的事件绑定到临时子对象的方法
- html - Bootstrap 4 图像网格对齐
- javascript - 从 Pug 中的字符串插入新行
- asp.net-mvc - ASP.NET Core MVC 在不使用 Http 请求的情况下使用内部 Web API
- java - 春季启动MVC。传递空请求正文