深度学习从零开始:理解神经网络中的层与反向传播
本文基于《深度学习从零开始》项目中的第五章内容,重点讲解神经网络中层的实现原理以及反向传播机制。我们将从基础层的构建开始,逐步深入到完整的两层神经网络实现,帮助读者理解深度学习中最核心的计算图概念和自动微分原理。
基础层的实现
在神经网络中,最基本的计算单元是层(layer)。我们先来看两种最基本的层实现:乘法层和加法层。
乘法层(MulLayer)
乘法层实现了两个输入数据的乘积运算及其反向传播:
class MulLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x # 保存输入值用于反向传播
self.y = y
return x * y
def backward(self, dout):
dx = dout * self.y # 上游梯度乘以另一个输入值
dy = dout * self.x
return dx, dy
乘法层的反向传播遵循微积分中的链式法则。假设正向传播计算为z = x * y,那么反向传播时:
- dz/dx = y
- dz/dy = x
加法层(AddLayer)
加法层实现了两个输入数据的求和运算及其反向传播:
class AddLayer:
def __init__(self):
pass # 加法层不需要保存任何中间状态
def forward(self, x, y):
return x + y
def backward(self, dout):
dx = dout * 1 # 加法运算的导数为1
dy = dout * 1
return dx, dy
加法层的反向传播更为简单,因为对于z = x + y,有:
- dz/dx = 1
- dz/dy = 1
实际应用:购买水果案例
为了理解这些层如何工作,我们来看一个实际例子:计算购买苹果和橙子的总价格(含税)。
只购买苹果的情况
apple = 100 # 苹果单价
apple_num = 2 # 苹果数量
tax = 1.1 # 税率
# 前向传播
apple_price = apple * apple_num # 计算苹果总价
price = apple_price * tax # 计算含税价格
# 反向传播
dprice = 1 # 初始梯度
dapple_price = dprice * tax # 价格对苹果总价的导数
dtax = dprice * apple_price # 价格对税率的导数
dapple = dapple_price * apple_num # 苹果总价对单价的导数
dapple_num = dapple_price * apple # 苹果总价对数量的导数
这个例子展示了如何通过链式法则计算每个变量对最终价格的"影响程度"。
同时购买苹果和橙子的情况
当计算更复杂的购买组合时,我们需要组合使用乘法和加法层:
# 前向传播
apple_price = apple * apple_num # 苹果总价
orange_price = orange * orange_num # 橙子总价
all_price = apple_price + orange_price # 水果总价
price = all_price * tax # 含税价格
# 反向传播
dprice = 1
dall_price, dtax = dprice * tax, dprice * all_price
dapple_price, dorange_price = dall_price * 1, dall_price * 1
dapple, dapple_num = dapple_price * apple_num, dapple_price * apple
dorange, dorange_num = dorange_price * orange_num, dorange_price * orange
这个例子展示了计算图中多个操作组合时的反向传播过程。
两层神经网络的实现
理解了基础层的工作原理后,我们可以构建一个完整的两层神经网络:
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size):
# 初始化权重参数
self.params = {}
self.params['W1'] = np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
# 构建网络层
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
self.lastLayer = SoftmaxWithLoss()
def predict(self, x):
# 前向传播
for layer in self.layers.values():
x = layer.forward(x)
return x
def loss(self, x, t):
# 计算损失
y = self.predict(x)
return self.lastLayer.forward(y, t)
def gradient(self, x, t):
# 反向传播计算梯度
self.loss(x, t) # 前向传播
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
# 收集各层梯度
grads = {}
grads['W1'] = self.layers['Affine1'].dW
grads['b1'] = self.layers['Affine1'].db
grads['W2'] = self.layers['Affine2'].dW
grads['b2'] = self.layers['Affine2'].db
return grads
这个实现包含了神经网络训练的所有关键组件:
- 参数初始化
- 前向传播计算
- 损失函数计算
- 反向传播计算梯度
- 参数更新
梯度检验
为了确保我们的反向传播实现是正确的,通常会进行梯度检验:
# 数值梯度计算
grad_numerical = network.numerical_gradient(x_batch, t_batch)
# 反向传播计算梯度
grad_backprop = network.gradient(x_batch, t_batch)
# 比较两种方法的差异
for key in grad_numerical.keys():
diff = np.average(np.abs(grad_backprop[key] - grad_numerical[key]))
print(f"{key}: {diff}")
梯度检验通过比较数值梯度(通过微小扰动计算)和反向传播梯度来验证实现的正确性。如果两者差异很小(通常小于1e-7),则可以认为反向传播实现是正确的。
神经网络训练
最后,我们来看完整的训练过程:
for i in range(iters_num):
# 随机选择mini-batch
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
# 计算梯度
grad = network.gradient(x_batch, t_batch)
# 更新参数
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
# 记录学习过程
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
# 每个epoch计算准确率
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
print(train_acc, test_acc)
训练过程展示了神经网络学习的典型流程:前向传播计算输出和损失,反向传播计算梯度,然后使用梯度下降法更新参数。通过观察训练集和测试集的准确率,我们可以监控模型的学习情况。
总结
本文通过《深度学习从零开始》项目中的实现,详细讲解了:
- 基础层(乘法层、加法层)的实现原理
- 反向传播的链式法则应用
- 完整的两层神经网络架构
- 梯度检验的重要性
- 神经网络训练的全过程
理解这些基础概念对于掌握深度学习至关重要。通过从零开始实现这些组件,读者可以深入理解神经网络的工作原理,而不仅仅是调用现成的深度学习框架。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考