深度学习 loss下降后上升在下降_【深度学习理论】纯公式手推+代码撸——神经网络的反向传播+梯度下降...

96b0dff53e2fda77cc2760320a3407cc.png

原文发表于语雀文档:

【深度学习理论】纯公式手推+代码撸——神经网络的反向传播+梯度下降 · 语雀​www.yuque.com

前言

神经网络是深度学习的基础,弄懂神经网络模型中的反向传播不可谓不重要~然鹅道理你都懂,手推一个简单神经网络的反向传播试试? 答:一般用代码写,没试过手推的,不太习惯~ (内心os:不习惯=不会)

aacd04527f7d73b862f699e4d6099458.png

问:那用代码实现一下? 答:试试就试试~

598d301c8ce883596033f8fa300e0193.png

(内心os: 有IDE我怕谁?)

哈哈,开个玩笑,下面我们说正题。本文主要内容就是一个简单的神经网络模型的推导,包括前向传播,反向传播,梯度更新的完整过程,采用手推+代码实现两种方式。其中手推的方式知乎上有完整的实现,本着不重复造轮子的原则,这里我就直接copy过来了。徒手推导我觉得对于加深神经网络的理解是非常有用的,手推完后再用代码实现一遍,对比验证一下,那就perfect了~(P.S.代码是本文原创,pytorch实现)


题目

初始的神经网络模型如下:

5abf0f79614a3ce5a6de4872fcb16ebd.png

i1,i2为输入;中间层/隐藏层只有1层,包含两个神经元节点:h1和h2;然后输出层也是2个节点:o1和o2 w1~w8为对应的权重矩阵;b1 b2是固定的bias,(文章中在反向传播时并没有更新其梯度,重点是更新了w1~w8的梯度)

初始化weight和bias后的矩阵如下:

b460811ecfcf746e2413e925c7c1f0ec.png

网络中神经元的激活函数是logistic函数,损失计算采用mse均方损失,学习率lr = 0.5 求: 1.【前向传播】推导出计算出第一轮前向传播后的损失; 2.【反向传播】求损失对权重矩阵的梯度,并对权重矩阵应用梯度下降

2.手推

手推部分原文出自于博客:a-step-by-step-backpropagation-example 。知乎也有翻译版的文章,看起来比较方便:深度学习---反向传播的具体案例。我这边给出题目描述和前向传播第一次计算的示例,至于反向传播和梯度更新,请大家动手自己推导:)

前向传播

计算h1h2

譬如对h1节点,输入是:

经过logistic激活后,输出是:

执行相同的过程后,h2的输出是:

计算o1o2

h1和h2的输出都求出来后,我们可以算一下o1的输入和输出了:

同理,可得到o2的输出:

求损失

最后,我们可以计算出第一次前向传播过程中的损失:

反向传播

反向传播的手推,大家试一下吧:)不会的可以看:深度学习---反向传播的具体案例

3.代码实现

这一部分,我们用两种方式实现:

  • 1.用torch.tensor构建网络
  • 2.用torch.nn.Module构建网络

第一种方式较为原始,即自己定义网络结构,用tensor连接神经元节点;第二种方式即利用nn.Module;

3.1 用torch.tensor构建网络

import torch
import torch.nn.functional as F


def Network(x):
    """构建tensor网络"""
    print('weight_l1', weight_l1)
    net_l1 = x.mm(weight_l1) + bias_l1
    out_l1 = torch.sigmoid(net_l1)
    net_l2 = out_l1.mm(weight_l2) + bias_l2
    out_l2 = torch.sigmoid(net_l2)
    print('weight_l2', weight_l2)
    return out_l2


# 1. 初始参数设置
lr = 0.5
# 输入输出
input = torch.tensor([[0.05, 0.1]])
truth = torch.tensor([[0.01, 0.99]])
# 权重矩阵
weight_l1 = torch.tensor([[0.15, 0.25],
                          [0.2, 0.3]], requires_grad=True)
weight_l2 = torch.tensor([[0.4, 0.5],
                          [0.45, 0.55]], requires_grad=True)
bias_l1 = torch.tensor([[0.35, 0.35]])
bias_l2 = torch.tensor([[0.6, 0.6]])

# 2. 模型训练
for i in range(10000):
    # 前向传播
    pred = Network(input)
    # 计算损失(mse均方损失)
    losses = F.mse_loss(pred, truth)
    print('epoch: %d loss: %fn================================================='
          % (i, losses.item()))
    # 反向传播
    losses.backward()
    # 更新梯度
    with torch.no_grad():
        weight_l1 -= weight_l1.grad * lr
        weight_l2 -= weight_l2.grad * lr
        # 清空梯度
        weight_l1.grad.zero_()
        weight_l2.grad.zero_()

第1~3轮输出

weight_l1 tensor([[0.1500, 0.2500],        [0.2000, 0.3000]], requires_grad=True)
 weight_l2 tensor([[0.4000, 0.5000],        [0.4500, 0.5500]], requires_grad=True) 
epoch: 1 loss: 0.298371106 
================================================= 
weight_l1 tensor([[0.1498, 0.2498],        [0.1996, 0.2995]], requires_grad=True) 
weight_l2 tensor([[0.3589, 0.5113],        [0.4087, 0.5614]], requires_grad=True) 
epoch: 2 loss: 0.291027814
 ================================================= 
weight_l1 tensor([[0.1496, 0.2495],        [0.1992, 0.2991]], requires_grad=True) 
weight_l2 tensor([[0.3174, 0.5224],        [0.3669, 0.5725]], requires_grad=True) 
epoch: 3 loss: 0.283547163
 =================================================

第10000轮输出

weight_l1 tensor([[0.3785, 0.4773],        [0.6570, 0.7546]], requires_grad=True) 
weight_l2 tensor([[-3.8929,  2.8618],        [-3.8684,  2.9257]], requires_grad=True) 
epoch: 10000 loss: 0.000035109 =================================================

3.2用torch.nn.Module构建网络

import torch
from torch import nn
import torch.nn.functional as F


class Network(nn.Module):
    def __init__(self):
        super().__init__()
        self.weight_l1 = nn.Parameter(torch.tensor([[0.15, 0.25],
                                                    [0.2, 0.3]], requires_grad=True))
        self.weight_l2 = nn.Parameter(torch.tensor([[0.4, 0.5],
                                                    [0.45, 0.55]], requires_grad=True))
        self.bias_l1 = nn.Parameter(torch.tensor([[0.35, 0.35]]))
        self.bias_l2 = nn.Parameter(torch.tensor([[0.6, 0.6]]))

    def forward(self, x):
        print('weight_l1', self.weight_l1)
        net_l1 = x.mm(self.weight_l1) + self.bias_l1
        out_l1 = torch.sigmoid(net_l1)
        net_l2 = out_l1.mm(self.weight_l2) + self.bias_l2
        out_l2 = torch.sigmoid(net_l2)
        print('weight_l2', self.weight_l2)
        return out_l2


# 输入输出
input = torch.tensor([[0.05, 0.1]])
output = torch.tensor([[0.01, 0.99]])
# 构建网络
net = Network()
for i in range(10000):
    # 前向传播
    pred = net(input)
    losses = F.mse_loss(pred, output)
    print('epoch: %d loss: %.9fn================================================='
          % (i, losses.item()))
    # 反向传播
    losses.backward()
    with torch.no_grad():
        net.weight_l1 -= net.weight_l1.grad * 0.5
        net.weight_l2 -= net.weight_l2.grad * 0.5
        net.zero_grad()

第1~3轮输出

weight_l1 Parameter containing: tensor([[0.1500, 0.2500],        [0.2000, 0.3000]], requires_grad=True) 
weight_l2 Parameter containing: tensor([[0.4000, 0.5000],        [0.4500, 0.5500]], requires_grad=True) 
epoch: 1 loss: 0.298371106 
================================================= 
weight_l1 Parameter containing: tensor([[0.1498, 0.2498],        [0.1996, 0.2995]], requires_grad=True) 
weight_l2 Parameter containing: tensor([[0.3589, 0.5113],        [0.4087, 0.5614]], requires_grad=True) 
epoch: 2 loss: 0.291027814 
================================================= 
weight_l1 Parameter containing: tensor([[0.1496, 0.2495],        [0.1992, 0.2991]], requires_grad=True) 
weight_l2 Parameter containing: tensor([[0.3174, 0.5224],        [0.3669, 0.5725]], requires_grad=True) 
epoch: 3 loss: 0.283547163 
=================================================

第10000轮输出

weight_l1 Parameter containing: tensor([[0.3785, 0.4773],        [0.6570, 0.7546]], requires_grad=True) 
weight_l2 Parameter containing: tensor([[-3.8929,  2.8618],        [-3.8684,  2.9257]], requires_grad=True) 
epoch: 10000 loss: 0.000035109 =================================================

可以看见,无论是哪种方式代码求出来的结果还是很准确的,当然要注意每次梯度传播完成后,清空权重weight_l1、weight_l2的梯度(或者直接情况整个网络的梯度)。其实,除了上面的两种方式,还有第三种方式——利用nn.Lenear全连接层的方式构造网络,这里偷个懒就不写了~想要自己实现的童鞋可以参考官网。

神经网络的构建方式: https:// pytorch.org/tutorials/b eginner/nn_tutorial.html

当手推+代码撸出了梯度下降+反向传播,有木有变得更强? 你没有变秃,但是更强了

435fc80b171f7f7ca15fc457d3b1880e.png

哈哈哈~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值