Skip to content

第 6 章 矩阵:定义与基本运算

学习目标

  • 理解矩阵的定义、记号与元素下标
  • 掌握矩阵加法、数乘、转置
  • 认识零矩阵、单位矩阵、对角矩阵与对称矩阵
  • 会用 NumPy 与 PyTorch 创建矩阵、取元素、做运算

6.1 为什么需要矩阵

第 2~5 章我们把数排成一排,得到向量;现在把多个向量排在一起,就得到矩阵

为什么要排在一起?看三个场景:

  1. 数据表:班里 3 名同学的身高和体重,一行一个人,一列一个指标:
  1. 方程组:第 1 章的距离、第 5 章的线性组合,最后都要解「一组线性方程」。把所有系数按位置排成一张表,方程本身就能用一个矩阵表示(第 8 章)。

  2. 变换:把一张图的所有像素坐标排成矩阵,一次乘法就能全部旋转或拉伸(第 7 章)。

矩阵就是把「重复的结构」变成「一个整体」,让整张表能作为一个对象参加运算。这是从「单个数字」到「批量数据」的飞跃,也是 NumPy 数组维度从 1 到 2 的飞跃。

6.2 矩阵的定义与记号

矩阵:把 个数按 行、 列排成的矩形数表,叫做 矩阵(读作「m 乘 n 矩阵」)。

记号和术语:

  • 是矩阵的名字;如果强调尺寸,写 ;
  • 表示第 行第 列的元素——先数行,再数列;
  • 行数,列数;
  • 时叫方阵;
  • 只有一行的矩阵是行向量,只有一列的是列向量——向量是矩阵的特例!

矩阵

6.3 用 NumPy 与 PyTorch 创建矩阵

python
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
    if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
        font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False

A = [[1, 2, 3],
     [4, 5, 6],
     [7, 8, 9]]

fig, ax = plt.subplots(figsize=(5.5, 4.5))
ax.axis("off")
table = ax.table(cellText=A, loc="center", cellLoc="center")
table.scale(1, 2.2)
table.auto_set_font_size(False)
table.set_fontsize(14)
for (row, col), cell in table.get_celld().items():
    cell.set_edgecolor("#d4d4d8")
    cell.set_facecolor("#fafafa" if row % 2 else "white")
    cell.set_linewidth(1.2)
ax.set_title("矩阵:3 行 3 列的数表", pad=12)
fig.savefig("book/public/figs/fig06-matrix.png", dpi=120, bbox_inches="tight")
plt.close(fig)

NumPy 里,矩阵就是二维数组:

python
import numpy as np

A = np.array([[160.0, 50.0],
              [170.0, 60.0],
              [155.0, 45.0]])
print("A =")
print(A)
print("形状 shape:", A.shape)   # (3, 2):3 行 2 列
print("维度 ndim:", A.ndim)
print("a[1][0] =", A[1, 0])    # 第 2 行第 1 列
print("第 1 行:", A[0])
print("第 2 列:", A[:, 1])

运行输出:

text
A =
[[160.  50.]
 [170.  60.]
 [155.  45.]]
形状 shape: (3, 2)
维度 ndim: 2
a[1][0] = 170.0
第 1 行: [160.  50.]
第 2 列: [50. 60. 45.]

注意索引从 0 开始:第 1 行是 A[0],第 1 列是 A[:, 0]A[i, j] 对应数学记号 下标减 1

PyTorch 的写法一致:

python
import torch

A = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0]])
print("torch A:")
print(A)
print("A[1, 0] =", A[1, 0])

运行输出:

text
torch A:
tensor([[1., 2.],
        [3., 4.]])
A[1, 0] = tensor(3.)

常用特殊矩阵直接用函数创建:

python
import numpy as np

print("零矩阵:\n", np.zeros((2, 3)))
print("单位矩阵:\n", np.eye(3))
print("对角矩阵:\n", np.diag([2.0, 5.0, 8.0]))
print("全 1 矩阵:\n", np.ones((2, 2)))

运行输出:

text
零矩阵:
 [[0. 0. 0.]
 [0. 0. 0.]]
单位矩阵:
 [[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]
对角矩阵:
 [[2. 0. 0.]
 [0. 5. 0.]
 [0. 0. 8.]]
全 1 矩阵:
 [[1. 1.]
 [1. 1.]]

单位矩阵 对角线上全是 1、其余全是 0 的矩阵。它在矩阵乘法里的角色,就像数 1 在乘法里的角色——第 7 章会验证这一点。

6.4 矩阵加法与数乘

矩阵加法:两个同形状的矩阵,对应位置相加:

数乘:每个元素都乘这个数:

这和向量的规则一模一样——向量本来就是只有一列(或一行)的矩阵。

python
import numpy as np

A = np.array([[1.0, 2.0],
              [3.0, 4.0]])
B = np.array([[5.0, 6.0],
              [7.0, 8.0]])

print("A + B =\n", A + B)
print("3A =\n", 3 * A)
print("A - B =\n", A - B)

运行输出:

text
A + B =
 [[ 6.  8.]
 [10. 12.]]
3A =
 [[ 3.  6.]
 [ 9. 12.]]
A - B =
 [[-4. -4.]
 [-4. -4.]]

加法与数乘满足和向量相同的运算律(交换律、结合律、分配律),验证方式也相同:逐元素验证。形状不同的矩阵不能相加:

python
# 预期错误:ValueError
import numpy as np

A = np.ones((2, 3))
B = np.ones((2, 2))
print(A + B)

运行输出(真实报错):

text
ValueError: operands could not be broadcast together with shapes (2,3) (2,2)

6.5 转置:行列互换

转置:把矩阵 的行和列互换,得到的矩阵记作 :

的第 行第 列,等于 的第 行第 列。

3×2 的矩阵转置后变成 2×3。

python
import numpy as np

A = np.array([[1.0, 2.0],
              [3.0, 4.0],
              [5.0, 6.0]])
print("A.T =\n", A.T)
print("转置两次回到自己:", np.allclose(A.T.T, A))

运行输出:

text
A.T =
 [[1. 3. 5.]
 [2. 4. 6.]]
转置两次回到自己: True

转置的性质(都可以逐元素验证):

满足 的方阵叫对称矩阵——它的元素关于主对角线对称,如 。对称矩阵后面在特征值分解和最小二乘里反复出现,因为 永远是方阵而且对称(第 13、14 章)。

动手实践

  1. 写出 零矩阵、 单位矩阵、以 为对角线的对角矩阵,并用 NumPy 创建验证。
  2. 计算 ,并验证
  3. 判断 是否对称矩阵,用代码验证
  4. 用 PyTorch 创建 两个 2×2 矩阵,计算 ,打印结果。
  5. 故意让两个形状不同的矩阵相加,记录报错信息,并用自己的话解释「同形状」是什么意思。

常见错误

错误写法/理解原因
认为 是第 1 列第 2 行下标约定 是第 行第 列,先数行
给不同形状的矩阵做加法加法要求同形状;NumPy 报 broadcast 错误
忘记索引从 0 开始,写 A[1][1] 取「第 1 行第 1 列」代码里 A[0][0] 才是数学里的
认为转置改变元素的值转置只改变排列,元素本身不变
A.T 误写成 A^TPython 里 ^ 是异或运算,不是转置;转置是 .T 属性或 np.transpose(A)

章末练习

基础

  1. 写出矩阵 ,再写出
  2. 计算 :
  3. 用 NumPy 创建 单位矩阵,打印第 2 行、第 2 列。

提高

  1. 证明 (逐元素写出两边第 行第 列是什么)。
  2. 已知 矩阵, 矩阵。 有意义吗? 有意义吗?分别说明理由。

挑战

  1. 对称矩阵 满足 。证明:对任意矩阵 , 一定是对称矩阵(提示:验证 ,要用转置性质 ——这条性质第 7 章讲完矩阵乘法后你自己就能补上证明)。
  2. 把向量 看成 列矩阵,写出 ,并解释「行向量」与「列向量」的区别。

章末自测

  1. 表示矩阵的第几行第几列?
    • A. 第 2 行第 3 列
    • B. 第 3 行第 2 列
    • C. 第 2 列第 3 行
    • D. 第 23 个元素
  2. 矩阵转置后是?
    • A. 矩阵
    • B. 矩阵
    • C. 矩阵
    • D. 矩阵
  3. 两个矩阵能相加的条件是?
    • A. 行数相同
    • B. 列数相同
    • C. 形状相同
    • D. 都是方阵
  4. 单位矩阵 是?
    • A. 全 0 的 3×3 矩阵
    • B. 对角线全 1、其余全 0 的 3×3 矩阵
    • C. 全 1 的 3×3 矩阵
    • D. 1×3 的行向量
    • A.
    • B.
    • C.
    • D.
  5. NumPy 中取矩阵第 1 行第 1 列的写法是?
    • A. A[1, 1]
    • B. A[0, 0]
    • C. A[1][0]
    • D. A(0, 0)
  6. 的第 行第 列等于 的?
    • A. 第 行第
    • B. 第 行第
    • C. 第 列第
    • D. 第 列第
  7. 下列哪个矩阵是对称矩阵?
    • A.
    • B.
    • C.
    • D.
  8. np.eye(2) 创建的是?
    • A. 2×2 全零矩阵
    • B. 2×2 单位矩阵
    • C. 2×2 全一矩阵
    • D. 长度为 2 的向量
  9. 两个形状分别为 的矩阵相加,会?
    • A. 得到 矩阵
    • B. 得到 矩阵
    • C. 报错
    • D. 自动转置后相加