1. 程式人生 > >神經網路之BP演算法

神經網路之BP演算法

文章轉載於http://www.cnblogs.com/charlotte77/p/5629865.html。 

最近在看深度學習的東西,一開始看的吳恩達的UFLDL教程,有中文版就直接看了,後來發現有些地方總是不是很明確,又去看英文版,然後又找了些資料看,才發現,中文版的譯者在翻譯的時候會對省略的公式推導過程進行補充,但是補充的又是錯的,難怪覺得有問題。反向傳播法其實是神經網路的基礎了,但是很多人在學的時候總是會遇到一些問題,或者看到大篇的公式覺得好像很難就退縮了,其實不難,就是一個鏈式求導法則反覆用。如果不想看公式,可以直接把數值帶進去,實際的計算一下,體會一下這個過程之後再來推導公式,這樣就會覺得很容易了。

  說到神經網路,大家看到這個圖應該不陌生:

  這是典型的三層神經網路的基本構成,Layer L1是輸入層,Layer L2是隱含層,Layer L3是隱含層,我們現在手裡有一堆資料{x1,x2,x3,...,xn},輸出也是一堆資料{y1,y2,y3,...,yn},現在要他們在隱含層做某種變換,讓你把資料灌進去後得到你期望的輸出。如果你希望你的輸出和原始輸入一樣,那麼就是最常見的自編碼模型(Auto-Encoder)。可能有人會問,為什麼要輸入輸出都一樣呢?有什麼用啊?其實應用挺廣的,在影象識別,文字分類等等都會用到,我會專門再寫一篇Auto-Encoder的文章來說明,包括一些變種之類的。如果你的輸出和原始輸入不一樣,那麼就是很常見的人工神經網路了,相當於讓原始資料通過一個對映來得到我們想要的輸出資料,也就是我們今天要講的話題。

  本文直接舉一個例子,帶入數值演示反向傳播法的過程,公式的推導等到下次寫Auto-Encoder的時候再寫,其實也很簡單,感興趣的同學可以自己推導下試試:)(注:本文假設你已經懂得基本的神經網路構成,如果完全不懂,可以參考Poll寫的筆記:)

  假設,你有這樣一個網路層:

  第一層是輸入層,包含兩個神經元i1,i2,和截距項b1;第二層是隱含層,包含兩個神經元h1,h2和截距項b2,第三層是輸出o1,o2,每條線上標的wi是層與層之間連線的權重,啟用函式我們預設為sigmoid函式。

  現在對他們賦上初值,如下圖:

  其中,輸入資料  i1=0.05,i2=0.10;

     輸出資料 o1=0.01,o2=0.99;

     初始權重  w1=0.15,w2=0.20,w3=0.25,w4=0.30;

           w5=0.40,w6=0.45,w7=0.50,w8=0.55;

  目標:給出輸入資料i1,i2(0.05和0.10),使輸出儘可能與原始輸出o1,o2(0.01和0.99)接近。

  Step 1 前向傳播

  1.輸入層---->隱含層:

  計算神經元h1的輸入加權和:

神經元h1的輸出o1:(此處用到啟用函式為sigmoid函式):

  同理,可計算出神經元h2的輸出o2:

  

  2.隱含層---->輸出層:

  計算輸出層神經元o1和o2的值:

  

這樣前向傳播的過程就結束了,我們得到輸出值為[0.75136079 , 0.772928465],與實際值[0.01 , 0.99]相差還很遠,現在我們對誤差進行反向傳播,更新權值,重新計算輸出。

Step 2 反向傳播

1.計算總誤差

總誤差:(square error)

但是有兩個輸出,所以分別計算o1和o2的誤差,總誤差為兩者之和:

2.隱含層---->輸出層的權值更新:

以權重引數w5為例,如果我們想知道w5對整體誤差產生了多少影響,可以用整體誤差對w5求偏導求出:(鏈式法則)

下面的圖可以更直觀的看清楚誤差是怎樣反向傳播的:

現在我們來分別計算每個式子的值:

計算

計算

(這一步實際上就是對sigmoid函式求導,比較簡單,可以自己推導一下)

計算

最後三者相乘:

這樣我們就計算出整體誤差E(total)對w5的偏導值。

回過頭來再看看上面的公式,我們發現:

為了表達方便,用來表示輸出層的誤差:

因此,整體誤差E(total)對w5的偏導公式可以寫成:

如果輸出層誤差計為負的話,也可以寫成:

最後我們來更新w5的值:

(其中,是學習速率,這裡我們取0.5)

同理,可更新w6,w7,w8:

3.隱含層---->隱含層的權值更新:

 方法其實與上面說的差不多,但是有個地方需要變一下,在上文計算總誤差對w5的偏導時,是從out(o1)---->net(o1)---->w5,但是在隱含層之間的權值更新時,是out(h1)---->net(h1)---->w1,而out(h1)會接受E(o1)和E(o2)兩個地方傳來的誤差,所以這個地方兩個都要計算。

計算

先計算

同理,計算出:

          

兩者相加得到總值:

再計算

再計算

最後,三者相乘:

 為了簡化公式,用sigma(h1)表示隱含層單元h1的誤差:

最後,更新w1的權值:

同理,額可更新w2,w3,w4的權值:

  這樣誤差反向傳播法就完成了,最後我們再把更新的權值重新計算,不停地迭代,在這個例子中第一次迭代之後,總誤差E(total)由0.298371109下降至0.291027924。迭代10000次後,總誤差為0.000035085,輸出為[0.015912196,0.984065734](原輸入為[0.01,0.99]),證明效果還是不錯的。

下面是我自己寫的matlab程式,雖然達到了預期的效果,但是程式洗的還不太成熟

clc;
clear all;
close all;
%初始化
i1=0.05;i2=0.10;
targeto1=0.01;targeto2=0.99;
b1=0.35;b2=0.60;
w1=0.15;w2=0.20;w3=0.25;w4=0.30;
w5=0.40;w6=0.45;w7=0.50;w8=0.55;
epochs=10000;lr=0.5;
%預置記憶體
d_Etotal2w1=[0,0];d_Etotal2w2=[0,0];
W1_new=[0,0];W2_new=[0,0];W3_new=[0,0];W4_new=[0,0];
d_Eo12outh12=[0,0];d_Etotal2outh12=[0,0];d_Eo22outh12=[0,0];
d_Etotal2w12=[0,0];d_Etotal2w34=[0,0];
for i=1:epochs
   %% 前向傳播
    %輸入層到隱含層
    neth1=w1*i1+w2*i2+b1;
    outh1=1./(1+exp(-neth1));
    neth2=w3*i1+w4*i2+b1;
    outh2=1./(1+exp(-neth2));
    %隱含層到輸出層
    neto1=w5*outh1+w6*outh2+b2;
    outo1=1./(1+exp(-neto1));
    neto2=w7*outh1+w8*outh2+b2;
    outo2=1./(1+exp(-neto2));
    %% 反向傳播
    %計算總誤差
    Eo1=0.5*(targeto1-outo1)^2;
    Eo2=0.5*(targeto2-outo2)^2;
    Etotal=Eo1+Eo2;
    %隱含層到輸出層的權值更新
    W1=[w5,w6];W2=[w7,w8];W3=[w1,w2];W4=[w3,w4];
    for m=1:2
        %更新w5,w6
        d_Etotal2outo1=-(targeto1-outo1);
        d_outo12neto1=outo1*(1-outo1);
        d_neto12w=[outh1,outh2];
        d_Etotal2w1(m)=d_Etotal2outo1*d_outo12neto1*d_neto12w(m);
        W1_new(m)=W1(m)-lr*d_Etotal2w1(m);
        %更新w7,w8
        d_Etotal2outo2=-(targeto2-outo2);
        d_outo22neto2=outo2*(1-outo2);
        d_neto22w=[outh1,outh2];
        d_Etotal2w2(m)=d_Etotal2outo2*d_outo22neto2*d_neto22w(m);
        W2_new(m)=W2(m)-lr*d_Etotal2w2(m);
        %更新w1,w2
        d_neto12outh12=[w5,w6];
        d_Eo12outo1=d_Etotal2outo1;
        d_Eo12neto1=d_Eo12outo1*d_outo12neto1;
        d_Eo12outh12(m)=d_Eo12neto1*d_neto12outh12(m);
        d_neto22outh12=[w7,w8];
        d_Eo22outo2=d_Etotal2outo2;
        d_Eo22neto2=d_Eo22outo2*d_outo22neto2;
        d_Eo22outh12(m)=d_Eo22neto2*d_neto22outh12(m);
        d_Etotal2outh12(m)=d_Eo12outh12(m)+d_Eo22outh12(m);
        d_outh12neth1=outh1*(1-outh1);
        d_neth12w12=[i1,i2];
        d_Etotal2w12(m)=d_Etotal2outh12(m)*d_outh12neth1*d_neth12w12(m);
        W3_new(m)=W3(m)-lr*d_Etotal2w12(m);   
        d_outh12neth2=outh2*(1-outh2);    
        d_Etotal2w34(m)=d_Etotal2outh12(m)*d_outh12neth2*d_neth12w12(m);
        W4_new(m)=W4(m)-lr*d_Etotal2w34(m);
    end
    w5=W1_new(1);w6=W1_new(2);
    w7=W2_new(1);w8=W2_new(2);
    w1=W3_new(1);w2=W3_new(2);
    w3=W4_new(1);w4=W4_new(2);
end


原文作者的python程式:

 #coding:utf-8
   import random
   import math
   
   #
   #   引數解釋:
   #   "pd_" :偏導的字首
   #   "d_" :導數的字首
   #   "w_ho" :隱含層到輸出層的權重係數索引
  #   "w_ih" :輸入層到隱含層的權重係數的索引
  
  class NeuralNetwork:
      LEARNING_RATE = 0.5
  
      def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):
          self.num_inputs = num_inputs
  
          self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)
          self.output_layer = NeuronLayer(num_outputs, output_layer_bias)
  
          self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)
          self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)
  
      def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):
          weight_num = 0
          for h in range(len(self.hidden_layer.neurons)):
              for i in range(self.num_inputs):
                  if not hidden_layer_weights:
                      self.hidden_layer.neurons[h].weights.append(random.random())
                  else:
                      self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])
                  weight_num += 1
  
     def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):
          weight_num = 0
          for o in range(len(self.output_layer.neurons)):
              for h in range(len(self.hidden_layer.neurons)):
                  if not output_layer_weights:
                      self.output_layer.neurons[o].weights.append(random.random())
                 else:
                      self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])
                  weight_num += 1
  
      def inspect(self):
          print('------')
          print('* Inputs: {}'.format(self.num_inputs))
          print('------')
          print('Hidden Layer')
          self.hidden_layer.inspect()
          print('------')
          print('* Output Layer')
          self.output_layer.inspect()
          print('------')
  
      def feed_forward(self, inputs):
          hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)
          return self.output_layer.feed_forward(hidden_layer_outputs)
  
      def train(self, training_inputs, training_outputs):
          self.feed_forward(training_inputs)
  
          # 1. 輸出神經元的值
          pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)
          for o in range(len(self.output_layer.neurons)):
  
              # ∂E/∂zⱼ
              pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])
  
          # 2. 隱含層神經元的值
          pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
          for h in range(len(self.hidden_layer.neurons)):
  
              # dE/dyⱼ = Σ ∂E/∂zⱼ * ∂z/∂yⱼ = Σ ∂E/∂zⱼ * wᵢⱼ
              d_error_wrt_hidden_neuron_output = 0
              for o in range(len(self.output_layer.neurons)):
                  d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]
  
              # ∂E/∂zⱼ = dE/dyⱼ * ∂zⱼ/∂
              pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()
  
          # 3. 更新輸出層權重係數
          for o in range(len(self.output_layer.neurons)):
              for w_ho in range(len(self.output_layer.neurons[o].weights)):
  
                  # ∂Eⱼ/∂wᵢⱼ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢⱼ
                  pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)
  
                  # Δw = α * ∂Eⱼ/∂wᵢ
                  self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight
  
          # 4. 更新隱含層的權重係數
          for h in range(len(self.hidden_layer.neurons)):
              for w_ih in range(len(self.hidden_layer.neurons[h].weights)):
  
                  # ∂Eⱼ/∂wᵢ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢ
                  pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)
  
                  # Δw = α * ∂Eⱼ/∂wᵢ
                  self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight
 
     def calculate_total_error(self, training_sets):
         total_error = 0
         for t in range(len(training_sets)):
             training_inputs, training_outputs = training_sets[t]
             self.feed_forward(training_inputs)
             for o in range(len(training_outputs)):
                 total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
         return total_error
 
 class NeuronLayer:
     def __init__(self, num_neurons, bias):
 
         # 同一層的神經元共享一個截距項b
         self.bias = bias if bias else random.random()
 
         self.neurons = []
         for i in range(num_neurons):
             self.neurons.append(Neuron(self.bias))
 
     def inspect(self):
         print('Neurons:', len(self.neurons))
         for n in range(len(self.neurons)):
             print(' Neuron', n)
             for w in range(len(self.neurons[n].weights)):
                 print('  Weight:', self.neurons[n].weights[w])
             print('  Bias:', self.bias)
 
     def feed_forward(self, inputs):
         outputs = []
         for neuron in self.neurons:
             outputs.append(neuron.calculate_output(inputs))
         return outputs
 
     def get_outputs(self):
         outputs = []
         for neuron in self.neurons:
             outputs.append(neuron.output)
         return outputs
 
 class Neuron:
     def __init__(self, bias):
         self.bias = bias
         self.weights = []
 
     def calculate_output(self, inputs):
         self.inputs = inputs
         self.output = self.squash(self.calculate_total_net_input())
         return self.output
 
     def calculate_total_net_input(self):
         total = 0
         for i in range(len(self.inputs)):
             total += self.inputs[i] * self.weights[i]
         return total + self.bias
 
     # 啟用函式sigmoid
     def squash(self, total_net_input):
         return 1 / (1 + math.exp(-total_net_input))
 
 
     def calculate_pd_error_wrt_total_net_input(self, target_output):
         return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();
 
     # 每一個神經元的誤差是由平方差公式計算的
     def calculate_error(self, target_output):
         return 0.5 * (target_output - self.output) ** 2
 
     
     def calculate_pd_error_wrt_output(self, target_output):
         return -(target_output - self.output)
 
     
     def calculate_pd_total_net_input_wrt_input(self):
         return self.output * (1 - self.output)
 
 
     def calculate_pd_total_net_input_wrt_weight(self, index):
         return self.inputs[index]
 
 
 # 文中的例子:
 
 nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)
 for i in range(10000):
     nn.train([0.05, 0.1], [0.01, 0.09])
     print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))
 
 
 #另外一個例子,可以把上面的例子註釋掉再執行一下:
 
 # training_sets = [
 #     [[0, 0], [0]],
 #     [[0, 1], [1]],
 #     [[1, 0], [1]],
 #     [[1, 1], [0]]
 # ]
 
 # nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
 # for i in range(10000):
 #     training_inputs, training_outputs = random.choice(training_sets)
 #     nn.train(training_inputs, training_outputs)
 #     print(i, nn.calculate_total_error(training_sets))

相關推薦

神經網路BP演算法

文章轉載於http://www.cnblogs.com/charlotte77/p/5629865.html。  最近在看深度學習的東西,一開始看的吳恩達的UFLDL教程,有中文版就直接看了,後來發現有些地方總是不是很明確,又去看英文版,然後又找了些資料看,才發現,中文

前向神經網路BP 演算法詳解 --- DNN

前向神經網路和 BP 演算法詳解 一、神經網路的概念詳解 1.1、人工神經網路的基礎單元 — > 感知機 1.1.1、感知機模型講解 首先我們需要明確一點就是,針對於擁有核函式的 SVM 或者多隱層 + 啟用函式的多層神

簡單易學的機器學習演算法——神經網路BP神經網路

%% BP的主函式 % 清空 clear all; clc; % 匯入資料 load data; %從1到2000間隨機排序 k=rand(1,2000); [m,n]=sort(k); %輸入輸出資料 input=data(:,2:25); output1 =data(:,1); %把輸出從1維變

神經網路BP演算法推導

我的原文:www.hijerry.cn/p/53364.htm… 感知機 感知機(perceptron)於1957年由Rosenblatt提出,是一種二分類線性模型。感知機以樣本特徵向量作為輸入,輸出為預測類別,取正、負兩類。感知機最終學習到的是將輸入空間(特徵空間)劃分為正、負兩類的分離超平面,屬於判別

神經網路BP演算法

5. 神經網路 5.1 前向傳播 神經網路分為很多層,包括輸入層、輸出層和中間的隱層。 使用的符號如下: a(j)i a i

西瓜書5.5 程式設計實現BP神經網路——標準BP演算法、累積BP演算法

這裡照著書上的公式,實現了一下標準BP演算法,和累積BP演算法,BP是error Back Propagation的意思,誤差逆傳播。BP網路通常是指用BP演算法訓練的多層前饋神經網路。程式碼是照著書本公式自己寫的,沒有參考網上的其他版本。 資料和程式碼地址:https:/

機器學習——神經網路累積BP演算法

在上篇的部落格介紹的“標準BP演算法”每次僅針對一個訓練樣例更新連線權和閾值,也就是說,BP演算法中的更新規則是基於單個的 推導而得,如果類似的推匯出基於累積誤差最小化的更新規則,就得到了累積誤差逆傳播

神經網路BP演算法推導

導言 神經網路是深度學習基礎,BP演算法是神經網路訓練中最基礎的演算法。因此,對神經網路結構和BP演算法進行梳理是理解深度學習的有效方法。參考資料UFLDL,BP推導,神經網路教材。 神經網路結構 典型網路為淺層網路,一般2~4層。其結構如下圖所示:

多層前饋神經網路BP演算法

1.多層前饋神經網路 首先說下多層前饋神經網路,BP演算法,BP神經網路之間的關係。多層前饋(multilayer feed-forward)神經網路由一個輸入層、一個或多個隱藏層和一個輸出層組成,後向傳播(BP)演算法在多層前饋神經網路上面進行學習,採用BP

神經網路BP演算法的原理與用Python實現原始碼

(1)什麼是梯度下降和鏈式求導法則 1.梯度下降 假設我們有一個函式J(w),如下圖所示。 現在,我們要求當w等於什麼的時候,J(w)能夠取到最小值。從圖中我們知道最小值在初始位置的左邊,也就意味著如果想要使J(w)最小,w的值需要減小。而初始位置的切線斜率a>0

卷積神經網路反向BP演算法公式推導

博文轉載至:http://blog.csdn.net/celerychen2009/article/details/8964753 此篇博文只涉及到公式推導,如果想了解卷積神經網路的具體工作過程,可檢視轉載博文博主其它文件或者百度自己去看。轉載的文章涉及到的角下標大家注意下

前饋神經網路BP演算法

單層感知機輸入層——>輸出層啟用函式在輸出層,常見啟用函式Logistic、softmax函式多層感知機(MLP)輸入層——>隱藏層——>輸出層啟用函式在隱藏層,線性變換預啟用+非線性啟用函式,資料分佈“線性可分”隱藏層所含神經元越多,可提取特徵越多常用啟用

機器學習總結(七):基本神經網路BP演算法、常用啟用函式對比

1.   神經網路 (1)為什麼要用神經網路? 對於非線性分類問題,如果用多元線性迴歸進行分類,需要構造許多高次項,導致特徵特多學習引數過多,從而複雜度太高。 (2)常用的啟用函式及其優缺點 階

從 0 開始機器學習 - 神經網路反向 BP 演算法

最近一個月專案好忙,終於擠出時間把這篇 BP 演算法基本思想寫完了,公式的推導放到下一篇講吧。 ## 一、神經網路的代價函式 神經網路可以看做是複雜邏輯迴歸的組合,因此與其類似,我們訓練神經網路也要定義代價函式,之後再使用梯度下降法來最小化代價函式,以此來訓練最優的權重矩陣。 ### 1.1 從邏輯迴歸

神經網路梯度下降法和反向傳播BP

梯度下降法和反向傳播網上資料非常多,記錄點自己理解的 1.梯度下降法是為了使損失函式求最小,而梯度方向是函式增長最快的方向,前面加個負號就變成函式減少最快的方向:                  

BP神經網路反向傳播演算法一步一步例項推導(Backpropagation Example)

1. loss 函式的優化 籠統來講: 設計loss函式是為了衡量網路輸出值和理想值之間的差距,儘管網路的直接輸出並不顯式的包含權重因子,但是輸出是同權重因子直接相關的,因此仍然可以將loss函式視作在權重因子空間中的一個函式。 可以將loss 記為E(w),這裡為

神經網路感知器演算法簡單介紹和MATLAB簡單實現

Perceptron Learning Algorithm 感知機學習演算法,在1943年被生物學家MeCulloch和數學家Pitts提出以後,面臨一個問題:引數需要依靠人工經驗選定,十分麻煩。因此人們希望找到一種能夠自己選定引數的方法。1957年,Fran

人工神經網路——反向傳播演算法(BP)以及Python實現

人工神經網路是模擬生物神經系統的。神經元之間是通過軸突、樹突互相連線的,神經元收到刺激時,神經脈衝在神經元之間傳播,同時反覆的脈衝刺激,使得神經元之間的聯絡加強。受此啟發,人工神經網路中神經元之間的聯絡(權值)也是通過反覆的資料資訊"刺激"而得到調整的。而反向傳

人工神經網路——【BP】反向傳播演算法證明

第一步:前向傳播 【注】此BP演算法的證明僅限sigmoid啟用函式情況。本博文講道理是沒錯的,畢竟最後還利用程式碼還核對了一次理論證明結果。 關於更為嚴謹的BP證明,即嚴格通過上下標證明BP的部落格請戳這裡 簡單的三層網路結構如下 引數定義:     可見層定

BP神經網路鳶尾花

1 #include <stdio.h> 2 #include <math.h> 3 #include <stdlib.h> 4 #include <time.h> 5 #include <string> 6 #include