在线性代数中,矩阵相乘(又称矩阵乘法)是一种核心运算,需满足特定维度条件:若矩阵 A 的维度为 (m, n),矩阵 B 的维度为 (n, p),则它们的乘积 AB 的维度为 (m, p)。
其中,矩阵相乘具有以下性质:
- 矩阵相乘不满足交换律,即 AB ≠ BA。
- 矩阵相乘满足结合律,即 (AB)C = A(BC)。
- 矩阵相乘满足分配律,即 A(B+C) = AB + AC。
NumPy 提供了 3 种矩阵相乘的方法,适用于不同的场景。
| 方法 | 说明 |
|---|---|
| numpy.dot() | 计算两个数组的点积 |
| numpy.matmul() | 计算两个数组的矩阵乘积 |
| ndarray @ ndarray | 计算两个数组的矩阵乘积,等价于 numpy.matmul() |
在实际开发中,我们应该遵循以下 2 个最佳实践:
- 优先使用 matmul() 函数或 “@” 运算符来执行矩阵运算,避免 dot() 函数在复杂维度下的意外行为。
- 处理向量时,显式使用 reshape() 方法可以确保维度正确(如将一维数组转为二维行 / 列向量)。
numpy.dot() 实现矩阵相乘
在 NumPy 中,我们可以使用 dot() 函数来计算两个数组的点积。dot() 函数的行为取决于输入的维度:
- 一维数组:计算向量内积(等价于标量积)。
- 二维数组:标准的矩阵乘法。
- 高维数组(≥3):按最后一个轴与倒数第二个轴进行乘积。
语法:
numpy.dot(arr1, arr2, out=None)说明:
dot() 函数接收以下 3 个参数。
arr1(必选):第 1 个输入数组。arr2(必选):第 2 个输入数组。out(可选):用于指定输出数组(需与结果维度匹配)。
示例 1:二维矩阵相乘(标准用法)
import numpy as np
a = np.array([[1,2], [3,4]])
b = np.array([[5,6], [7,8]])
result = np.dot(a, b)
print(result)运行结果如下。
[[19 22]
[43 50]]示例 2:一维向量内积
import numpy as np
a = np.array([1, 2])
b = np.array([3, 4])
result = np.dot(a, b)
print(result)运行结果如下。
11分析:
当输入是一维数组时,dot() 会计算它们的内积,其结果为一个标量(即一个单一的数值):1*3 + 2*4 = 11。
示例 3:三维张量积(广播规则)
import numpy as np
a = np.ones((2, 3, 4))
b = np.ones((2, 4, 5))
result = np.dot(a, b).shape
print(result)运行结果如下。
(2, 3, 2, 5)分析:
在这个例子中,dot() 函数会将最后一个轴(4)与第二个数组的倒数第二个轴(4)相乘,从而生成四维数组(2, 3, 2, 5)。
numpy.matmul() 实现矩阵相乘
在 NumPy 中,我们还可以使用 matmul() 函数来计算两个数组的矩阵乘积。
语法:
numpy.matmul(arr1, arr2, out=None)说明:
matmul() 函数常用参数有以下 3 个。
arr1(必选):第 1 个输入数组。arr2(必选):第 2 个输入数组。out(可选):用于指定输出数组。
matmul() 是专用的矩阵乘法函数,它严格遵循线性代数规则:
- 不支持标量乘法:输入必须是至少二维数组。
- 批量处理:若输入为三维数组(如形状为 (k, m, n) 和 (k, n, p)),则返回形状为 (k, m, p) 的数组。
- 广播规则:允许数组在非矩阵维度上广播(如形状为 (m, n) 和 (n, p) 的数组可与 (j, m, n) 和 (j, n, p) 广播)。
示例 4:标准二维矩阵乘法
import numpy as np
a = np.array([[1,2], [3,4]])
b = np.array([[5,6], [7,8]])
result = np.matmul(a, b)
print(result)运行结果如下。
[[19 22]
[43 50]]分析:
np.matmul(a, b) 与 np.dot(a, b) 结果是一样的。
示例 5:批量矩阵乘法(三维数组)
import numpy as np
a = np.array([[[1,2], [3,4]], [[5,6], [7,8]]]) # 形状:(2, 2, 2)
b = np.array([[[9,10], [11,12]], [[13,14], [15,16]]]) # 形状:(2, 2, 2)
result = np.matmul(a, b)
print(result.shape) # 形状
print(result[0]) # 第一个批量结果运行结果如下。
(2, 2, 2)
[[31 34]
[71 78]]分析:
matmul() 会将两个 2×2×2 的数组看作是两个 “批次” 的 2×2 矩阵。它分别计算第一个批次的 a[0] 和 b[0] 的乘积,以及第二个批次的 a[1] 和 b[1] 的乘积,并将结果合并成一个形状为 (2, 2, 2) 的新数组。
示例 6:自动广播
import numpy as np
a = np.array([[1,2], [3,4]]) # (2, 2)
b = np.array([[[5,6], [7,8]]]) # (1, 2, 2)
result = np.matmul(a, b).shape
print(result)运行结果如下。
(1, 2, 2)分析:
当形状 (2, 2) 与 (1, 2, 2) 的矩阵进行相乘时,前者会被广播为 (1, 2, 2),因此最终结果的形状为 (1, 2, 2)。
对于广播规则是怎样的,我们在后续的 “NumPy 广播机制” 一节中会详细介绍。
“@” 实现矩阵相乘
在 NumPy 中,我们还可以使用 “@” 运算符来计算两个数组的矩阵乘积。
语法:
ndarray @ ndarray说明:
这里的 ndarray 指的是 ndarray 对象。
提示: @ 是 Python 3.5+ 引入的矩阵乘法运算符,它与 matmul() 函数是完全等价的。你可以将其看成是 matmul() 函数的 “语法糖”,主要用于提高代码的可读性。
示例 7:@ 运算符的基本用法
import numpy as np
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
arr3 = arr1 @ arr2 # 矩阵相乘
print(arr3)运行结果如下。
[[19 22]
[43 50]]分析:
arr1 @ arr2 的结果与 numpy.matmul(arr1, arr2) 完全相同,但语法更简洁,也更直观。在实际开发中,如果想要进行矩阵乘法,我们更加推荐使用 “arr1 @ arr2” 这种方式。
