先分清对象:两张逻辑 GPU 各负责一半 Parameter
🎯 先猜一猜
SimpleModel(dim=4) 有两个无 bias 的 Linear 层。
list(model.parameters()) 中有多少个 Parameter 对象?
先补的知识
- model.parameters() 返回一个可迭代对象;先转成 list,后面才能按下标切片并反复访问。
- 当前 SimpleModel 有 fc1.weight 和 fc2.weight 两个 Parameter,因为两个 Linear 都设置了 bias=False。
- Notebook 文字用 flatten 帮助理解,但当前代码并没有把每个 Parameter 的元素拼成大向量;它实际切分的是 Parameter 对象列表。
- 列表切片保留原 Parameter 的引用,不会复制权重。后面通过这些引用更新时,SimpleModel 里的权重也会改变。
图解原理
把优化器看成一个任务分派员。它先把模型参数按出现顺序排成列表,再从中点切成前后两组:逻辑 GPU 0 负责前半,逻辑 GPU 1 负责后半。这里没有真实 CUDA 通信,字典只是模拟每张卡的责任范围。
当前模型到底有几个参数对象
fc1.weight一个 Parameter,测试中 shape=[4,4]
fc2.weight一个 Parameter,测试中 shape=[4,4]
bias不存在,因为 bias=False
len(self.params)2,而不是 32 个标量
切分后的所有权
[fc1.weight, fc2.weight] GPU 0
[fc1.weight] GPU 1
[fc2.weight]
测试正是按这个顺序提供梯度:键 0 对应 fc1,键 1 对应 fc2。
| 结构 | 外层类型 | 内容 | 是否复制 Tensor |
|---|---|---|---|
self.params | list | 全部 Parameter 引用 | 否 |
self.gpu_partitions | dict | gpu_id -> Parameter 列表 | 否 |
self.gpu_partitions[0] | list | 当前测试中只有 fc1.weight | 否 |
语法热身:用待处理任务列表练习中点切分
tasks = ['embed', 'attention', 'mlp', 'head']
middle = len(tasks) // 2
owners = {
0: tasks[:middle],
1: tasks[middle:],
}
assert owners[0] == ['embed', 'attention']
assert owners[1] == ['mlp', 'head']例子中的变量 -> Notebook TODO 变量/操作
tasks->self.params:Notebook 先用 list 收集 Parameter 引用。middle->half_idx:都使用列表长度的整除中点。owners->self.gpu_partitions:键是 gpu_id,值是它负责的列表切片。- 字符串任务没有 shape;Notebook 列表元素是 Parameter,各自保留原 shape、dtype、device。
巩固一下
为什么 gpu_partitions 中应保存原 Parameter 引用?
学完这一段,试着做
用一个小动作确认自己理解了;最后再进入官方题目。