幾乎全部的教材都是從logistic迴歸開始的,logistic分類太經典了,並且它也是神經網絡的組成部分,每個神經元均可以看做是進行了一次logistics分類。python
logistic即邏輯分類,是一種二分類方法。其算法流程也比較簡單:線性求和、sigmoid激活、計算偏差、優化參數。算法
第1,2步是用於根據輸入來判斷分類的,因此放在一塊兒說。假設有n維的輸入向量x,也有相應的n維參數列向量h,還有一個偏置量b,現行求和獲得:
\[z = h^{T}x+b\]
由於z的值域是[-∞,+∞],是沒法根據z來作出x的邏輯(類別)判斷的,所以咱們引入了一個函數,將z的值映射到[0,1]之間,稱之爲激活函數。激活函數有不少種,這裏的激活函數採用sigmoid。
\[ \begin{align*} & \sigma(x)=\dfrac{1}{1+\mathrm{e}^{-x}} \\ & \sigma'(x) = \sigma(x)*(1-\sigma(x)) \end{align*} \]
當x越大,\(\sigma(x)\)越接近1,x越小$\sigma(x)越接近0,x=0時值爲0.5。因此:網絡
\[a=\sigma(z)=\sigma(h^{T}x+b)\]app
a>0.5時屬於正類,反之屬於負類,這樣便完成了分類工做。dom
訓練就是對h和b進行尋優的過程。如何訓練呢?首先咱們須要定義一個損失函數(優化目標)。咱們指望輸入x斷定爲y,而實際獲得的斷定值是a,損失函數爲\(C(a,y)\)。
\[\frac{\partial{C}}{\partial{h}}=0,\frac{\partial{C}}{\partial{b}}=0\]
便可獲得最優解。
注意:在大部分狀況下,數據規模都比較大,或者屬於非凸優化問題,不能這樣直接獲得最優解,而是經過迭代的方法。
\[ \begin{align*} &h:=h-\eta\frac{\partial{C}}{\partial{h}} \\ &b:=b-\eta\frac{\partial{C}}{\partial{b}} \end{align*} \]
其中\(\eta\)爲學習率。
定義平方損失函數爲\(C=(a-y)^2/2\),能夠獲得:
\[ \begin{align*} &\frac{\partial{C}}{\partial{h}}=(a-y)\frac{\partial{a}}{\partial{h}}=(a-y)a(1-a)z'=(a-y)a(1-a)x \\ &\frac{\partial{C}}{\partial{b}}=(a-y)a(1-a) \end{align*} \]
每次迭代的公式爲:
\[\begin{align*} &h:=h-\eta(a-y)a(1-a)x \\ &b:=b-\eta(a-y)a(1-a) \end{align*}\]函數
用二分分類器解決多分類(k類)問題,能夠採用一對多法,將某類做爲正類,其餘全部做爲一類,構建k個分類器;或者一對一設計k(k-1)/2個分類器,再投票。固然更直接的是把輸出值變爲向量,直接輸出屬於每一類的機率。
前面的公式修改後,W變成了矩陣,b/z/a/y都變成了向量。
\[ \mathbf{z}=W\mathbf{x}+\mathbf{b} \\ \mathbf{a}=\sigma(\mathbf{z}) \]
此時的\(\sigma\)函數是對向量的每個元素單獨運算。獲得向量a後其最大值所在的索引就是判別出的分類。修正後的優化公式:
\[ \frac{\partial{C}}{\partial{W}}=\mathbf{(a-y).\times {a}.\times{(1-a)}\times{x^{T}}} \\ \frac{\partial{C}}{\partial{b}}=\mathbf{(a-y).\times {a}.\times{(1-a)}} \]
注意向量之間有些是點乘。學習
最簡單的神經網絡:輸入層-隱藏層 -輸出層,分別記爲x,h,y。優化
從輸入層到隱藏層的矩陣記爲\(W_{hx}\),偏置量爲\(b_{h}\);從隱藏層到輸出層的矩陣記爲\(W_{yh}\),偏置量爲\(b_{y}\),獲得:spa
\[ \begin{align*} & \mathbf{h_{z}=W_{hx}x+b_{h}} \\ & \mathbf{h_{a}=\sigma(h_{z})} \\ & \mathbf{y_{z}=W_{yh}h_{a}+b_{y}} \\ & \mathbf{y_{a}=\sigma(y_{z})} \end{align*} \]設計
不難看出,其實就是兩層logistic的堆疊。按照傳統的logistic算法,能夠根據偏差來優化\(W_{hx},b_{h}\),那麼如何更新從輸入到隱藏層的參數呢?這就要引入後向算法了,其核心是:鏈式法則
首先看\(W_{hx},b_{h}\)的更新,
\[ \begin{align*} & C=(y_{a}-y)^2/2 \\ & \frac{\partial{C}}{\partial{y_{z}}} = (y_{a}-y)*\sigma'(y_z) \\ & \frac{\partial{C}}{\partial{W_{yh}}}=\frac{\partial{C}}{\partial{y_{z}}} \frac{\partial{y_{z}}}{\partial{W_{yh}}} = C' \sigma'(y_z) h_a^T = (y_a-y)y_{z}(1-y_{z})h_{a}^{T} \\ & \frac{\partial{C}}{\partial{b_{y}}}=\frac{\partial{C}}{\partial{y_{z}}} \frac{\partial{y_{z}}}{\partial{b_{y}}} = C'\sigma'(y_z) \end{align*} \]
上面的公式中也用到了鏈式法則,相似地,能夠獲得:
\[ \begin{align*} & \frac{\partial{C}}{\partial{h_a}}=\frac{\partial{C}}{\partial{y_z}} \frac{\partial{y_z}}{\partial{h_a}} = W_{yh}*C'\sigma'(y_z) \\ & \frac{\partial{C}}{\partial{W_{hx}}} = \frac{\partial{C}}{\partial{h_a}} \frac{\partial{h_a}}{\partial{W_{hx}}} = \frac{\partial{C}}{\partial{h_a}} \sigma'(h_z) x^{T} \\ &\frac{\partial{C}}{\partial{b_{h}}} = \frac{\partial{C}}{\partial{h_a}} \frac{\partial{h_a}}{\partial{b_{h}}} = \frac{\partial{C}}{\partial{h_a}} \sigma'(h_z) \end{align*} \]
能夠看到\(W_{hx},b_{h}\)的計算中使用了\(\frac{\partial{C}}{\partial{h_a}}\),它是輸出層傳導到隱藏層的偏差。在獲得各個參數的偏導後即可以進行參數優化了。
\[ \begin{align*} & W_{yh} := W_{yh} - \eta\frac{\partial C}{\partial W_{yh}} \\ & \mathbf b_y := \mathbf b_y - \eta\frac{\partial C}{\partial \mathbf b_y} \\ & W_{hx} := W_{hx} - \eta\frac{\partial C}{\partial W_{hx}} \\ & \mathbf b_h := \mathbf b_h - \eta\frac{\partial C}{\partial \mathbf b_h} \\ \end{align*} \]
實例以下圖:
左上角是實際的分類,右上角是分類器判別的分類,下面是誤分率的趨勢圖,主要程序是train函數。
#!/usr/bin/python # -*- coding:utf-8 -*- # coding=utf-8 # Author: houkai # Description: # import numpy as np import matplotlib.pyplot as plt import random import math # 構造各個分類 def gen_sample(): data = [] radius = [0,50] for i in range(1000): # 生成10k個點 catg = random.randint(0,1) # 決定分類 r = random.random()*10 arg = random.random()*360 len = r + radius[catg] x_c = math.cos(math.radians(arg))*len y_c = math.sin(math.radians(arg))*len x = random.random()*30 + x_c y = random.random()*30 + y_c data.append((x,y,catg)) return data def plot_dots(data): data_asclass = [[] for i in range(2)] for d in data: data_asclass[int(d[2])].append((d[0],d[1])) colors = ['r.','b.','r.','b.'] for i,d in enumerate(data_asclass): # print(d) nd = np.array(d) plt.plot(nd[:,0],nd[:,1],colors[i]) plt.draw() def train(input, output, Whx, Wyh, bh, by): """ 完成神經網絡的訓練過程 :param input: 輸入列向量, 例如 [x,y].T :param output: 輸出列向量, 例如[0,1,0,0].T :param Whx: x->h 的參數矩陣 :param Wyh: h->y 的參數矩陣 :param bh: x->h 的偏置向量 :param by: h->y 的偏置向量 :return: """ h_z = np.dot(Whx, input) + bh # 線性求和 h_a = 1/(1+np.exp(-1*h_z)) # 通過sigmoid激活函數 y_z = np.dot(Wyh, h_a) + by y_a = 1/(1+np.exp(-1*y_z)) c_y = (y_a-output)*y_a*(1-y_a) dWyh = np.dot(c_y, h_a.T) dby = c_y c_h = np.dot(Wyh.T, c_y)*h_a*(1-h_a) dWhx = np.dot(c_h,input.T) dbh = c_h return dWhx,dWyh,dbh,dby,c_y def test(train_set, test_set, Whx, Wyh, bh, by): train_tag = [int(x) for x in train_set[:,2]] test_tag = [int(x) for x in test_set[:,2]] train_pred = [] test_pred = [] for i,d in enumerate(train_set): input = train_set[i:i+1,0:2].T tag = predict(input,Whx,Wyh,bh,by) train_pred.append(tag) for i,d in enumerate(test_set): input = test_set[i:i+1,0:2].T tag = predict(input,Whx,Wyh,bh,by) test_pred.append(tag) # print(train_tag) # print(train_pred) train_err = 0 test_err = 0 for i in range(train_pred.__len__()): if train_pred[i]!=int(train_tag[i]): train_err += 1 for i in range(test_pred.__len__()): if test_pred[i]!=int(test_tag[i]): test_err += 1 # print(test_tag) # print(test_pred) train_ratio = float(train_err) / train_pred.__len__() test_ratio = float(test_err) / test_pred.__len__() return train_err,train_ratio,test_err,test_ratio def predict(input,Whx,Wyh,bh,by): # print('-----------------') # print(input) h_z = np.dot(Whx, input) + bh # 線性求和 h_a = 1/(1+np.exp(-1*h_z)) # 通過sigmoid激活函數 y_z = np.dot(Wyh, h_a) + by y_a = 1/(1+np.exp(-1*y_z)) # print(y_a) tag = np.argmax(y_a) return tag if __name__=='__main__': input_dim = 2 output_dim = 2 hidden_size = 200 Whx = np.random.randn(hidden_size, input_dim)*0.01 Wyh = np.random.randn(output_dim, hidden_size)*0.01 bh = np.zeros((hidden_size, 1)) by = np.zeros((output_dim, 1)) data = gen_sample() plt.subplot(221) plot_dots(data) ndata = np.array(data) train_set = ndata[0:800,:] test_set = ndata[800:1000,:] train_ratio_list = [] test_ratio_list = [] for times in range(10000): i = times%train_set.__len__() input = train_set[i:i+1,0:2].T tag = int(train_set[i,2]) output = np.zeros((2,1)) output[tag,0] = 1 dWhx,dWyh,dbh,dby,c_y = train(input,output,Whx,Wyh,bh,by) if times%100==0: train_err,train_ratio,test_err,test_ratio = test(train_set,test_set,Whx,Wyh,bh,by) print('times:{t}\t train ratio:{tar}\t test ratio: {ter}'.format(tar=train_ratio,ter=test_ratio,t=times)) train_ratio_list.append(train_ratio) test_ratio_list.append(test_ratio) for param, dparam in zip([Whx, Wyh, bh, by], [dWhx,dWyh,dbh,dby]): param -= 0.01*dparam for i,d in enumerate(ndata): input = ndata[i:i+1,0:2].T tag = predict(input,Whx,Wyh,bh,by) ndata[i,2] = tag plt.subplot(222) plot_dots(ndata) # plt.figure() plt.subplot(212) plt.plot(train_ratio_list) plt.plot(test_ratio_list) plt.show()