太天才了,把感知機組裝在一起是不是就是神經網路了?

TechFlow2019發表於2021-01-13

大家好,今天來繼續聊聊深度學習。

有同學跟我說很久沒有更新深度學習的模型了,倒不是不願意更新,主要是一次想把一個技術專題寫完。但是純技術文章觀眾老爺們不太愛看,所以我一般都把純技術文章放在次條。不過既然有同學催更,那麼我還是響應一下需求,來更新一篇。

神經網路與感知機的不同

上一篇文章當中我們講了感知機,由於文章比較久了,估計很多同學沒有看過,沒有關係,可以點選下方傳送門回去補課。

深度學習開篇,來聊聊感知機的原理

我們當時在文章裡放了一張圖,這張圖是一個多層感知機的圖,大家看一下,就是下面這張圖。

這張圖乍一看沒什麼問題,但是細想會覺得有點奇怪,好像我們印象裡看到的神經網路的圖片也是這樣的,既然如此,那麼它們之間有什麼區別呢?

表面上最明顯的區別就是名字不同,這是一張神經網路的圖片。我們發現同樣是三層,但是它每一層的名字分別是輸入層、中間層(隱藏層)和輸出層。我們一般把輸入層和輸出層單獨命名,中間的若干層都叫做隱藏層或者是中間層。當然像是感知機一樣,以數字來命名層數也是可以的,比如下圖當中的輸入層叫做第0層,中間層叫做第一層,最後輸出層叫做第2層。

我們一般不把輸出層看作是有效的神經網路,所以下圖的網路被稱為二層神經網路,而不是三層神經網路。

除了名字的叫法不同之外,還有一個最關鍵的區別就是啟用函式,為了說明白這點,我們先來看看神經網路當中的訊號傳遞。

訊號傳遞

下圖是一張我隨便找來的神經網路圖,我們可以看到輸入的第一個節點被置為了1。這樣做是為了方便引入偏移量,只是我們一般情況下畫圖的時候,不會特意把偏移量畫出來。我們以下圖為例子來看下神經網路當中訊號的傳遞方式。

我們以為例,可以來試著寫出的表示式,它一共有三個input,分別是1, , ,然後我們也可以看到每一個input對應的權重,所以最後可以寫成:

到這裡還沒有結束,神經網路當中每一層都會有對應的啟用函式。一般情況下同一層網路當中的啟用函式相同,我們把它叫做h,所以最終這個節點的輸出並不是剛剛得到的,而是

啟用函式我們已經比較熟悉了,之前介紹過很多次,常用的大概有以下幾種:Relu、Sigmoid、tanh、softmax,以及一些衍生出的變種。一般情況下,在輸出層之前我們通常使用Relu,如果模型是一個分類模型,我們會在最後使用Sigmoid或者是softmax,如果是迴歸模型則不使用任何啟用函式。

Sigmoid我們已經很熟悉了,如果我們把LR模型也看成是一個單層的神經網路的話,那麼Sigmoid就是它的啟用函式。Sigmoid應用在二分類場景當中單個的輸出節點上,輸出的值如果大於0.5表示為真,否則為假。在一些概率預估場景當中,也可以認為輸出值就代表了事件發生的概率。

與之對應的是softmax函式,它應用在多分類問題當中,它應用的節點數量不是1個,而是k個。這裡的k表示多分類場景當中的類別數量。我們以k=3舉例,看下圖:

在圖中一共有三個節點,對於每一個節點來說,它的公式可以寫成:

其實和Sigmoid的計算方式是一樣的,只不過最後計算了一個權重。最後我們會在這k個節點當中選擇最大的作為最終的分類結果。

程式碼實現

最後,我們來試著寫一下神經網路的程式碼,由於現在我們還沒有介紹神經網路的訓練方法,所以我們只能實現它預測的部分。等我們介紹完了反向傳播演算法之後,再來補上模型訓練的過程。

如果不考慮反向傳播的話,其實整個演算法的程式碼非常簡單,只要熟悉Python語法的同學都能看懂。

import numpy as np

def relu(x):
    return np.where(x > 0, x, 0)


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


class NeuralNetwork():
    def __init__(self):
        self.params = {}
        self.params['W1'] = np.random.rand(23)
        self.params['b1'] = np.random.rand(13)
        self.params['W2'] = np.random.rand(32)
        self.params['b2'] = np.random.rand(12)
        self.params['W3'] = np.random.rand(21)
        self.params['b3'] = np.random.rand(11)
       
    def forward(self, x):
        a1 = np.dot(x, self.params['W1']) + self.params['b1']
        z1 = relu(a1)
        
        a2 = np.dot(z1, self.params['W2']) + self.params['b2']
        z2 = relu(a2)
        
        a3 = np.dot(z2, self.params['W3']) + self.params['b3']
        return np.where(sigmoid(a3) > 0.510)
    
    
if __name__ == "__main__":
    nn = NeuralNetwork()
    print(nn.forward(np.array([32])))

在下一篇文章當中我們將會來探討神經網路的訓練方法,也就是大名鼎鼎的反向傳播演算法,看看它是在神經網路當中如何運作的。

今天的文章就到這裡,衷心祝願大家每天都有所收穫。如果還喜歡今天的內容的話,請來一個三連支援吧~(點贊、關注、轉發

相關文章