self.w = Tensor(np.random.randn(in_features, out_features)*0.01, requires_grad=True)
self.b = Tensor(np.zeros(out_features), requires_grad=True)
def __call__(self, x):
return x.dot(self.w) + self.b # 这里假设我们已经实现了dot运算
```
然后,我们可以像搭乐高一样,把多个层组合成一个顺序模型:
```python
class MyMiniModel:
def __init__(self):
self.layers = [
Linear(784, 128),
ReLU(), # 假设也实现了ReLU激活函数层
Linear(128, 10)
]
def __call__(self, x):
for layer in self.layers:
x = layer(x)
return x
```
模型能输出预测了,我们需要衡量它预测得好不好,这就是损失函数(比如交叉熵损失)。损失函数的输出是一个标量Tensor,它就是整个计算图的终点,也是我们启动反向传播的触发器。
有了梯度,怎么更新参数?优化器登场。最简单的就是随机梯度下降(SGD),它的逻辑直白得可爱:`参数 = 参数 - 学习率*梯度`。
```python
class SGD:
def __init__(self, params, lr=0.01):
self.params = [p for p in params if p.requires_grad]
self.lr = lr
def step(self):
for p in self.params:
p.data -= self.lr*p.grad # 核心更新步骤
def zero_grad(self):
for p in self.params:
p.grad = None # 重要!每轮训练前需清空上一轮的梯度
```
现在,万事俱备,只差一个训练循环把它们串起来。这个循环是每个深度学习开发者最熟悉的节奏:
1.前向传播:输入数据,得到预测和损失。
2.清空梯度:调用 `optimizer.zero_grad()`。
3.反向传播:调用 `loss.backward()`,梯度填满计算图。
4.参数更新:调用 `optimizer.step()`,模型朝着减少损失的方向迈出一小步。
重复这个过程几十、几百个“轮回”(Epoch),你会惊喜地发现,损失在下降,模型开始变得“聪明”了。
为了让你对搭建过程有个全局视角,我梳理了一个简易的路线图:
| 阶段 | 核心任务 | 关键产出 | 可能遇到的“坑” |
|---|---|---|---|
| :--- | :--- | :--- | :--- |
| 第一阶段:奠基 | 实现Tensor类,支持基础运算(add,mul) | 可进行数值计算的对象 | 理解值存储与梯度存储的分离;正确实现`__repr__`方便调试 |
| 第二阶段:注入灵魂 | 实现自动微分系统(Autograd) | 具备`backward()`方法,能计算标量对张量的梯度 | 梯度累加的逻辑;处理广播(broadcasting)情况下的梯度传播 |
| 第三阶段:搭建积木 | 实现常用网络层(Linear,ReLU)和损失函数(MSE,CrossEntropy) | 可以组装出完整的神经网络 | 矩阵运算的维度对齐;损失函数数值稳定性处理(如logsoftmax) |
| 第四阶段:闭环运行 | 实现优化器(SGD)和训练循环 | 一个能完成从数据到模型更新的完整流程 | 学习率设置;梯度爆炸/消失的初步感知;正确地在每个epoch前清零梯度 |
走到这里,你的“迷你框架”已经能跑起来了。但你肯定会立刻发现一堆问题:速度太慢(因为没有用GPU甚至没做向量化优化)、功能太单一(缺少卷积层、RNN层)、内存管理粗糙…… 这太正常了。而这,恰恰是这个项目最大的价值——你亲手触摸到了那些成熟框架在诞生初期所要解决的核心挑战。
所以,回到最初的问题,我们为什么要“从零写AI框架”?
*它是一把“手术刀”,剖开了深度学习框架的黑箱。以后再看到PyTorch的代码,你看到的将不再是魔法,而是一系列清晰的设计决策。
*它是一次深刻的“基础学习”。反向传播、计算图、优化原理,这些概念在动手之后,从课本上的公式变成了你代码里活生生的逻辑。
*它赋予了“调参”和“Debug”的直觉。当模型不收敛时,你可能会本能地去检查梯度流是否正常,因为你知道框架底层大概发生了什么。
当然,我必须诚实地说,这个“玩具框架”离实用相差十万八千里。真实世界的框架需要考虑计算效率(并行、GPU)、自动调度、分布式训练、动态图/静态图、算子融合、内存池、移动端部署…… 每一个点都是一个深不见底的领域。
但无论如何,恭喜你。如果你跟着这个思路走了一遍(哪怕只是在脑子里),你已经完成了一次了不起的“思想实验”。你不再只是一个框架的使用者,你成为了一个理解者,甚至在未来,有可能成为一个创造者。这,就是从零开始的意义。
