深度学习从零开始:理解神经网络中的层与反向传播

深度学习从零开始:理解神经网络中的层与反向传播

deep-learning-from-scratch 『ゼロから作る Deep Learning』(O'Reilly Japan, 2016) deep-learning-from-scratch 项目地址: https://gitcode.com/gh_mirrors/deepl/deep-learning-from-scratch

本文基于《深度学习从零开始》项目中的第五章内容,重点讲解神经网络中层的实现原理以及反向传播机制。我们将从基础层的构建开始,逐步深入到完整的两层神经网络实现,帮助读者理解深度学习中最核心的计算图概念和自动微分原理。

基础层的实现

在神经网络中,最基本的计算单元是层(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

这个实现包含了神经网络训练的所有关键组件:

  1. 参数初始化
  2. 前向传播计算
  3. 损失函数计算
  4. 反向传播计算梯度
  5. 参数更新

梯度检验

为了确保我们的反向传播实现是正确的,通常会进行梯度检验:

# 数值梯度计算
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)

训练过程展示了神经网络学习的典型流程:前向传播计算输出和损失,反向传播计算梯度,然后使用梯度下降法更新参数。通过观察训练集和测试集的准确率,我们可以监控模型的学习情况。

总结

本文通过《深度学习从零开始》项目中的实现,详细讲解了:

  1. 基础层(乘法层、加法层)的实现原理
  2. 反向传播的链式法则应用
  3. 完整的两层神经网络架构
  4. 梯度检验的重要性
  5. 神经网络训练的全过程

理解这些基础概念对于掌握深度学习至关重要。通过从零开始实现这些组件,读者可以深入理解神经网络的工作原理,而不仅仅是调用现成的深度学习框架。

deep-learning-from-scratch 『ゼロから作る Deep Learning』(O'Reilly Japan, 2016) deep-learning-from-scratch 项目地址: https://gitcode.com/gh_mirrors/deepl/deep-learning-from-scratch

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时泓岑Ethanael

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值