← 返回周历 预备 视频 DeepInfer 101
预备 2

预备 2:把矩阵手算连接到代码

浏览文字约 4 分钟 · 手算与练习请按正文分段完成 源文件:Markdown 本地渲染版 · 无需联网
本页目录 · 6 节

目标:理解行副本、二维索引和累计值的作用域。
修改:prep.py 中的 copy_matrix、matmul。
预计投入:45–90 分钟;矩阵乘法只用你已经会的手算规则。

1. 一张矩阵在 Python 中是什么

本节路线:第1–2节修复prep q2的copy_matrix,第3–5节修复同题的matmul;先修为列表循环和预备1的状态追踪。两者都完成后再跑q2全部测试。命令沿用 课程根目录。出口应有一幅行别名图、一格点积、一个非方阵新例子,而不是要求你现在掌握GPU二维布局。

a = [[1, 2], [3, 4]]

外层列表放两行,内层列表放各行的元素。a[1] 是 [3,4],a[1][0] 是 3。两个索引都从 0 开始。

这里讨论 Python 对象,不把嵌套列表当作 GPU 的连续二维存储。后面计算 word 地址时,会明确换成一维布局。

2. 先预测:只复制外层够不够?

a = [[1, 2], [3, 4]]
b = a[:]
b[0][0] = 99
print(a)

先预测,再在交互窗口试。a[:] 建了新外层,但其中的行仍然指向原来的行:

a[0] ──→ [1, 2] ←── b[0]
a[1] ──→ [3, 4] ←── b[1]

所以改 b[0][0] 会改到 a[0][0]。copy_matrix 的规格要求每一行都独立,因此要逐行复制。本课只处理数值矩阵,行中的数值不需要再递归复制。

复制题提示

遍历原矩阵的每一行,给每行做 row[:],将新行放进一个新外层列表。可以用循环,不要求会列表推导式。

3. 手算四格,再看三层循环

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

C[0][0] = 1*5 + 2*7 = 19
C[0][1] = 1*6 + 2*8 = 22
C[1][0] = 3*5 + 4*7 = 43
C[1][1] = 3*6 + 4*8 = 50

将“取 A 的一行和 B 的一列”翻译成索引:

A[i][p] * B[p][j]

i 决定输出行,j 决定输出列,p 扫过相乘相加的那条维度。A 的形状为 (m,k),B 为 (k,n),输出为 (m,n);两个 k 必须相同。

4. 累计值应在哪里清零?

starter 把 total=0 放在所有循环之前。先别改,跟踪第一行:

输出格进入该格前 total本格应加的值错误结果正确结果
C[0][0]0191919
C[0][1]19224122

第一格正确,第二格开始累积了别的格子的结果。想清楚:total 代表整张矩阵的和,还是当前一个输出格的点积?

矩阵乘法提示 1

只要开始计算一个新的 C[i][j],就应有一个新的累计值。

矩阵乘法提示 2

把初始化移到 j 循环里面、p 循环之前。不要放在 p 循环里,否则每次乘法都会抹掉前面的和。

5. 运行、定位、再检查

python course/tools/grade.py prep --question q2 --failfast

如果失败的是副本测试,先修 copy_matrix;如果是数值测试,打印一个错误格子的 i,j,p,total。本题只要求非空矩形输入,不要求在这一步写一个完整的张量库。

也不要用 [[0]*n]*m 创建独立输出行:它会重复引用同一行。给每一行单独创建列表。

6. 独立检验

手算 [[1,2,3]] @ [[1],[0],[-1]],预测形状和结果,再调用函数对照。然后解释为什么下式不合法:[[1,2]] @ [[3,4]]。

检查自己的答案

合法例子的形状是 (1,3) @ (3,1) → (1,1),结果 [[-2]]。另一个例子是 (1,2) @ (1,2),内维 2 和 1 不匹配,要求抛 ValueError。

下一步:预备 3:让测试帮助你。