笨方法學(xué)機(jī)器學(xué)習(xí)(二)全連接神經(jīng)網(wǎng)絡(luò)

網(wǎng)絡(luò)構(gòu)成

算法假設(shè)

我們的大腦接收眼睛觀察傳播來的數(shù)據(jù)后,會對其進(jìn)行一層層的神經(jīng)元去解析數(shù)據(jù),然后得到我們對于所見的判斷。然而我們對這個分析過程的了解以及腦部的研究較為淺,無法得知其腦部工作的原理。但是,我們是否可以對其進(jìn)行部分抽象化為以下過程:


腦部在接收一系列數(shù)據(jù)的時候進(jìn)行了一個函數(shù)式(function)的抽象,得到了其認(rèn)知。

那么,我們的神經(jīng)網(wǎng)絡(luò)就是模擬這個過程,將輸入信號,神經(jīng)元的function處理,以及輸出,都數(shù)字化。用統(tǒng)計與學(xué)習(xí)的方式,將這個過程一步步還原;


全連接神經(jīng)網(wǎng)絡(luò)

其全連接神經(jīng)網(wǎng)絡(luò)規(guī)則如下:

  • 神經(jīng)元按照層來布局。最左邊的層叫做輸入層,負(fù)責(zé)接收輸入數(shù)據(jù);最右邊的層叫輸出層,我們可以從這層獲取神經(jīng)網(wǎng)絡(luò)輸出數(shù)據(jù)。輸入層和輸出層之間的層叫做隱藏層,因為它們對于外部來說是不可見的。
  • 同一層的神經(jīng)元之間沒有連接。
  • 第N層的每個神經(jīng)元和第N-1層的所有神經(jīng)元相連(這就是full connected的含義),第N-1層神經(jīng)元的輸出就是第N層神經(jīng)元的輸入。
  • 每個連接都有一個權(quán)值。

神經(jīng)元

神經(jīng)元是構(gòu)成神經(jīng)網(wǎng)絡(luò)的基本單位:


一個神經(jīng)元的組成為:

  • 輸 入:n維度向量x
  • 線性加權(quán):
  • 激活函數(shù): $H(x)$,要求非線性,容易求導(dǎo)數(shù)
  • 輸出 a

激活函數(shù)的選擇

(1)sigmoid函數(shù)

(2)tanh函數(shù)

(3)relu函數(shù)

計算樣例

下面我們來用最簡單是sigmoid函數(shù)來嘗試手算一個很簡單的神經(jīng)網(wǎng)絡(luò):


  • 中間藍(lán)色的神經(jīng)元代表了隱層神經(jīng)元,其左邊的是W權(quán)值,b代表了其偏置項。
  • X 為輸入層
  • 紅色神經(jīng)元代表輸出,只有一個神經(jīng)元代表這個神經(jīng)網(wǎng)絡(luò)只有一個輸出

x1與z1的連線權(quán)值為$0.1$,x1的輸出為$0.5$,所以x1到z1的輸入為$0.1$*$0.5$,同理。x2到z1的輸入為$-0.06$,所以根據(jù)計算公式,z1的輸入為:$0.1 0.5 +0.2-(0.3)+0.01$,再將其進(jìn)行激活函數(shù)處理,得到Z1的輸出:$0.50224$

神經(jīng)網(wǎng)絡(luò)的訓(xùn)練

  • 我們需要知道一個神經(jīng)網(wǎng)絡(luò)的每個連接上的權(quán)值.
  • 我們可以說神經(jīng)網(wǎng)絡(luò)就是一個模型,這些權(quán)值就是模型的參數(shù)(即模型要學(xué)習(xí)的東西),
  • 對于這個神經(jīng)網(wǎng)絡(luò)的連接方式,網(wǎng)絡(luò)層數(shù),每層的節(jié)點(diǎn)數(shù),這些,我們是事先設(shè)置的,成為超參數(shù).

目標(biāo)函數(shù)與誤差計算

在監(jiān)督學(xué)習(xí)中,對于每一個樣本,我們得到其特征$x$,標(biāo)記$y$.我們通過模型$h(x)$計算得到輸出值:

,顯然$y$是其真實值,
是其神經(jīng)網(wǎng)絡(luò)的預(yù)測值,我們希望預(yù)測值跟真實值更接近,
數(shù)學(xué)中常用的辦法是將兩個值的差的平方的$1/2$來代表其接近程度:

我們將$e$當(dāng)做單個成本的誤差,將所有的訓(xùn)練樣本的誤差值相加,得到其誤差誤差項$E$:


訓(xùn)練的目標(biāo),就是找到合適的$w$,使誤差$E$最小;

訓(xùn)練方法:反向傳播算法(Back Propagation)

簡單的說,反向傳播算法的過程就是:

  • 給定的一個輸入集$X$,取其中的一個向量$x$
  • 先正向計算$x$的神經(jīng)網(wǎng)絡(luò),得到其輸出值$y$
  • 訓(xùn)練向量$x$有人為標(biāo)定的標(biāo)簽$l$(label),計算其預(yù)測值跟真實值之間的誤差,
  • 將誤差項每一層地反向傳播,得到每一層的誤差,然后利用每一層的誤差去更新每一層的權(quán)值去擬合模型

我們先給出各層的計算公式,然后再進(jìn)行數(shù)學(xué)公式的推導(dǎo)(不擅長數(shù)學(xué)的可以跳過,等代碼實現(xiàn)再回來看推導(dǎo))

(1) 輸出層的誤差計算:



其中,
是節(jié)點(diǎn)i的誤差值,$Yi$是節(jié)點(diǎn)$i$的輸出值,$Ti$是對應(yīng)節(jié)點(diǎn)$i$的目標(biāo)值

(2) 隱藏層的誤差計算與傳遞



我們知道:

$ai$是節(jié)點(diǎn)i的輸出值,$Wki$節(jié)點(diǎn)i到它下一層節(jié)點(diǎn)$i$連接的權(quán)重,
是節(jié)點(diǎn)$i$到下一層節(jié)點(diǎn)$k$的誤差項

更新權(quán)重:


數(shù)學(xué)公式的推導(dǎo):

我們?nèi)【W(wǎng)絡(luò)所有輸出層節(jié)點(diǎn)的誤差平方和作為目標(biāo)函數(shù):


,
用隨機(jī)梯度下降算法對目標(biāo)函數(shù)進(jìn)行優(yōu)化:



我們可以從上圖得到,$W84$僅影響$a4$節(jié)點(diǎn)到$8$節(jié)點(diǎn)的輸入值,設(shè):$netj$為節(jié)點(diǎn)$j$的加權(quán)輸入:


$Ed$是$netj$的函數(shù),而$netj$是$Wji$的函數(shù),根據(jù)鏈?zhǔn)角髮?dǎo)法則:



$xji$是節(jié)點(diǎn)i到節(jié)點(diǎn)j的輸出值,即$j$的輸入值

輸出層:

netj僅影響節(jié)點(diǎn)j的輸出值,,所以Ed是yi的函數(shù),yi是netj的函數(shù):



計算第一項
將Ed帶入公式,得到:


計算第二項:


綜合,得:


將其推導(dǎo)代入 隨機(jī)推導(dǎo)公式,得:


隱層:

我們定義節(jié)點(diǎn)j的所有直接下游節(jié)點(diǎn)的集合為:$Downstream(j)$,所以netj只能通過影響$Downstream(j)$來影響$Ed$,設(shè)Netk是節(jié)點(diǎn)J的下游輸入,而$Ed$是netk的函數(shù),而netk是netj的函數(shù),所以我們用全導(dǎo)數(shù)公式:



因為:
,代入,得:

自此,我們完成了公式的推導(dǎo)

代碼:

import numpy as np

#定義tan函數(shù)以及tan函數(shù)的導(dǎo)數(shù)
def tanh(x):
    return np.tanh(x)

def tanh_deriv(x):
    return  1.0-np.tanh(x)*np.tanh(x)

#定義logistich函數(shù)以及其導(dǎo)數(shù)
def logistic(x):
    return 1/(1+np.exp(-x))

def logistic_derivatrive(x):
    return logistic(x)*(1-logistic(x))

class NeturalNetwork(object):
    def __init__(self,layers,activations='tanh'):
        '''
        :param layers: 一個list  包括每一層的神經(jīng)元數(shù)
        :param activations:激活函數(shù)
        '''
        if activations=='tanh':
            self.activation = tanh
            self.activation_deriv = tanh_deriv
        if activations=='logistic':
            self.activation = logistic
            self.activation_deriv = logistic_derivatrive
        self.weights = []
        for i in range(1,len(layers)-1):
            #i跟前一層的權(quán)重
            self.weights.append((2*np.random.random((layers[i-1]+1,layers[i]+1))-1)*0.25)
            #i層跟i+1層進(jìn)行賦值 權(quán)重
            self.weights.append((2*np.random.random((layers[i]+1,layers[i+1]))-1)*0.25)

    def fit(self,X,y,learning_rate = 0.2,epochs=10000):
        '''
        :param X:
        :param y:
        :param learning_rate: 學(xué)習(xí)率
        :param epochs: 學(xué)習(xí)步驟
        :return:
        '''
        #二維矩陣
        X = np.atleast_2d(X)
        #ones 矩陣全是1   shape函數(shù)返回的是行列數(shù)(返回一個List)跟X一樣維度的矩陣
        temp = np.ones([X.shape[0],X.shape[1]+1])
        #temp等于第一列到最后一列跟x一樣的矩陣
        temp  [:,0:-1]=X
        X= temp
        Y=np.array(y)

        #第幾次循環(huán)
        for k in range(epochs):
            i = np.random.randint(X.shape[0])
            #隨機(jī)取一個數(shù),代表第i行,對i行數(shù)據(jù)進(jìn)行更新
            a = [X[i]]
            #形成這一行數(shù)據(jù)為輸入的神經(jīng)網(wǎng)絡(luò),dot代表內(nèi)積
            for l in range(len(self.weights)):
                a.append(self.activation(np.dot(a[l],self.weights[l])))
            #誤差
            error = y[i]-a[-1]
            deltas = [error * self.activation_deriv(a[-1])]
            #開始往回算每一層的誤差
            #deltas是所有權(quán)重的誤差列表
            for l in range(len(a)-2,0,-1):
                deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_deriv(a[l]))
            deltas.reverse()
            for i in range(len(self.weights)):
                layers = np.atleast_2d(a[i])
                delta = np.atleast_2d(deltas[i])
                self.weights[i] += learning_rate*layers.T.dot(delta)
    def predict(self,x):
        x = np.array(x)
        temp = np.ones(x.shape[0]+1)
        temp[0:-1] = x
        a =temp
        for l in range(0,len(self.weights)):
            a = self.activation(np.dot(a,self.weights[l]))
        return a
用手寫訓(xùn)練集來測試我們的神經(jīng)網(wǎng)絡(luò):
import numpy as np
from sklearn.datasets import load_digits
from sklearn.metrics import confusion_matrix,classification_report
from sklearn.preprocessing import LabelBinarizer
from neuralNetwork.nn import NeturalNetwork
from sklearn.cross_validation import train_test_split

digits =load_digits()
#1797張 8*8的手寫數(shù)字圖片
X = digits.data
Y = digits.target
#標(biāo)準(zhǔn)化
X -=X.min()
X /=X.max()

nn = NeturalNetwork([64,100,10],'logistic')

#分離測試集跟訓(xùn)練集
X_train,X_test,y_train,y_test = train_test_split(X,Y)
labels_train = LabelBinarizer().fit_transform(y_train)
labels_test  = LabelBinarizer().fit_transform(y_test)

print("start fitting")

nn.fit(X_train,labels_train,epochs=3000)

print("end training")
predictions=[]

for i in range(X_test.shape[0]):
    o = nn.predict(X_test[i])
    predictions.append(np.argmax(o))

#10*10矩陣(分類是10) ,對角線表示預(yù)測的對,行是預(yù)測值,列是真實值
print(confusion_matrix(y_test,predictions))
'''
v[[43  0  0  0  0  0  0  0  0  0]
 [ 0 37  0  0  0  0  1  0  0  8]
 [ 0  1 38  3  0  0  0  0  0  0]
 [ 0  0  1 47  0  1  0  1  0  0]
 [ 0  0  0  0 47  0  0  0  0  1]
 [ 0  0  0  0  0 48  1  0  0  0]
 [ 0  2  0  0  0  0 38  0  0  0]
 [ 0  0  0  0  1  0  0 37  0  1]
 [ 1  7  0  1  0  4  1  0 26  6]
 [ 0  0  0  4  0  0  0  0  0 43]]
'''
print(classification_report(y_test,predictions))
'''統(tǒng)計
             precision    recall  f1-score   support

          0       0.98      1.00      0.99        43
          1       0.79      0.80      0.80        46
          2       0.97      0.90      0.94        42
          3       0.85      0.94      0.90        50
          4       0.98      0.98      0.98        48
          5       0.91      0.98      0.94        49
          6       0.93      0.95      0.94        40
          7       0.97      0.95      0.96        39
          8       1.00      0.57      0.72        46
          9       0.73      0.91      0.81        47

avg / total       0.91      0.90      0.90       450

'''

用向量式編程的思想改進(jìn)我們的代碼:

# -*- coding:utf-8 -*-
#!/usr/bin/local/bin/python

import numpy as np

class FullConnectedLayer(object):
    def __init__(self,input_size,
                 output_size,
                 learing_rate,
                 activator):
        self.input_size = input_size
        self.output_size = output_size
        self.activator = activator
        self.learning_rate = learing_rate
        self.W = np.random.uniform(-0.1,0.1,(output_size,input_size))
        self.b = np.zeros((output_size,1))
        self.output = np.zeros((output_size,1))
    def forward(self,input_array):
        self.input = input_array
        self.output = self.activator.forward(
            np.dot(self.W,input_array)+self.b
        )

    def backward(self,delta_array):
        self.delta = self.activator.backward(self.input) * np.dot(
            self.W.T,delta_array
        )
        self.W_grad = np.dot(delta_array,self.input.T)
        self.b_grad = delta_array

    def update(self):
        self.W += self.learning_rate * self.W_grad
        self.b += self.learning_rate * self.b_grad
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 楔子 自古正邪不兩立,幾百年前正道大敗魔教,將之趕出中原,如今魔教欲卷土重來,江湖人人自危,風(fēng)雨欲來。 01 落日...
    胡小楓閱讀 730評論 8 10
  • 數(shù)據(jù)類型 數(shù)據(jù)類型由三部分組成身份(id)、類型(type)、值??磳ο笥袥]有改變使用id(object)查看對象...
    letry閱讀 387評論 0 0
  • 記得小時候買磁帶,硬拖帶拉爸媽,站在營業(yè)柜臺前墊著腳尖指著里面看的見摸不著的磁帶,然后滿心歡喜的看著爸媽付錢,...
    錢爸比閱讀 436評論 0 1

友情鏈接更多精彩內(nèi)容