深入理解fastai/fastbook:从零构建神经网络基础
引言
在深度学习的实践中,我们经常使用各种高级框架提供的现成模块来构建模型。但真正理解神经网络的工作原理,需要我们深入底层,从最基础的矩阵运算开始构建。本文将基于fastai/fastbook项目中的内容,带您从零开始构建神经网络的核心组件,理解前向传播和反向传播的底层实现。
神经元与神经网络基础
神经元数学模型
神经网络的基本单元是神经元,其数学表达式为:
输出 = Σ(输入 × 权重) + 偏置
用Python代码表示:
output = sum([x*w for x,w in zip(inputs,weights)]) + bias
这个输出随后会通过一个非线性激活函数(如ReLU)进行处理:
def relu(x): return x if x >= 0 else 0
全连接层实现
当我们把多个神经元组合成全连接层(又称密集层或线性层)时,就需要处理批量输入与多个神经元的权重矩阵相乘的问题。假设:
- 输入矩阵x的大小为batch_size × n_inputs
- 权重矩阵w的大小为n_neurons × n_inputs
- 偏置向量b的大小为n_neurons
那么全连接层的输出可以表示为:
y = x @ w.t() + b
其中@
表示矩阵乘法,.t()
表示矩阵转置。
从零实现矩阵乘法
基础实现
我们先不使用PyTorch的矩阵乘法,而是用纯Python实现:
def matmul(a,b):
ar,ac = a.shape
br,bc = b.shape
assert ac==br
c = torch.zeros(ar, bc)
for i in range(ar):
for j in range(bc):
for k in range(ac): c[i,j] += a[i,k] * b[k,j]
return c
这个实现使用了三重循环,效率非常低。在实际测试中,PyTorch内置的矩阵乘法比这个实现快约100,000倍!
优化实现
我们可以利用PyTorch的张量操作来优化,首先去掉最内层的循环:
def matmul(a,b):
ar,ac = a.shape
br,bc = b.shape
assert ac==br
c = torch.zeros(ar, bc)
for i in range(ar):
for j in range(bc):
c[i,j] = (a[i] * b[:,j]).sum()
return c
这个优化使速度提升了约700倍,但仍有提升空间。
PyTorch张量操作技巧
元素级运算
PyTorch支持各种元素级运算(加减乘除、比较等),这些运算在相同形状的张量间进行:
a = tensor([10., 6, -4])
b = tensor([2., 8, 7])
a + b # 返回 tensor([12., 14., 3.])
a < b # 返回 tensor([False, True, True])
广播机制
广播是PyTorch/NumPy中处理不同形状张量运算的重要机制。基本规则是:
- 从最后一个维度开始向前比较
- 两个维度要么相同,要么其中一个为1,或者其中一个不存在
例如标量与张量的运算:
a = tensor([10., 6, -4])
a > 0 # 返回 tensor([True, True, False])
这里标量0被"广播"成与a相同形状的张量[0,0,0]。
进一步优化矩阵乘法
利用广播机制,我们可以进一步优化矩阵乘法实现:
def matmul(a,b):
ar,ac = a.shape
br,bc = b.shape
assert ac==br
c = torch.zeros(ar, bc)
for i in range(ar):
c[i] = (a[i].unsqueeze(-1) * b).sum(dim=0)
return c
这个实现利用了广播来避免显式的列循环,效率更高。
构建神经网络层
基于上述矩阵乘法实现,我们可以构建一个完整的神经网络层:
def linear_layer(x, w, b):
return x @ w.t() + b
def relu(x):
return x.clamp_min(0.)
总结
通过从零开始实现神经网络的基础组件,我们深入理解了:
- 神经元和全连接层的数学原理
- 矩阵乘法的高效实现方式
- PyTorch张量操作和广播机制
- 如何组合这些基础组件构建神经网络层
这种底层实现虽然在实际项目中不常用,但对于理解深度学习的工作原理至关重要。在后续学习中,我们将基于这些知识实现更复杂的网络结构和训练过程。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考