NumPy array与matrix区别及矩阵乘法@和*避坑指南
NumPy 中 array 和 matrix 都能表示二维数据,但乘法运算符、转置、一维向量解释差异明显。实际开发中常见的 ValueError: shapes (3,3) and (1,3) not aligned,往往就是混用 ndarray 和 matrix 的 *、@ 导致。下面结合实测过程,把 array 与 matrix 的区别、矩阵乘法写法、维度排查方法整理出来。一、array 与 matrix 的基本关系
NumPy 的主要对象是同种元素的多维数组 ndarray。维度叫轴,轴的个数叫秩,这同线性代数中的矩阵秩不是一个概念;要求矩阵秩,可以用 np.linalg.matrix_rank。matrix 是 array 的分支(子类),很多场景通用。官方建议如果两者都能用,优先 array:更灵活、更快,还能表示 3、4、5 维;matrix 的优势是运算符简单,两个矩阵相乘可以直接用 *,而 array 的 * 是逐元素乘法,普通矩阵乘法要用 dot 或 @。
import numpy as np
i = np.matrix(np.eye(4))
print(type(i))
print(np.linalg.matrix_rank(i))
i = np.eye(4)
print(type(i))
print(np.linalg.matrix_rank(i))
输出分别是 matrix 类型、4、ndarray 类型、4。说明两者都能被 matrix_rank 处理,但类型不同。
二、array 中的 @、* 与一维 shape
np.array() 的 shape 是 (3,),它既可能被当作行向量,也可能被当作列向量,取决于放在矩阵乘法左边还是右边。@ 是矩阵乘法,* 或 np.multiply 是数量积/逐元素乘法。示例:
import numpy as np
a = np.array([, , ])
b = np.array()
c1 = a @ b
c2 = b @ a
print(c1)
print(c2)
c1 相当于 (3,3) @ (3,),得到 (3,);c2 相当于 (3,) @ (3,3),也得到 (3,)。这说明一维 ndarray 在 @ 两侧会按规则参与矩阵乘法,但不要把它当成真正固定形状的行向量或列向量。
再看逐元素乘法:
import numpy as np
a = np.array([, , ])
b = np.array()
c1 = a * b
c2 = b * a
print(c1)
print(c2)
这里 * 不会执行矩阵乘法,而是把 b 广播到 a 的每一列/每一行,结果仍是逐元素运算。
三、matrix 中 * 是矩阵乘法,multiply 才是逐元素乘法
matrix 与 ndarray 最大的差异在于运算符含义。matrix 中 * 直接表示矩阵乘法。例如:
import numpy as np
a = np.mat([, , ])
b = np.mat()
c = b * a
print(c.shape)
c2 = np.multiply(a, b)
print(c2)
b 被构造为 matrix 后,形状是 (1,3),即行向量。因此 b * a 是 (1,3)*(3,3),可以执行。若写 a * b,则等价于 (3,3)*(1,3),会报:
ValueError: shapes (3,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0)
原因是 matrix 中 * 调用矩阵乘法,a 的列数 3 与 b 的行数 1 不匹配。若要让 a * b 成立,需要把 b 变成列向量,例如 b.reshape(3,1) 或 b.T。逐元素乘法在 matrix 中要用 np.multiply:
c = np.multiply(a, b)
print(c)
输出为:
[
]
四、@ 在 matrix 中同样可用
实测 @ 在 matrix 中也可表示矩阵乘法,并且与 * 的结果一致:
import numpy as np
a = np.mat([, , ])
b = np.mat()
c1 = a @ b
c2 = a * b
print(c1 == c2)
输出全为 True。也就是说,在 matrix 中 @ 和 * 都可表示叉乘/矩阵乘法;但在 array 中 @ 才是矩阵乘法,* 是逐元素乘法。
五、为什么 (1×2) 和 (1×3) 直接相乘会报错
如果要实现 (1×2) 与 (1×3) 相乘,直观做法是先把 (1×2) 转置成 (2×1),再与 (1×3) 相乘,得到 (2×3)。但直接用一维 array 会出问题:
import numpy as np
a = np.array()
b = np.array()
c = a @ b
报错:
ValueError: shapes (2,) and (3,) not aligned: 2 (dim 0) != 3 (dim 0)
这是因为一维 ndarray 的 @ 不是把 a 当 (1,2)、把 b 当 (3,1) 的二维矩阵乘法,而是要求参与运算的一维长度一致。解决办法是借助 matrix 转换为明确的二维矩阵:
import numpy as np
a = np.array()
b = np.array()
c = (np.matrix(a).T) @ (np.matrix(b))
print(c)
输出:
[
]
这里 np.matrix(a).T 得到 (2,1),np.matrix(b) 得到 (1,3),相乘后就是 (2,3)。
六、.T 转置对一维 array 不起作用,对 matrix 起作用
二维 array 可以正常转置:
import numpy as np
a = np.array([, ])
print(a.T.shape)
输出 (3,2)。但一维 array 不行:
import numpy as np
a = np.array()
print(a.shape)
print(a.T.shape)
输出都是 (3,),转置后形状不变。matrix 则可以:
import numpy as np
a = np.mat([])
print(a.shape)
print(a.T.shape)
输出 (1,3) 和 (3,1)。这也是上面用 np.matrix(a).T 能把一维数组变成列向量的原因。
七、实践建议
实际写 NumPy 矩阵运算时,建议每一步都打印 shape,尤其是在 @、*、dot、multiply 混用时。需要矩阵语义时,优先把一维数组 reshape 成明确二维形状,如 (n,1) 或 (1,n);或者使用 @ 配合二维 array。matrix 虽然让 * 直接表示矩阵乘法,但容易与 array 的广播、逐元素乘法混淆,官方也更推荐 array。只要明确 array 和 matrix 在 *、@、multiply、.T 上的差异,就能避免 shapes not aligned 一类错误。
Re: NumPy array与matrix区别及矩阵乘法@和*避坑指南
这个整理很实用,array 和 matrix 看起来都能表示二维数据,但运算符语义差别确实大。我印象最深的是 matrix 里 * 直接做矩阵乘法,而 array 里 * 是逐元素,@ 才是矩阵乘法;还有一维 array 的 shape 是 (3,),.T 之后还是 (3,),所以不能想当然当行向量或列向量用。那个 shapes (3,3) and (1,3) not aligned 的例子也很有代表性,混用 matrix 和 array 时特别容易中招。以后我会尽量优先 ndarray,遇到矩阵乘法就用 @,需要逐元素就用 * 或 np.multiply,并且把参与运算的向量 reshape 成明确的二维形状,这样排查维度问题会清楚很多。感谢整理。Re: NumPy array与matrix区别及矩阵乘法@和*避坑指南
楼主整理得很实用,尤其是把 array 和 matrix 在 @、* 上的语义差异分开讲清楚了。我的体会是,如果项目里两者都可能出现,最好尽早统一成 ndarray:矩阵乘法统一用 @,逐元素乘法统一用 *,这样读代码时不容易误解。遇到一维数组时更要小心,shape 是 (3,) 这种,在 @ 左右会按规则变化,但并不是固定的行向量或列向量;需要明确维度时,先 reshape 成 (n,1) 或 (1,n) 会更稳。matrix 虽然 * 直接就是矩阵乘法,但和 ndarray 混用很容易出现 shapes (3,3) and (1,3) not aligned 这类报错,排查时先看 shape 和乘法的维度顺序。整体看,这篇对日常避坑和维度排查很有帮助,感谢分享。Re: NumPy array与matrix区别及矩阵乘法@和*避坑指南
感谢整理,这篇很实用。array 和 matrix 的 * 含义不同这个点确实容易混,尤其是 array 里 * 是逐元素、@ 才是矩阵乘法,而 matrix 里 * 和 @ 都能做矩阵乘法,逐元素反而要用 np.multiply,这一对照看完清楚多了。一维 array 的 shape 是 (3,)、.T 形状不变、@ 还要求一维长度一致,这几个坑也很真实。用 np.matrix(a).T @ np.matrix(b) 把一维转成明确的二维行列再乘,例子很直观。我以后会尽量统一用 ndarray,矩阵乘法就用 @,需要行列向量时显式 reshape 成二维,减少混用 matrix 带来的对齐报错。首帖最后好像截断了,第六部分 matrix 转置那里只到 impor,如果有后续内容期待补上。
页:
[1]