我的问题是关于下面的最后一行:mu@sigma@mu
。它为什么有效?将一维ndarray视为行向量还是列向量?无论哪种方式,都不应该是mu.T@sigma@mu
或mu@sigma@mu.T
?我知道mu.T
仍然会返回mu
,因为mu
只有一个维度,但是,解释器似乎太聪明了。
>> import numpy as np
>> mu = np.array([1, 1])
>> print(mu)
[1 1]
>> sigma = np.eye(2) * 3
>> print(sigma)
[[ 3. 0.]
[ 0. 3.]]
>> mu@sigma@mu
6.0
更一般地说,这是python中矩阵代数的一种更好的做法:使用ndarray和@
进行上面的矩阵乘法(更清晰的代码),或者使用np.matrix
和重载的{{1}如下(数学上不那么混乱)
*
答案 0 :(得分:2)
Python从左到右执行链式操作:
In [32]: mu=np.array([1,1])
In [33]: sigma= np.array([[3,0],[0,3]])
In [34]: mu@sigma@mu
Out[34]: 6
与做两个表达式相同:
In [35]: temp=mu@sigma
In [36]: temp.shape
Out[36]: (2,)
In [37]: temp@mu
Out[37]: 6
在我的评论(已删除)中,我声称@
正在执行np.dot
。那不太对劲。该文档描述了不同的1d数组的处理。但结果形状是相同的:
In [38]: mu.dot(sigma).dot(mu)
Out[38]: 6
In [39]: mu.dot(sigma).shape
Out[39]: (2,)
对于1d和2d数组,np.dot
和@
应该产生相同的结果。它们在处理高维数组时有所不同。
历史上numpy使用的数组可以是0d,1d和on。 np.dot
是原始的矩阵乘法方法/函数。
np.matrix
,主要是为了方便任性的MATLAB程序员。它只允许2d数组(就像旧的,20世纪90年代的MATLAB一样)。它使用
__mat__
(*)
def __mul__(self, other):
if isinstance(other, (N.ndarray, list, tuple)) :
# This promotes 1-D vectors to row vectors
return N.dot(self, asmatrix(other))
if isscalar(other) or not hasattr(other, '__rmul__') :
return N.dot(self, other)
return NotImplemented
Mu*sigma
和Mu@sigma
表现相同,但调用树不同
In [48]: Mu@sigma@Mu
...
ValueError: shapes (1,2) and (1,2) not aligned: 2 (dim 1) != 1 (dim 0)
Mu*sigma
产生一个(1,2)矩阵,它不能矩阵乘以a(1,2),因此需要转置:
In [49]: Mu@sigma@Mu.T
Out[49]: matrix([[6]])
请注意,这是一个(1,1)矩阵。如果你想要一个标量,你必须使用item
。 (在MATLAB中,没有标量这样的东西。一切都有形状/大小。)
@
是Python和numpy的最新成员。它作为未实现的运算符添加到Python中。 numpy(可能还有其他包)实现了它。
它使链式表达式成为可能,但我对dot
中的链式[38]
没有任何问题。在处理更高维度的情况时,它更有用。
此添加意味着使用旧np.matrix
类的理由减少了一个。 (矩阵式行为在scipy.sparse
矩阵类中根深蒂固。)
如果你想要数学纯度'我建议采用数学物理方法,并使用爱因斯坦符号 - 在np.einsum
中实现。
对于这么小的数组,时序反映的是调用结构,而不是实际的计算次数:
In [57]: timeit mu.dot(sigma).dot(mu)
2.79 µs ± 7.75 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [58]: timeit mu@sigma@mu
6.29 µs ± 31.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [59]: timeit Mu@sigma@Mu.T
17.1 µs ± 134 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [60]: timeit Mu*sigma*Mu.T
17.7 µs ± 517 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
请注意,老式的' dot
是最快的,而两个矩阵版本都比较慢。
我可能会在mu@sigma@mu.T
这样的表达式中添加.T
,因此@
(matmul
)不知道有尝试转置mu
。它只是使用转置的结果。请记住它是解析表达式而不是numpy
函数的Python解释器。因此,如果mu.T
对mu
没有任何作用,就像1d数组的情况一样,.T
也不会对@
产生影响。
transpose
已为2d数组定义良好。 numpy
转置的编写方式使得它也适用于任何维度的数组。它改变了轴的顺序,但从不添加尺寸。还有其他工具可以执行此操作,例如reshape
和np.newaxis
索引。
在Octave中,只为二维物体定义转置
>> ones(2,3,4)'
error: transpose not defined for N-d objects
MATLAB中不存在1-d对象。
In [270]: np.ones((2,3,4),int).T.shape
Out[270]: (4, 3, 2)
np.dot
非常清楚如何处理1d数组,'向量':
- 如果
a
和b
都是1-D数组,则它是向量的内积 (没有复杂的共轭)。
在matmul
中,描述更复杂,但结果是相同的。
关于你的评论:
A = [1,2]且B = [3,5]为(2,)ndarray,A @ B可能意味着[1,2] * [3,5]' = 13,或者它可能意味着[1,2]' * [3,5] = [[3,5],[6,10]]
在numpy
中获取产品的各种方式是:
In [273]: A = np.array([1,2]); B = np.array([3,5])
元素乘法(MATLAB中的.*
)
In [274]: A*B
Out[274]: array([ 3, 10])
内在产品
In [275]: A@B # same as np.dot(A,B)
Out[275]: 13
外部产品
In [276]: np.outer(A,B)
Out[276]:
array([[ 3, 5],
[ 6, 10]])
获得内在产品的另一种方法:
In [278]: np.sum(A*B)
Out[278]: 13
用爱因斯坦符号(来自数学物理学),内在和外在:
In [280]: np.einsum('i,i',A,B)
Out[280]: array(13)
In [281]: np.einsum('i,j',A,B)
Out[281]:
array([[ 3, 5],
[ 6, 10]])
使用广播获取外部
In [282]: A[:,None]*B[None,:]
Out[282]:
array([[ 3, 5],
[ 6, 10]])
您[1, 2]'
所要求的内容在numpy
中实现为A[:,None]
,将1d数组转换为列向量(2d)。
Octave在numpy
之后很久就加入了广播。我不知道MATLAB是否已经发展到那么远。 :)
@
不进行广播,但可以使用列或行向量:
In [283]: A@B[:,None] # your imagined A*B'
Out[283]: array([13])
要使用@
获取外部,我需要添加维度以使A
成为列向量,并B
添加行向量:
In [284]: A[:,None]@B
ValueError: shapes (2,1) and (2,) not aligned: 1 (dim 1) != 2 (dim 0)
In [285]: A[:,None]@B[None,:]
Out[285]:
array([[ 3, 5],
[ 6, 10]])
当数组确实是2d,并且包括一维为1的情况时,@
/ dot
行为与常见的矩阵约定不同。当尺寸为1或大于2时,MATLAB的直觉失败,部分原因是MATLAB无法处理这些直觉。
这个Octave错误表明n-d矩阵只能捎带2d。它们在numpy
意义上并不是真正的n-d。
>> ones(2,3,4) * ones(2,3,4)
error: operator *: nonconformant arguments (op1 is 2x12, op2 is 2x12)
答案 1 :(得分:1)
基本上,如果您有1d数组one_d
和2d数组two_d
,则将它们与@
相乘会以这样的方式处理1d数组,即获得常规矩阵 - 向量乘法:
one_d @ two_d # gives a row vector (that is returned as 1d array)
two_d @ one_d # gives a column vector (also returned as 1d array)
在内部,1d数组扩展为shape(n,1)或(1,n)数组,执行乘法,然后将其转换回一维数组。
关于哪个选项更好,scipy documentation推荐数组。