第 1 课作业手册
主题
作业 1:PyTorch 与机器学习基础复习
对应材料
- 作业说明:
ai-model/1/1-prog/hw/1-hw.pdf
-
作业 notebook:
ai-model/1/1-prog/hw/1-cse447_517_HW0-pytorch.ipynb
致谢
本作业主要来自 Yejin Choi 老师的课程
CSE 517/447(Winter 2024)中的 Assignment 0。
同时,作业 notebook 中的部分 PyTorch 教学内容改编自 Stanford
CS224N PyTorch Tutorial。在此一并致谢。
一、这份作业在做什么
这不是一份“拼命写很多代码”的作业,而是一份入门校准作业。它的目标是确认你已经具备后续课程所需的三项基础能力:
- 会使用 PyTorch 的基本 tensor 操作。
- 理解神经网络中的前向传播、损失函数、反向传播和梯度下降。
- 能把“手推出来的训练过程”转化成 PyTorch 代码。
这份作业和第 1 课内容高度一致。它本质上是在把课堂上的概念真正落到代码里。
二、整体结构
这份 notebook 可以分成三段:
Part 1:PyTorch 张量基础
- tensor 创建
- 数据类型
- shape
- reshape
- NumPy 转换
- 向量化运算
- 索引
- 常用激活函数
这部分主要是复习和热身,不是评分重点,但后面所有题目都建立在这些操作之上。
Part 2:手工实现一个小神经网络
这部分是作业核心。作者构造了一个很小的 toy neural network,并让你:
- 手动写 forward pass
- 手动写 backprop
- 手动做一次 gradient descent
- 回答一个关于 loss 是否一定下降的概念题
这部分最能帮助你真正理解“神经网络训练到底在发生什么”。
Part 3:用 PyTorch 自动完成刚才那些事
把前面手工做的内容改写成 PyTorch 版本:
- 使用
requires_grad
- 使用
backward()
- 检查自动梯度和手工梯度是否一致
- 使用
nn.Module 实现同一个 toy model
- 写一个最小训练循环,把 loss 优化到足够低
这部分的重点是:理解 PyTorch 不是魔法,它只是把你前面手工做的步骤自动化了。
三、作业题目概览
根据 notebook,真正需要你完成的题目主要有 5 个。
Exercise 1:手工实现 forward pass
你需要补全 forward(x),完成:
- 第一层线性变换
- 隐层激活
- 第二层线性变换
- 输出激活
本题重点
- 搞清楚每一层输入输出的 shape
- 理解“线性层 + 激活函数”是怎么串起来的
- 把课堂上的网络结构图翻译成矩阵运算代码
本题最容易错的地方
- bias 加法维度不对
- 激活函数位置放错
- 输出 shape 和
Y 对不上
Exercise 2:手工实现 backprop
你需要补全 backprop(x),显式写出:
本题重点
- 真正理解链式法则
-
分清楚:
- 输出对 loss 的梯度
- 中间变量对输出的梯度
- 参数对中间变量的梯度
本题最容易错的地方
- 忘了某一层的激活函数导数
- 梯度 shape 不匹配
sum(0) 的维度没想清楚
学习意义
这道题是整份作业最重要的部分。
如果你能独立写出这题,说明你对“反向传播为什么成立”已经不是停留在口头理解。
Exercise 3:回答概念题
题目问的是:
推荐回答方向
你应该结合学习率和优化地形来思考:
- 学习率太大,可能一步跨过了下降区域
- 损失面可能复杂,不是完美凸函数
- 所以一次更新后 loss 不一定严格下降
本题重点
Exercise 4:用 nn.Module 实现同一个 toy model
你需要定义 ToyModel 类,把前面手工写的网络改成 PyTorch
模块形式。
本题重点
- 理解
__init__() 和 forward() 的职责分工
- 用
nn.Linear、激活函数层搭建模型
- 理解“模型参数由框架管理”是什么意思
本题最容易错的地方
forward() 里没有真正把层串起来
- 输出 shape 不符合预期
- 忘了使用和前面一致的激活逻辑
Exercise 5:写最小优化循环
你需要让 ToyModel 在 toy data 上训练到足够低的
mse loss。
作业要求是:
你需要做的典型步骤
- 定义模型
- 定义优化器
- 前向计算
- 计算 loss
zero_grad()
backward()
step()
- 重复多轮
本题重点
- 训练循环骨架必须彻底记熟
- 这是后面所有深度学习代码的基础模板
本题最容易错的地方
- 忘记
optimizer.zero_grad()
- 忘记
loss.backward()
- 忘记多轮训练
- 学习率不合适,loss 降不下去
四、建议完成顺序
建议按下面节奏做:
第 1 步:先完整跑一遍 notebook
第 2 步:先完成 Exercise 1
- 先只关注 forward
- 确认输出 shape 正确
第 3 步:再做 Exercise 2
- 把 forward 中的中间变量重新理清
- 一步一步推导梯度
第 4 步:跑通 autograd 对照部分
- 检查手工梯度和自动梯度是否接近
- 如果差距很大,优先回头排查 Exercise 2
第 5 步:完成 ToyModel
第 6 步:最后做优化循环
- 让 loss 稳定下降
- 调整 epoch 和学习率,直到达到要求
五、完成这份作业时应该重点观察什么
1. shape
每一步都要问自己:
- 输入 shape 是什么?
- 输出 shape 是什么?
- 参数矩阵 shape 为什么能乘上去?
如果 shape 没搞清楚,后面很多 bug 会看起来像“神秘错误”。
2. 梯度
要理解三件事:
- loss 对输出的梯度是什么
- 梯度如何通过激活函数继续往前传
- 为什么参数梯度要在 batch 上求和
3. 手工实现 vs PyTorch 自动实现
这份作业最有价值的地方就在于:
- 你先自己手工做一遍
- 再看到 PyTorch 如何自动替你完成
如果跳过前半部分,后半部分就会只剩“调库”。
4. 训练是否真的学到了
loss 下降说明模型正在拟合训练数据。
但 notebook 最后也提醒你:
这其实是在提醒你:
六、建议学生记录的关键问题
做作业时建议顺手记下:
- 手工写 forward 时,网络每一层到底在算什么?
- 手工写 backprop 时,最难理解的是哪一段?
- 为什么自动求导能替代手工推梯度?
zero_grad() 为什么一定要有?
- 为什么 loss 下降了,模型仍可能不能很好泛化到新样本?
七、常见卡点
1. Forward 写出来了,但数值很怪
可能原因:
- 激活函数没加
- bias 没加
- shape 广播和你想的不一样
2. Backprop 写不出来
建议做法:
- 不要试图一次全写完
- 先画出计算图
- 从 loss 开始一层层往回推
3. 手工梯度和 autograd 对不上
优先检查:
4. 优化循环 loss 降不下去
优先检查:
- 有没有重复清零梯度
- 有没有忘了
step()
- 学习率是不是过大或过小
- 训练轮数是不是太少
八、建议提交内容
如果你按课程作业形式来收,可以要求学生提交:
- 完整填写后的
.ipynb
-
简短文字说明:
- 你是怎么理解 Exercise 2 的 backprop 的?
- Exercise 3 你的回答是什么?
- 你为 Exercise 5 选了什么优化策略?
如果只提交 notebook,也建议学生保留运行结果,方便检查。
九、这份作业真正想让你带走什么
- 神经网络训练并不神秘,本质就是前向、损失、反向、更新。
- 反向传播不是一句口号,而是可以被手工写出来的链式法则。
- PyTorch 的自动求导只是把这些步骤自动化了。
nn.Module 和优化循环是今后阅读深度学习代码的基础。
-
“能跑通 notebook”只是起点,“能解释每一步为什么这样写”才是真正学会。