Python機器學習筆記:不得不瞭解的機器學習知識點(2)
之前一篇筆記: Python機器學習筆記:不得不瞭解的機器學習知識點(1)
1,什麼樣的資料集不適合用深度學習?
- 資料集太小,資料樣本不足時,深度學習相對其它機器學習演算法,沒有明顯優勢。
- 資料集沒有區域性相關特性,目前深度學習表現比較好的領域主要是影象/語音/自然語言處理等領域,這些領域的一個共性是區域性相關性。影象中畫素組成物體,語音訊號中音位組合成單詞,文字資料中單詞組合成句子,這些特徵元素的組合一旦被打亂,表示的含義同時也被改變。對於沒有這樣的區域性相關性的資料集,不適於使用深度學習演算法進行處理。舉個例子:預測一個人的健康狀況,相關的引數會有年齡、職業、收入、家庭狀況等各種元素,將這些元素打亂,並不會影響相關的結果。
2,softmax函式的數學推導及Python實現
softmax用於多分類過程中最後一層,將多個神經元的輸出,對映到(0, 1)區間內,可以看成概率來理解,從而來進行多分類!
softmax函式如下:
更形象的如下圖表示:
softmax 直白來說就是講原來輸出是 3, 1, -3 通過 softmax 函式一作用,就對映成為(0, 1)的值,而這些值的累和為1,那麼我們就可以將其理解成概率,在最後選取輸出節點的時候,我們可以選取概率最大的節點,作為我們的預測目標!
Python程式碼實現:
# _*_coding:utf-8_*_ import tensorflow as tf import numpy as np import math # softmax函式,或稱歸一化指數函式 def softmax(x, axis=1): # 為了避免求 exp(x) 出現溢位的情況,一般需要減去最大值 # 計算每行的最大值 row_max = x.max(axis=axis) # 每行元素都需要減去對應的最大值,否則求exp(x)會溢位,導致INF情況 row_max = row_max.reshpae(-1, 1) x = x - row_max x_exp = np.exp(x) # 如果是列向量,則axis=0 x_sum = np.sum(x_exp, axis=1, keepdims=True) s = x_exp / x_sum return s # 簡單一些 def softmax(x): """Compute softmax values for each sets of scores in x.""" e_x = np.exp(x - np.max(x)) return e_x / e_x.sum() # 使用 tf的softmax函式 with tf.Session() as sess: tf_s2 = tf.nn.softmax(x, axis=axis) s2 = sess.run(tf_s2)
下面我們分析一下,減去最大值和不減去最大值是否有必要嗎?首先看程式碼:
import numpy as np def softmax(x): """Compute softmax values for each sets of scores in x.""" e_x = np.exp(x - np.max(x)) return e_x / e_x.sum() def softmax1(x): """Compute softmax values for each sets of scores in x.""" return np.exp(x) / np.sum(np.exp(x), axis=0) scores = [3.0, 1.0, 0.2] print(softmax(scores)) print(softmax1(scores)) ''' 結果輸出如下: [0.8360188 0.11314284 0.05083836] [0.8360188 0.11314284 0.05083836] '''
其實兩個結果輸出是一樣的,即使第一個實現了每列和最大值的差異,然後除以總和,但是問題來了,實現在程式碼和時間複雜度方面是否相似?哪一個更有效率?
當然,他們都是正確的,但是從數值穩定性的角度來看,第一個是正確的,因為我們避免了求 exp(x) 出現溢位的情況,這裡減去了最大值。我們推導一下:
# 轉化公式: a ^(b – c)=(a ^ b)/(a ^ c) e ^ (x - max(x)) / sum(e^(x - max(x)) = e ^ x / (e ^ max(x) * sum(e ^ x / e ^ max(x))) = e ^ x / sum(e ^ x)
3,歐氏距離和曼哈頓距離
歐氏距離(也稱為歐幾里得度量),是應用勾股定理計算兩個點之間的直線距離,也就是指m維空間兩個點之間的真實距離,或者向量的自然長度(即該點到原點的距離)。
下面是歐式距離的公式(分別是二維空間,三維空間,n維空間):
曼哈頓距離表示的是兩個點在標準座標系上絕對軸距之和,是種使用在幾何度量空間的幾何學用語。
例如在平面上,座標(x1, y1)的i點與座標(x2, y2)的j點的曼哈頓距離為: d(i,j)=|X1-X2|+|Y1-Y2| 如圖所示,很直接明瞭的理解歐氏距離和曼哈頓距離:
圖中紅線代表曼哈頓距離,綠色代表歐氏距離,也就是直線距離,而藍色和黃色代表等價的曼哈頓距離。
歐氏距離和曼哈頓距離的Python實現:
import numpy as np def manhattan_distance(vec1, vec2): """ This method implements the manhattan distance metric :param p_vec: vector one :param q_vec: vector two :return: the manhattan distance between vector one and two """ return np.sum(np.fabs(vec1 - vec2)) def edclidean_distance(vec1, vec2): """ This method implements the edclidean distance metric :param vec1: vector one :param vec2: vector two :return: the edclidean distance between vector one and two """ # 方法一 distance = np.sqrt(np.sum(np.square(vec1 - vec2))) # method 2 dist = np.linalg.norm(vec1 - vec2) return distance
4,什麼是資料埋點
資料埋點我們可以分為兩類,其一是頁面統計,其二是行為統計。
頁面統計可以幫我們知曉某個頁面被多少人訪問了多少次,行為統計是指使用者在介面上的操作行為,應用最多的是按鈕的點選次數。
5,請簡要說說一個完整的機器學習專案流程
5.1:抽象成數學問題
明確問題是進行機器學習的第一步。機器學習的訓練過程通常都是一件非常耗時的事情,胡亂嘗試時間成本是非常高的。
這裡的抽象成數學問題,指的我們明確我們可以獲得什麼樣的資料,目標是一個分類還是迴歸或者是聚類的問題,如果都不是的話,如果劃歸為其中的某類問題。
5.2:獲取資料
資料決定了機器學習結果的上限,而演算法只是儘可能逼近這個上限。資料要有代表性,否則必然會過擬合。而且對於分類問題,資料偏斜不能過於嚴重,不同類別的資料數量不要有數個數量級的差距。
而且還要對資料的量級有一個評估,多少個樣本,多少個特徵,可以估算出其對記憶體的消耗程度,判斷訓練過程中記憶體是否能夠放得下。如果放不下就得考慮改進演算法或者使用一些降維的技巧了。如果資料量實在太大,那就要考慮分散式了。
5.3 特徵預處理與特徵選擇
良好的資料要能夠提取出良好的特徵才能真正發揮效力。
特徵預處理、資料清洗是很關鍵的步驟,往往能夠使得演算法的效果和效能得到顯著提高。歸一化、離散化、因子化、缺失值處理、去除共線性等,資料探勘過程中很多時間就花在它們上面。這些工作簡單可複製,收益穩定可預期,是機器學習的基礎必備步驟。
篩選出顯著特徵、摒棄非顯著特徵,需要機器學習工程師反覆理解業務。這對很多結果有決定性的影響。特徵選擇好了,非常簡單的演算法也能得出良好、穩定的結果。這需要運用特徵有效性分析的相關技術,如相關係數、卡方檢驗、平均互資訊、條件熵、後驗概率、邏輯迴歸權重等方法。
5.4:訓練模型與調優
直到這一步才用到我們上面說的演算法進行訓練。現在很多演算法都能夠封裝成黑盒供人使用。但是真正考驗水平的是調整這些演算法的(超)引數,使得結果變得更加優良。這需要我們對演算法的原理有深入的理解。理解越深入,就越能發現問題的癥結,提出良好的調優方案。
5.5:模型診斷
如何確定模型調優的方向與思路呢?這就需要對模型進行診斷的技術。
過擬合、欠擬合判斷是模型診斷中至關重要的一步。常見的方法如交叉驗證,繪製學習曲線等。過擬合的基本調優思路是增加資料量,降低模型複雜度。欠擬合的基本調優思路是提高特徵數量和質量,增加模型複雜度。
誤差分析 也是機器學習至關重要的步驟。通過觀察誤差樣本,全面分析誤差產生誤差的原因:是引數的問題還是演算法選擇的問題,是特徵的問題還是資料本身的問題……
診斷後的模型需要進行調優,調優後的新模型需要重新進行診斷,這是一個反覆迭代不斷逼近的過程,需要不斷地嘗試, 進而達到最優狀態。
5.6:模型融合
一般來說,模型融合後都能使得效果有一定提升。而且效果很好。
工程上,主要提升演算法準確度的方法是分別在模型的前端(特徵清洗和預處理,不同的取樣模式)與後端(模型融合)上下功夫。因為他們比較標準可複製,效果比較穩定。而直接調參的工作不會很多,畢竟大量資料訓練起來太慢了,而且效果難以保證。
5.7:上線執行
這一部分內容主要跟工程實現的相關性比較大。工程上是結果導向,模型在線上執行的效果直接決定模型的成敗。 不單純包括其準確程度、誤差等情況,還包括其執行的速度(時間複雜度)、資源消耗程度(空間複雜度)、穩定性是否可接受。
這些工作流程主要是工程實踐上總結出的一些經驗。並不是每個專案都包含完整的一個流程。這裡的部分只是一個指導性的說明,只有大家自己多實踐,多積累專案經驗,才會有自己更深刻的認識。
6,全連線神經網路網路結構
(此題參考:https://blog.csdn.net/cuiyuan605/article/details/84307323)
神經網路演算法,是使用計算機模擬生物神經系統,來模擬人類思維方式的演算法。它的基本單位就是人工神經元。通過相互連線形成一張神經網路。對於神經網路有些瞭解的盆友可能都知道,神經網路其實就是一個輸入 X(向量) 到輸出 Y(向量)的對映函式:f(x) = Y,函式的係數就是我們所要訓練的網路引數 W,只要函式係數確定下來,對於任何輸入xi,我們就能得到一個與之對應的輸出 yi,至於 yi 是否符合我們的預期,這就是輸入如何提高模型效能方面的問題。
生物神經網路中,每個神經元與其他神經元連線,當它“啟用”時,會傳遞化學物質到相連的神經元,改變其他神經元的電位,當電位達到一定“閾值”,那麼這個神經元也會被啟用。
單個人工神經元的計算公式如下:
其中:
為輸入引數向量,表示其他神經元輸入的訊號。
為每個輸入引數的權重值,表示對應神經元訊號的權重。
theta 為閾值或者偏差值,是指該啟用神經元的難易程度。
y 為神經元的輸出值,表示該神經元是否被啟用。
Act() 為啟用函式,理想的啟用函式如下圖(a)中的躍階函式,“1” 為神經元興奮,“0”為神經元抑制,但由於躍階函式具有不是連續可導等不好的性質,因此一般採用下面(b) 圖的 Sigmoid 函式作為啟用函式:
下面定義一個全連線神經網路:
全連線神經網路,就是指每一層的每個神經元都和下一層的每個神經元項連線。
Layer:0 為輸入層
Layer:L 為輸出層
其他L-1 個Layer 為隱層
輸入 x : ,我們稱一個輸入值 x 為一個樣本
輸出 y : ,變數的上標(L)表示該變量出於神經網路的那一層。
表示第 L 層編號為 i 的神經元
表示第 L 層的神經元數量
7,全連線神經網路的前向傳播
前向傳播比較簡單,就是向量點乘,也就是加權求和,然後經過一個啟用函式。也就是網路如何根據輸入 X 得到輸出 Y的。
記 為第 l-1 層第 k個神經元到第 l 層第 j 個神經元的權重, 為第 l 層 第 j 個神經元的偏置, 為第 l 層第 j 個神經元的啟用值(啟用函式的輸出)。不難看出 的值取決於上一層神經元的啟用:
將上面重寫為矩陣形式:
為了方便表示,記 為每一層權重輸入,矩陣形式則變為
利用矩陣形式可以一層層計算網路的啟用值,最終能根據輸入X 得到相應的輸出 。
8,隨機梯度下降法
(此題參考:https://blog.csdn.net/qq_38150441/article/details/80533891 和 https://blog.csdn.net/qq_39037383/article/details/89156894)
梯度下降演算法的思想就是根據人類在漸進學習中,不斷從錯誤中糾正自己的認知的過程中感觸到的。
8.1 梯度下降
簡單來說,梯度下降就是從山頂找一條最短的路走到山底最低的地方。但是因為選擇方向的原因,我們找到的最低點可能不是真正的最低點。如圖所示,黑色標註的路線所指的方向並不是真正的地方。(因為梯度下降是一種思想,沒有嚴格的定義,所以用一個比喻來解釋什麼是梯度下降)
既然是選擇一個方向下山,那麼這個方向該如何選?每次該怎麼走?
先說選的方向,在演算法中是以隨機方式給出的,這也是造成有時候走不到真正最低點的原因。如果選定了方向,以後每走一步,都選擇的時最陡的方向,直到最低點。總結起來就是:隨機選擇一個方向,然後每次都選擇最陡的方向,直到這個方向上能達到的最低點。
在機器學習演算法中,有時候需要對原始的模型構建損失函式,然後通過優化演算法對損失函式進行優化,以便尋找到最優的引數,使得損失函式的值最小。而求解機器學習引數的優化演算法中,使用最多的就是基於梯度下降的優化演算法(Gradient Descent GD)。
梯度下降的優缺點:
- 優點:效率。在梯度下降法的求解過程中,只需求解損失函式的一階導數,計算的代價比較小,可以在很多大規模資料集上應用。
- 缺點:求解的時區域性最優值,即由於方向選擇的問題,得到的結果不一定是全域性最優步長選擇,過小使得函式收斂速度慢,過大又容易找不到最優解。
8.2 隨機梯度下降
隨機梯度下降(SGD)是一種簡單但非常有效地方法,多用於支援向量機,邏輯迴歸等凸損失函式下的線性分類器的學習。並且SGD已經成功應用於文字分類和自然語言處理中經常遇到的大規模和稀疏機器學習問題。SGD 既可以用於分類計算,也可以用於迴歸計算。
隨機梯度下降法不是對每個樣本集進行求梯度更新引數,而是對一個或者多個樣本進行求梯度,更新引數,採集多個樣本為樣本集再進行如下操作:
1.初始化引數為任意值(可以取到面上任意一點) 2.對樣本集裡每個樣本進行遍歷如下操作 1.求解梯度值 2.更新引數 3.若達到指定迭代次數或者收斂條件,則訓練結束
隨機梯度下降法不同於批量梯度下降,隨機梯度下降是每次迭代使用一個樣本來對引數進行更新。使得訓練速度加快。
對於一個樣本的目標函式為:
對目標函式求偏導:
引數更新:
隨機梯度下降的優缺點:
- 優點:由於不是在全部訓練資料上的損失函式,而是在每輪迭代中,隨機優化某一條訓練資料上損失函式,這樣每一輪引數的更新速度大大加快。
- 缺點:準確度下降,由於即使在目標函式為強凸函式的情況下,SGD仍舊無法做到線性收斂。可能會收斂到區域性最優,而單個樣本並不能代表全體樣本的趨勢,而且不易於並行實現。
9,LR的原理和Loss的推導
首先,LR是一個分類模型,討論二分類情況下,在這個基礎上我們假設樣本服從伯努利分佈(0~1)分佈。做了假設分佈後下一步就是求分佈引數,這個過程一般採用極大似然估計MLE(Maximum Likelihood Estimation),具體的方法就是求該假設分佈在訓練樣本上的聯合概率(樣本帶入連乘),然後求其關於 theta 的最大值,為了方便計算所以一般取 -log,單調性保持不變,所有就有了 logLoss: L(Y, P(Y|X)) = - logP(Y|X)。
10,機器學習中,為何要經常對資料做歸一化
(參考文獻:https://blog.csdn.net/abc_138/article/details/82798674)
一般做機器學習應用的時候大部分時間是花費在特徵處理上,其中很關鍵的一步就是對特徵資料進行歸一化。
首先要明白歸一化的目的是什麼,其目的是為了避免數值較大的特徵A變化掩蓋了數值較小的特徵B變化,最終希望讓特徵AB都能對結果有影響。
那麼為什麼要做歸一化呢?
維基百科給出的解釋:1,歸一化後加快了梯度下降求最優解的速度。2,歸一化有可能提高精度。
解釋:歸一化為什麼能提高梯度下降法求解最優解的速度?
如下圖所示(來自:斯坦福機器學習視訊)
藍色的圈圈圖代表的是兩個特徵的等高線。其中左圖兩個特徵 X1和 X2的區間差別非常大,X1區間為[0, 2000] ,x2區間是 [1, 5],像這種有的資料那麼大,有的資料那麼小,兩類之間的幅度相差這麼大,其所形成的等高線非常尖。當使用梯度下降法尋求最優解時,很有可能走“之字型”路線(垂直等高線走),從而導致需要迭代很多次才能收斂。而右圖對兩個原始特徵進行了歸一化,其對應的等高線顯得很圓,在梯度下降進行求解時能較快的收斂,因此如果機器學習模型使用梯度下降法求最優解時,歸一化往往非常有必要,否則很難收斂,甚至不能收斂。
解釋:歸一化有可能提高精度
一些分類器需要計算樣本之間的距離(如歐式距離),例如KNN。如果一個特徵值域範圍非常大,那麼距離計算就主要取決於這個特徵,從而與實際情況相悖(比如這時實際情況是值域範圍小的特徵更重要)。
歸一化的型別
1,線性歸一化
這種歸一化方法比較適用於在數值比較集中的情況。這種方法有個缺陷,如果max和min 不穩定,很容易使得歸一化結果不穩定,使得後續使用效果也不穩定。實際使用中可以用經驗常量值來替代 max和 min。
2,標準差標準化
經過處理的資料符合標準正態分佈,即均值為0,標準差為1。
3,非線性歸一化
經常用在資料分化比較大的場景,有些數值很大,有些很小。通過一些數學函式,將原始值進行對映。該方法包括 log、指數,正切等。需要根據資料分佈的情況,決定非線性函式的曲線,比如log(V, 2)還是log(V, 10)等。
11,batch
深度學習中頻繁出現batch這個詞語,所以我們有必要了解一下。
深度學習中 的優化演算法,說白了就是梯度下降。每次的引數更新有兩種方式。
第一種,遍歷全部資料集算一次損失函式,然後算函式對各個引數的梯度,更新梯度。這張方式每更新一次引數都要把資料集裡的所有樣本都看一遍,計算量開銷大,計算速度慢,不支援線上學習,這稱為 Batch gradient descent,批梯度下降。
另一種,每看一個數據就算一下損失函式,然後求梯度更新引數,這個稱為隨機梯度下降, stochastic gradient descent。這個方法速度比較快,但是收斂效能不太好,可能在最優點附近晃來晃去, hit 不到最優點。兩次引數的更新也有可能互相抵消掉,造成目標函式震盪的比較劇烈。
為了克服兩種方法的缺點,現在一般採用的時一種折中手段,mini-batch gradient decent,小批的梯度下降,這種方法把資料分為若干個批,按批來更新引數。這樣一個批中的一組資料共同決定了本次梯度的方向,下降起來就不容易跑偏,減少了隨機性。另外一方面因為批次的樣本數與整個資料集相比少了很多,計算量也不是很大。
基本上現在的梯度下降都是基於 mini-batch的,所以Keras的模組中經常會出現 batch_size,就是指這個。
12,關於機器學習擬合問題
12.1 什麼是機器學習過擬合?
所謂過擬合,就是指模型在訓練集上的效果很好,在測試集上的預測效果很差。
12.2 如何避免過擬合問題?
1,重取樣Bootstrap
2,L1,L2 正則化
3,決策樹的剪枝操作
4,交叉驗證
12.3 什麼是機器學習的欠擬合?
所謂欠擬合就是模型複雜度低或者資料集太小,對模型資料的擬合程度不高,因此模型在訓練集上的效果就不好。
12.3 如何避免欠擬合問題?
1,增加樣本數量
2,增加樣本特徵的數量
3,可以進行特徵維度擴充套件
12.4 演算法的誤差一般是由那幾個方面引起的?
1,因模型無法表達基本資料的複雜度而造成的偏差(bias)——欠擬合
2,因模型過度擬合訓練集資料而造成的方差(variance)——過擬合
13,為什麼樸素貝葉斯如此“樸素”?
貝葉斯演算法簡單高效,在處理分類問題上,是首先要考慮的方法之一。
貝葉斯分類是一類分類演算法的總稱,這類演算法均以貝葉斯定理為基礎,故統稱為貝葉斯分類。公式如下:
該公式最大的優點就是可以忽略AB 的聯合概率直接求其條件概率分佈。
而樸素貝葉斯為什麼如此樸素,因為他假定所有的特徵在資料集中的作用是同樣重要和獨立的。正如我們所知,這個假設在現實世界中是很不真實的,因此說樸素貝葉斯真的很“樸素”。
樸素貝葉斯分類是一種非常簡單的分類演算法,其思想是樸素的。即:對於給出的待分類項,求解在此項出現的條件下各個類別出現的概率,那個最大,就認為此待分類項屬於那個類別。
理論上,樸素貝葉斯模型與其他分類方法相比具有最小的誤差率。但是實際上並非總是如此,這是因為樸素貝葉斯模型給定輸出類別的情況下,假設屬性之間相互獨立,這個假設在實際應用中往往是不成立的,在屬性個數比較多或者屬性之間相關性較大時,分類效果不好。而在屬性相關性較小的時,樸素貝葉斯效能最為良好。對於這一點,有半樸素貝葉斯之類的演算法通過考慮部分關聯性適度改進。
14,反向傳播演算法(BP演算法)的推導及其Python實現
下面學習如何調整一個神經網路的引數,也就是誤差反向傳播演算法(BP演算法)。以得到一個能夠根據輸入,預測正確輸出的模型。
14.1,首先我們要了解優化的目標
根據人工神經元的定義,有以下三個公式:
其中,Act() 是啟用函式,之前學習過。
根據上面兩個公式,可以得出各個神經元之間的通用公式,如下:
其中上式是人工神經網路正向傳播的核心公式。
那麼,我們根據什麼來調整神經網路的引數,以得到一個能夠正確預測結果的模型呢?請看下面的公式:
上式用來計算我們期望的輸出和實際輸出的“差別”,其中cost() 叫做損失函式。我們的期望是損失函式值達到最小。
但是隻根據一次輸出的損失值,對引數進行調整,無法使模型適應所有輸入樣本。我們需要的是,調整引數,使得所有輸入樣本,得到輸出的總損失值最小,而不是隻讓妻子一個樣本的損失值最小,導致其他樣本損失值增大。因此有下面公式:
上式表示一個 batch 的所有樣本輸出的總損失值的平均值。其中,bn 表示一個 batch中樣本的數量。
為什麼不用所有的樣本計算損失值,而將所有樣本分成一個個的 batch呢?因為所有的訓練樣本數量太大了,可能有數以百萬計,將所有的樣本損失值都一起進行運算,計算量過於龐大,大大降低了模型計算的速度。
而計算總的損失值 C,其中是一個以所有的連線權重 W 和 所有的閾值 theta 未為變數的多元函式。我們想要的模型就是求得 C 最小時,所有 W 和 theta 的值。直接計算顯然是不可能的,因為對於一個大的深度神經網路,所有的引數變數,可能數以萬計。
在這裡我們使用梯度下降演算法來逐步逼近 C的最小值,也即是先隨機得到一組引數變數的值,然後計算引數變數當前的梯度,向梯度的反方向,也就是C變小最快的方向,逐步調整引數值,最終得到 C 的最小值,或者近似最小值。
而將所有樣本,隨機分成一個個固定長度的 batch,以得到近似的梯度方向,叫做隨機梯度下降演算法。
14.2 開始求梯度
那麼根據梯度的定義,接下來的任務,就是求取各個引數變數相對於 C 的偏導數。我們將使用誤差反向傳播演算法來求取各個引數變數的偏導數。
求取偏導數的方法和神經網路正向傳播(根據樣本計算輸出值)的方式類似,也是逐層求解,只是方向正好相反,從最後一層開始,逐層向前。
首先,我們先求神經網路最後一層,也即是輸出層的相關引數的偏導數。為了降低推導的複雜性,我們只計算相對一個樣本的損失值函式 Cbi 的偏導數,因為相對於總損失值函式 C 的偏導數值,也不過是把某個引數的所有相對於 Cbi 偏導數值加起來而已。
根據上面公式,以及 複合函式求導法則,可以得到輸出層(L層)某個神經元的權值引數 W 的偏導數,計算公式如下:
根據前面三個公式求導如下:
將這三個公式代入上面公式,可以得到:
我們令:
則:
將上式代入損失函式求導的公式中可以得到:
這樣我們就得到了輸出層 L 相關的權重引數 W 的偏導數計算公式!
接下來,同理可以求得輸出層 L 相關的閾值 theta 的偏導數計算公式為:
而根據第二個公式可以得到:
將上式代入到上上式可以得到:
這就是 輸出層 L 相關的閾值 theta 的偏導數計算公式!
14.3 根據 L 層,求前一層引數的偏導函式
從下面公式,可知,一個權重引數 W 隻影響一個 L-1 層的神經元:
因此可以得到有下面公式:
將上式代入到上上式可以得到:
根據假設:
我們可以得到:
將上式代入到上上式,可以得到:
同理,我們可以得到:
根據14.3 第一個公式可以得到:
將上式代入到上上式,可以得到:
這樣我們就得到了 L-1 層神經元相關引數的計算公式。
下面我們還需要推導一下 之間的關係,根據下面公式:
我們可以得到:
同理可得:
將上式代入到上上式,可以得:
我們知道,一個權重引數 W 隻影響一個 L-1 層的神經元,但這個 L-1 層神經元影響了所有 L層的神經元。因此,根據多元複合函式求導法則。有:
根據我們之前的假設,可以得到:
將上式代入到上上式,可以得到:
我們可以知道:
將上式代入到上上式,可以得到:
最後將上式代入之前的公式,可以得到:
這樣我們就得到了反向傳播,逐層推導的通用公式:
這裡, W 和 Z 都是整箱傳播過程中已經算好的常數,而 可以從 L層開始逐層向前推導,直到第1層,第0層是輸入層,不需要調整引數,而第L層的引數可以參考下面公式:
下面是全連線神經網路的Python實現程式碼:
#coding=utf-8 import numpy as np import matplotlib.pylab as plt import random class NeuralNetwork(object): def __init__(self, sizes, act, act_derivative, cost_derivative): #sizes表示神經網路各層的神經元個數,第一層為輸入層,最後一層為輸出層 #act為神經元的啟用函式 #act_derivative為啟用函式的導數 #cost_derivative為損失函式的導數 self.num_layers = len(sizes) self.sizes = sizes self.biases = [np.random.randn(nueron_num, 1) for nueron_num in sizes[1:]] self.weights = [np.random.randn(next_layer_nueron_num, nueron_num) for nueron_num, next_layer_nueron_num in zip(sizes[:-1], sizes[1:])] self.act=act self.act_derivative=act_derivative self.cost_derivative=cost_derivative #前向反饋(正向傳播) def feedforward(self, a): #逐層計算神經元的啟用值,公式(4) for b, w in zip(self.biases, self.weights): a = self.act(np.dot(w, a)+b) return a #隨機梯度下降演算法 def SGD(self, training_data, epochs, batch_size, learning_rate): #將訓練樣本training_data隨機分為若干個長度為batch_size的batch #使用各個batch的資料不斷調整引數,學習率為learning_rate #迭代epochs次 n = len(training_data) for j in range(epochs): random.shuffle(training_data) batches = [training_data[k:k+batch_size] for k in range(0, n, batch_size)] for batch in batches: self.update_batch(batch, learning_rate) print("Epoch {0} complete".format(j)) def update_batch(self, batch, learning_rate): #根據一個batch中的訓練樣本,調整各個引數值 nabla_b = [np.zeros(b.shape) for b in self.biases] nabla_w = [np.zeros(w.shape) for w in self.weights] for x, y in batch: delta_nabla_b, delta_nabla_w = self.backprop(x, y) nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)] nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)] #計算梯度,並調整各個引數值 self.weights = [w-(learning_rate/len(batch))*nw for w, nw in zip(self.weights, nabla_w)] self.biases = [b-(learning_rate/len(batch))*nb for b, nb in zip(self.biases, nabla_b)] #反向傳播 def backprop(self, x, y): #儲存b和w的偏導數值 nabla_b = [np.zeros(b.shape) for b in self.biases] nabla_w = [np.zeros(w.shape) for w in self.weights] #正向傳播 activation = x #儲存每一層神經元的啟用值 activations = [x] #儲存每一層神經元的z值 zs = [] for b, w in zip(self.biases, self.weights): z = np.dot(w, activation)+b zs.append(z) activation = self.act(z) activations.append(activation) #反向傳播得到各個引數的偏導數值 #公式(13) d = self.cost_derivative(activations[-1], y) * self.act_derivative(zs[-1]) #公式(17) nabla_b[-1] = d #公式(14) nabla_w[-1] = np.dot(d, activations[-2].transpose()) #反向逐層計算 for l in range(2, self.num_layers): z = zs[-l] sp = self.act_derivative(z) #公式(36),反向逐層求引數偏導 d = np.dot(self.weights[-l+1].transpose(), d) * sp #公式(38) nabla_b[-l] = d #公式(37) nabla_w[-l] = np.dot(d, activations[-l-1].transpose()) return (nabla_b, nabla_w) #距離函式的偏導數 def distance_derivative(output_activations, y): #損失函式的偏導數 return 2*(output_activations-y) # sigmoid函式 def sigmoid(z): return 1.0/(1.0+np.exp(-z)) # sigmoid函式的導數 def sigmoid_derivative(z): return sigmoid(z)*(1-sigmoid(z)) if __name__ == "__main__": #建立一個5層的全連線神經網路,每層的神經元個數為1,8,5,3,1 #其中第一層為輸入層,最後一層為輸出層 network=NeuralNetwork([1,8,5,3,1],sigmoid,sigmoid_derivative,distance_derivative) #訓練集樣本 x = np.array([np.linspace(-7, 7, 200)]).T #訓練集結果,由於使用了sigmoid作為啟用函式,需保證其結果落在(0,1)區間內 y = (np.cos(x)+1)/2 #使用隨機梯度下降演算法(SGD)對模型進行訓練 #迭代5000次;每次隨機抽取40個樣本作為一個batch;學習率設為0.1 training_data=[(np.array([x_value]),np.array([y_value])) for x_value,y_value in zip(x,y)] network.SGD(training_data,5000,40,0.1) #測試集樣本 x_test = np.array([np.linspace(-9, 9, 120)]) #測試集結果 y_predict = network.feedforward(x_test) #圖示對比訓練集和測試集資料 plt.plot(x,y,'r',x_test.T,y_predict.T,'*') plt.show()