NumPy Matrix 与 Array 类的乘法有何不同?
Posted
技术标签:
【中文标题】NumPy Matrix 与 Array 类的乘法有何不同?【英文标题】:how does multiplication differ for NumPy Matrix vs Array classes? 【发布时间】:2011-04-22 21:09:35 【问题描述】:numpy 文档建议使用数组而不是矩阵来处理矩阵。但是,与八度音程(直到最近我还在使用)不同,* 不执行矩阵乘法,您需要使用函数 matrixmultipy()。我觉得这让代码很不可读。
有没有人分享我的观点,并找到了解决方案?
【问题讨论】:
您是在征求意见,而不是提问。是否有更具体的内容我们可以帮助您或指导您使其更具可读性? 实际上,如果您进行线性代数并且不想使用 multiply() ,那么文档建议使用矩阵,那么问题是什么? 我没有详细阅读文档。只是好奇,数组比矩阵类有什么优势?我发现数组不区分行和列。是因为数组应该被认为是张量而不是矩阵吗?正如乔指出的那样,矩阵类是 2-dim 的事实非常有限。这种设计背后的想法是什么,例如,为什么没有像 matlab/octave 这样的单个矩阵类? 我想主要的问题是 python 没有.*
vs '*' 语法用于元素明智与矩阵乘法。如果它有,那么一切都会更简单,尽管我很惊讶他们选择 *
来表示逐元素而不是矩阵乘法。
【参考方案1】:
避免使用matrix
类的主要原因是a) 它本质上是二维的,b) 与“普通”numpy 数组相比有额外的开销。如果您所做的只是线性代数,那么无论如何,请随意使用矩阵类...不过,我个人觉得它比它的价值更麻烦。
对于数组(Python 3.5 之前的版本),使用 dot
而不是 matrixmultiply
。
例如
import numpy as np
x = np.arange(9).reshape((3,3))
y = np.arange(3)
print np.dot(x,y)
或者在较新版本的 numpy 中,只需使用 x.dot(y)
就我个人而言,我发现它比 *
暗示矩阵乘法的运算符更具可读性......
对于 Python 3.5 中的数组,请使用 x @ y
。
【讨论】:
当你有一堆乘法时它是不可读的,例如 x'A'*Ax. @elexhobby -x.T.dot(A.T).dot(A).dot(x)
不是那么难以理解,我。不过,对每个人来说都是他自己的。如果你主要做矩阵乘法,那么一定要使用numpy.matrix
!
对了,为什么矩阵乘法叫“点”?在什么意义上它是一个点积?
@amcnabb - 矩阵乘法有时在教科书中被称为“点积”(在那些书中,您想到的点积称为“标量积”或“标量点积” ”)。毕竟,标量点积只是两个向量的矩阵乘法,所以使用“点”来表示矩阵乘法通常并没有太大的延伸。至少在我的经验中,这种特殊符号在工程和科学文本中似乎(?)比在数学中更常见。它在 numpy 中的流行主要是因为numpy.matrixmultiply
很难输入。
@amcnabb 重点是generalizes to arbitrary dimensionality 没有歧义。正是这一点使numpy.dot
等价于矩阵乘法。如果您真的不喜欢这种表示法,请使用 matrix
类。【参考方案2】:
NumPy 数组 上的操作与 NumPy 矩阵 上的操作的关键事项是:
NumPy 矩阵是 NumPy 数组的一个子类
NumPy array 操作是 element-wise(一旦考虑到广播)
NumPy 矩阵运算遵循线性代数的普通规则
一些代码sn-ps来说明:
>>> from numpy import linalg as LA
>>> import numpy as NP
>>> a1 = NP.matrix("4 3 5; 6 7 8; 1 3 13; 7 21 9")
>>> a1
matrix([[ 4, 3, 5],
[ 6, 7, 8],
[ 1, 3, 13],
[ 7, 21, 9]])
>>> a2 = NP.matrix("7 8 15; 5 3 11; 7 4 9; 6 15 4")
>>> a2
matrix([[ 7, 8, 15],
[ 5, 3, 11],
[ 7, 4, 9],
[ 6, 15, 4]])
>>> a1.shape
(4, 3)
>>> a2.shape
(4, 3)
>>> a2t = a2.T
>>> a2t.shape
(3, 4)
>>> a1 * a2t # same as NP.dot(a1, a2t)
matrix([[127, 84, 85, 89],
[218, 139, 142, 173],
[226, 157, 136, 103],
[352, 197, 214, 393]])
但如果将这两个 NumPy 矩阵转换为数组,此操作将失败:
>>> a1 = NP.array(a1)
>>> a2t = NP.array(a2t)
>>> a1 * a2t
Traceback (most recent call last):
File "<pyshell#277>", line 1, in <module>
a1 * a2t
ValueError: operands could not be broadcast together with shapes (4,3) (3,4)
虽然使用 NP.dot 语法适用于 数组;此操作类似于矩阵乘法:
>> NP.dot(a1, a2t)
array([[127, 84, 85, 89],
[218, 139, 142, 173],
[226, 157, 136, 103],
[352, 197, 214, 393]])
所以你需要一个 NumPy 矩阵吗?即,NumPy 数组是否足以进行线性代数计算(前提是您知道正确的语法,即 NP.dot)?
规则似乎是,如果参数(数组)的形状 (m x n) 与给定的线性代数运算兼容,那么你没问题,否则,NumPy 会抛出异常。
我遇到的唯一例外(可能还有其他例外)是计算逆矩阵。
下面是 sn-ps,我在其中调用了纯线性代数运算(实际上,来自 Numpy 的线性代数模块)并传入 NumPy 数组
行列式:
>>> m = NP.random.randint(0, 10, 16).reshape(4, 4)
>>> m
array([[6, 2, 5, 2],
[8, 5, 1, 6],
[5, 9, 7, 5],
[0, 5, 6, 7]])
>>> type(m)
<type 'numpy.ndarray'>
>>> md = LA.det(m)
>>> md
1772.9999999999995
特征向量/特征值对:
>>> LA.eig(m)
(array([ 19.703+0.j , 0.097+4.198j, 0.097-4.198j, 5.103+0.j ]),
array([[-0.374+0.j , -0.091+0.278j, -0.091-0.278j, -0.574+0.j ],
[-0.446+0.j , 0.671+0.j , 0.671+0.j , -0.084+0.j ],
[-0.654+0.j , -0.239-0.476j, -0.239+0.476j, -0.181+0.j ],
[-0.484+0.j , -0.387+0.178j, -0.387-0.178j, 0.794+0.j ]]))
矩阵规范:
>>>> LA.norm(m)
22.0227
qr 分解:
>>> LA.qr(a1)
(array([[ 0.5, 0.5, 0.5],
[ 0.5, 0.5, -0.5],
[ 0.5, -0.5, 0.5],
[ 0.5, -0.5, -0.5]]),
array([[ 6., 6., 6.],
[ 0., 0., 0.],
[ 0., 0., 0.]]))
矩阵排名:
>>> m = NP.random.rand(40).reshape(8, 5)
>>> m
array([[ 0.545, 0.459, 0.601, 0.34 , 0.778],
[ 0.799, 0.047, 0.699, 0.907, 0.381],
[ 0.004, 0.136, 0.819, 0.647, 0.892],
[ 0.062, 0.389, 0.183, 0.289, 0.809],
[ 0.539, 0.213, 0.805, 0.61 , 0.677],
[ 0.269, 0.071, 0.377, 0.25 , 0.692],
[ 0.274, 0.206, 0.655, 0.062, 0.229],
[ 0.397, 0.115, 0.083, 0.19 , 0.701]])
>>> LA.matrix_rank(m)
5
矩阵条件:
>>> a1 = NP.random.randint(1, 10, 12).reshape(4, 3)
>>> LA.cond(a1)
5.7093446189400954
inversion 需要 NumPy 矩阵:
>>> a1 = NP.matrix(a1)
>>> type(a1)
<class 'numpy.matrixlib.defmatrix.matrix'>
>>> a1.I
matrix([[ 0.028, 0.028, 0.028, 0.028],
[ 0.028, 0.028, 0.028, 0.028],
[ 0.028, 0.028, 0.028, 0.028]])
>>> a1 = NP.array(a1)
>>> a1.I
Traceback (most recent call last):
File "<pyshell#230>", line 1, in <module>
a1.I
AttributeError: 'numpy.ndarray' object has no attribute 'I'
但 Moore-Penrose 伪逆 似乎工作得很好
>>> LA.pinv(m)
matrix([[ 0.314, 0.407, -1.008, -0.553, 0.131, 0.373, 0.217, 0.785],
[ 1.393, 0.084, -0.605, 1.777, -0.054, -1.658, 0.069, -1.203],
[-0.042, -0.355, 0.494, -0.729, 0.292, 0.252, 1.079, -0.432],
[-0.18 , 1.068, 0.396, 0.895, -0.003, -0.896, -1.115, -0.666],
[-0.224, -0.479, 0.303, -0.079, -0.066, 0.872, -0.175, 0.901]])
>>> m = NP.array(m)
>>> LA.pinv(m)
array([[ 0.314, 0.407, -1.008, -0.553, 0.131, 0.373, 0.217, 0.785],
[ 1.393, 0.084, -0.605, 1.777, -0.054, -1.658, 0.069, -1.203],
[-0.042, -0.355, 0.494, -0.729, 0.292, 0.252, 1.079, -0.432],
[-0.18 , 1.068, 0.396, 0.895, -0.003, -0.896, -1.115, -0.666],
[-0.224, -0.479, 0.303, -0.079, -0.066, 0.872, -0.175, 0.901]])
【讨论】:
mInv = NP.linalg.inv(m) 计算数组的倒数 这里要注意的重要一点是 * 是逐元素乘法,点是真正的矩阵乘法。请看***.com/a/18255635/1780570 IMP 注意:为了支持数组,应避免使用 numpy 矩阵。文档中的注释->“不再建议使用此类,即使对于线性代数也是如此。而是使用常规数组。将来可能会删除该类。”另请参阅***.com/a/61156350/6043669【参考方案3】:在 3.5 中,Python 终于got a matrix multiplication operator。语法是a @ b
。
【讨论】:
谢谢!是的,很高兴看到我不是唯一一个觉得当前符号不可读的人。【参考方案4】:在处理数组和处理矩阵时,点运算符会给出不同的答案。例如,假设如下:
>>> a=numpy.array([1, 2, 3])
>>> b=numpy.array([1, 2, 3])
让我们将它们转换成矩阵:
>>> am=numpy.mat(a)
>>> bm=numpy.mat(b)
现在,我们可以看到两种情况的不同输出:
>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1. 2. 3.]
[2. 4. 6.]
[3. 6. 9.]]
【讨论】:
具体来说,*是逐元素乘法,点是真正的矩阵乘法。请看***.com/a/18255635/1780570 那是因为作为一个numpy数组,a.T == a,转置没有做任何事情。 如果你写 at = np.array([[1],[2],[3]]),那么 numpy.dot(at,b) 应该给你同样的结果。 matix和array的区别不在点,而在转置。 或者实际上,如果你写 a = numpy.array([[1,2,3]]) 那么 a.T 将真正转置,一切都会像矩阵一样工作。【参考方案5】:来自http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html的参考
...,numpy.matrix 类的使用是不鼓励,因为它没有添加任何 2D numpy.ndarray 对象,并可能导致 混淆 正在使用哪个类。例如,
>>> import numpy as np
>>> from scipy import linalg
>>> A = np.array([[1,2],[3,4]])
>>> A
array([[1, 2],
[3, 4]])
>>> linalg.inv(A)
array([[-2. , 1. ],
[ 1.5, -0.5]])
>>> b = np.array([[5,6]]) #2D array
>>> b
array([[5, 6]])
>>> b.T
array([[5],
[6]])
>>> A*b #not matrix multiplication!
array([[ 5, 12],
[15, 24]])
>>> A.dot(b.T) #matrix multiplication
array([[17],
[39]])
>>> b = np.array([5,6]) #1D array
>>> b
array([5, 6])
>>> b.T #not matrix transpose!
array([5, 6])
>>> A.dot(b) #does not matter for multiplication
array([17, 39])
scipy.linalg 操作可以同样应用于 numpy.matrix 或 2D numpy.ndarray 对象。
【讨论】:
【参考方案6】:This trick 可能是您正在寻找的。这是一种简单的运算符重载。
然后您可以像这样使用建议的 Infix 类:
a = np.random.rand(3,4)
b = np.random.rand(4,3)
x = Infix(lambda x,y: np.dot(x,y))
c = a |x| b
【讨论】:
【参考方案7】:@petr-viktorin 提到的来自PEP 465 - A dedicated infix operator for matrix multiplication 的相关引用阐明了 OP 遇到的问题:
[...] numpy 提供了两种不同的类型和不同的
__mul__
方法。对于numpy.ndarray
对象,*
执行元素乘法,矩阵乘法必须使用函数调用 (numpy.dot
)。对于numpy.matrix
对象,*
执行矩阵乘法,元素乘法需要函数语法。使用numpy.ndarray
编写代码可以正常工作。使用numpy.matrix
编写代码也可以正常工作。 但是当我们尝试将这两段代码整合在一起时,麻烦就开始了。需要ndarray
并获得matrix
的代码(反之亦然)可能会崩溃或返回不正确的结果
@
中缀运算符的引入应该有助于统一和简化python矩阵代码。
【讨论】:
【参考方案8】:函数matmul(从 numpy 1.10.1 开始)对这两种类型都适用,并将结果作为 numpy 矩阵类返回:
import numpy as np
A = np.mat('1 2 3; 4 5 6; 7 8 9; 10 11 12')
B = np.array(np.mat('1 1 1 1; 1 1 1 1; 1 1 1 1'))
print (A, type(A))
print (B, type(B))
C = np.matmul(A, B)
print (C, type(C))
输出:
(matrix([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]]), <class 'numpy.matrixlib.defmatrix.matrix'>)
(array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]), <type 'numpy.ndarray'>)
(matrix([[ 6, 6, 6, 6],
[15, 15, 15, 15],
[24, 24, 24, 24],
[33, 33, 33, 33]]), <class 'numpy.matrixlib.defmatrix.matrix'>)
由于 python 3.5 为 mentioned early,您还可以使用新的矩阵乘法运算符 @
类似
C = A @ B
得到和上面一样的结果。
【讨论】:
以上是关于NumPy Matrix 与 Array 类的乘法有何不同?的主要内容,如果未能解决你的问题,请参考以下文章
Python与线性代数——Numpy中的matrix()和array()的区别