日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

手把手推導Back Propagation

 520jefferson 2022-05-13
Image

撰文|月踏

BP(Back Propagation)是深度學習神經網絡的理論核心,本文通過兩個例子展示手動推導BP的過程。

1
鏈式法則

鏈式法則是BP的核心,分兩種情況:


1. 一元方程

在一元方程的情況下,鏈式法則比較簡單,假設存在下面兩個函數:

 
Image
那么x的變化最終會影響到z的值,用數學符號表示如下: 

Image


z對x的微分可以表示如下: 

Image

 
2. 多元方程

在多元方程的情況下,鏈式法則稍微復雜一些,假設存在下面三個函數: 
 
Image

因為s的微小變化會通過g(s)和h(s)兩條路徑來影響z的結果,這時z對s的微分可以表示如下: 

Image
 
這就是鏈式法則的全部內容,后面用實際例子來推導BP的具體過程。

2
只有一個weight的簡單情況

做了一個簡單的網絡,這可以對應到鏈式法則的第一種情況,如下圖所示:
ImageImage
圖1

其中圓形表示葉子節(jié)點,方塊表示非葉子節(jié)點,每個非葉子節(jié)點的定義如下,訓練過程中的前向過程會根據這些公式進行計算: 

Image

這個例子中,我們是想更新w1、b1、w2三個參數值,假如用lr表示learning rate,那么它們的更新公式如下: 
 
Image

在訓練開始之前,b1、w1、w2都會被初始化成某個值,在訓練開始之后,參數根據下面兩個步驟來進行更新:

  1. 先進行一次前向計算,這樣可以得到y(tǒng)1、y2、y3、loss的值
  2. 再進行一次反向計算,得到每個參數的梯度值,進而根據上面的公式(13)、(14)、(15)來更新參數值

下面看下反向傳播時的梯度的計算過程,因為梯度值是從后往前計算的,所以先看w2的梯度計算: 

Image
 
再繼續(xù)看w1的梯度計算: 

Image
 
最后看b1的梯度計算: 

Image

 把w2、w1、b1的梯度計算出來之后,就可以按照公式(13)、(14)、(15)來更新參數值了,下面用OneFlow按照圖1搭建一個對應的網絡做實驗,代碼如下:

import oneflow as ofimport oneflow.nn as nnimport oneflow.optim as optim
class Sample(nn.Module): def __init__(self): super(Sample, self).__init__() self.w1 = of.tensor(10.0, dtype=of.float, requires_grad=True) self.b1 = of.tensor(1.0, dtype=of.float, requires_grad=True) self.w2 = of.tensor(20.0, dtype=of.float, requires_grad=True) self.loss = nn.MSELoss()
def parameters(self): return [self.w1, self.b1, self.w2]
def forward(self, x, label): y1 = self.w1 * x + self.b1 y2 = y1 * self.w2 y3 = 2 * y2 return self.loss(y3, label)
model = Sample()
optimizer = optim.SGD(model.parameters(), lr=0.005)data = of.tensor(1.0, dtype=of.float)label = of.tensor(500.0, dtype=of.float)
loss = model(data, label)print('------------before backward()---------------')print('w1 =', model.w1)print('b1 =', model.b1)print('w2 =', model.w2)print('w1.grad =', model.w1.grad)print('b1.grad =', model.b1.grad)print('w2.grad =', model.w2.grad)loss.backward()print('------------after backward()---------------')print('w1 =', model.w1)print('b1 =', model.b1)print('w2 =', model.w2)print('w1.grad =', model.w1.grad)print('b1.grad =', model.b1.grad)print('w2.grad =', model.w2.grad)optimizer.step()print('------------after step()---------------')print('w1 =', model.w1)print('b1 =', model.b1)print('w2 =', model.w2)print('w1.grad =', model.w1.grad)print('b1.grad =', model.b1.grad)print('w2.grad =', model.w2.grad)optimizer.zero_grad()print('------------after zero_grad()---------------')print('w1 =', model.w1)print('b1 =', model.b1)print('w2 =', model.w2)print('w1.grad =', model.w1.grad)print('b1.grad =', model.b1.grad)print('w2.grad =', model.w2.grad)

這段代碼只跑了一次forward和一次backward,然后調用step更新了參數信息,最后調用zero_grad來對這一輪backward算出來的梯度信息進行了清零,運行結果如下:

------------before backward()---------------w1 = tensor(10., requires_grad=True)b1 = tensor(1., requires_grad=True)w2 = tensor(20., requires_grad=True)w1.grad = Noneb1.grad = Nonew2.grad = None------------after backward()---------------w1 = tensor(10., requires_grad=True)b1 = tensor(1., requires_grad=True)w2 = tensor(20., requires_grad=True)w1.grad = tensor(-4800.)b1.grad = tensor(-4800.)w2.grad = tensor(-2640.)------------after step()---------------w1 = tensor(34., requires_grad=True)b1 = tensor(25., requires_grad=True)w2 = tensor(33.2000, requires_grad=True)w1.grad = tensor(-4800.)b1.grad = tensor(-4800.)w2.grad = tensor(-2640.)------------after zero_grad()---------------w1 = tensor(34., requires_grad=True)b1 = tensor(25., requires_grad=True)w2 = tensor(33.2000, requires_grad=True)w1.grad = tensor(0.)b1.grad = tensor(0.)w2.grad = tensor(0.)

3
以conv為例的含有多個weights的情況

用一個非常簡單的conv來舉例,這個conv的各種屬性如下:

Image

如下圖所示:
 

Image

圖2

假定這個例子中的網絡結構如下圖:

Image
圖3

在這個簡單的網絡中,z節(jié)點表示一個avg-pooling的操作,kernel是2x2,loss采用均方誤差,下面是對應的公式:

Image

前傳部分同上一節(jié)一樣,直接看反傳過程,目的是為了求w0、w1、w2、w3的梯度,并更新這四個參數值,以下是求w0梯度的過程: 

Image

下面是求w1、w2、w3梯度的過程類似,直接寫出結果:

Image
 
最后再按照下面公式來更新參數即可: 

Image

用OneFlow按照圖3來搭建一個對應的網絡做實驗,代碼如下:

import oneflow as ofimport oneflow.nn as nnimport oneflow.optim as optim
class Sample(nn.Module): def __init__(self): super(Sample, self).__init__() self.op1 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(2,2), bias=False) self.op2 = nn.AvgPool2d(kernel_size=(2,2)) self.loss = nn.MSELoss()
def forward(self, x, label): y1 = self.op1(x) y2 = self.op2(y1) return self.loss(y2, label)
model = Sample()
optimizer = optim.SGD(model.parameters(), lr=0.005)data = of.randn(1, 1, 3, 3)label = of.randn(1, 1, 1, 1)
loss = model(data, label)print('------------before backward()---------------')param = model.parameters()print('w =', next(param))loss.backward()print('------------after backward()---------------')param = model.parameters()print('w =', next(param))optimizer.step()print('------------after step()---------------')param = model.parameters()print('w =', next(param))optimizer.zero_grad()print('------------after zero_grad()---------------')param = model.parameters()print('w =', next(param))
輸出如下(里面的input、param、label的值都是隨機的,每次運行的結果會不一樣):

------------before backward()---------------w = tensor([[[[ 0.2621, -0.2583], [-0.1751, -0.0839]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)------------after backward()---------------w = tensor([[[[ 0.2621, -0.2583], [-0.1751, -0.0839]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)------------after step()---------------w = tensor([[[[ 0.2587, -0.2642], [-0.1831, -0.0884]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)------------after zero_grad()---------------w = tensor([[[[ 0.2587, -0.2642],          [-0.1831, -0.0884]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)

參考資料:
1.http://speech.ee./~tlkagk/courses.html
2.https://speech.ee./~hylee/index.php
3.https://www./c/HungyiLeeNTU

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多