Python?實(shí)現(xiàn)一個(gè)全連接的神經(jīng)網(wǎng)絡(luò)
前言
在這篇文章中,準(zhǔn)備用 Python 從頭開始實(shí)現(xiàn)一個(gè)全連接的神經(jīng)網(wǎng)絡(luò)。你可能會(huì)問,為什么需要自己實(shí)現(xiàn),有很多庫和框架可以為我們做這件事,比如 Tensorflow、Pytorch 等。這里只想說只有自己親手實(shí)現(xiàn)了,才是自己的。
想到今天自己從接觸到從事與神經(jīng)網(wǎng)絡(luò)相關(guān)工作已經(jīng)多少 2、3 年了,其中也嘗試用 tensorflow 或 pytorch 框架去實(shí)現(xiàn)一些經(jīng)典網(wǎng)絡(luò)。不過對(duì)于反向傳播背后機(jī)制還是比較模糊。
梯度
梯度是函數(shù)上升最快方向,最快的方向也就是說這個(gè)方向函數(shù)形狀很陡峭,那么也是函數(shù)下降最快的方向。
雖然關(guān)于一些理論、梯度消失和結(jié)點(diǎn)飽和可以輸出一個(gè) 1、2、3 但是深究還是沒有底氣,畢竟沒有自己動(dòng)手去實(shí)現(xiàn)過一個(gè)反向傳播和完整訓(xùn)練過程。所以感覺還是浮在表面,知其所以然而。
因?yàn)樽罱幸欢慰臻e時(shí)間、所以利用這段休息時(shí)間將要把這部分知識(shí)整理一下、深入了解了解
類型 | 符號(hào) | 說明 | 表達(dá)式 | 維度 |
---|---|---|---|---|
標(biāo)量 | n^LnL | 表示第 L 層神經(jīng)元的數(shù)量 | ||
向量 | B^LBL | 表示第 L 層偏置 | n^L \times 1nL×1 | |
矩陣 | W^LWL | 表示第 L 層的權(quán)重 | n^L \times n^LnL×nL | |
向量 | Z^LZL | 表示第 L 層輸入到激活函數(shù)的值 | Z^L=W^LA^{(L-1)} + B^LZL=WLA(L−1)+BL | n^L \times 1nL×1 |
向量 | A^LAL | 表示第 L 層輸出值 | A^L = \sigma(Z^L)AL=σ(ZL) | n^L \times 1nL×1 |
我們大家可能都了解訓(xùn)練神經(jīng)網(wǎng)絡(luò)的過程,就是更新網(wǎng)絡(luò)參數(shù),更新的方向是降低損失函數(shù)值。也就是將學(xué)習(xí)問題轉(zhuǎn)換為了一個(gè)優(yōu)化的問題。那么如何更新參數(shù)呢?我們需要計(jì)算參與訓(xùn)練參數(shù)相對(duì)于損失函數(shù)的導(dǎo)數(shù),然后求解梯度,然后使用梯度下降法來更新參數(shù),迭代這個(gè)過程,可以找到一個(gè)最佳的解決方案來最小化損失函數(shù)。
我們知道反向傳播主要就是用來結(jié)算損失函數(shù)相對(duì)于權(quán)重和偏置的導(dǎo)數(shù)
可能已經(jīng)聽到或讀到了,很多關(guān)于在網(wǎng)絡(luò)通過反向傳播來傳遞誤差的信息。然后根據(jù)神經(jīng)元的 w 和 b 對(duì)偏差貢獻(xiàn)的大小。也就是將誤差分配到每一個(gè)神經(jīng)元上。 但這里的誤差(error)是什么意思呢?這個(gè)誤差的確切的定義又是什么?答案是這些誤差是由每一層神經(jīng)網(wǎng)絡(luò)所貢獻(xiàn)的,而且某一層的誤差是后繼層誤差基礎(chǔ)上分?jǐn)偟模W(wǎng)絡(luò)中第 層的誤差用
來表示。
反向傳播是基于 4 個(gè)基本方程的,通過這些方程來計(jì)算誤差和損失函數(shù),這里將這 4 個(gè)方程一一列出
關(guān)于如何解讀這個(gè) 4 個(gè)方程,隨后想用一期分享來說明。
class NeuralNetwork(object): def __init__(self): pass def forward(self,x): # 返回前向傳播的 Z 也就是 w 和 b 線性組合,輸入激活函數(shù)前的值 # 返回激活函數(shù)輸出值 A # z_s , a_s pass def backward(self,y,z_s,a_s): #返回前向傳播中學(xué)習(xí)參數(shù)的導(dǎo)數(shù) dw db pass def train(self,x,y,batch_size=10,epochs=100,lr=0.001): pass
我們都是神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)過程,也就是訓(xùn)練過程。主要分為兩個(gè)階段前向傳播和后向傳播
- 在前向傳播函數(shù)中,主要計(jì)算傳播的 Z 和 A,關(guān)于 Z 和 A 具體是什么請(qǐng)參見前面表格
- 在反向傳播中計(jì)算可學(xué)習(xí)變量 w 和 b 的導(dǎo)數(shù)
def __init__(self,layers = [2 , 10, 1], activations=['sigmoid', 'sigmoid']): assert(len(layers) == len(activations)+1) self.layers = layers self.activations = activations self.weights = [] self.biases = [] for i in range(len(layers)-1): self.weights.append(np.random.randn(layers[i+1], layers[i])) self.biases.append(np.random.randn(layers[i+1], 1))
- layers 參數(shù)用于指定每一層神經(jīng)元的個(gè)數(shù)
- activations 為每一層指定激活函數(shù),也就是
來簡單讀解一下代碼 assert(len(layers) == len(activations)+1)
for i in range(len(layers)-1): self.weights.append(np.random.randn(layers[i+1], layers[i])) self.biases.append(np.random.randn(layers[i+1], 1))
因?yàn)闄?quán)重連接每一個(gè)層神經(jīng)元的 w 和 b ,也就兩兩層之間的方程,上面代碼是對(duì)
前向傳播
def feedforward(self, x): # 返回前向傳播的值 a = np.copy(x) z_s = [] a_s = [a] for i in range(len(self.weights)): activation_function = self.getActivationFunction(self.activations[i]) z_s.append(self.weights[i].dot(a) + self.biases[i]) a = activation_function(z_s[-1]) a_s.append(a) return (z_s, a_s)
這里激活函數(shù),這個(gè)函數(shù)返回值是一個(gè)函數(shù),在 python 用 lambda
來返回一個(gè)函數(shù),這里簡答留下一個(gè)伏筆,隨后會(huì)對(duì)其進(jìn)行修改。
@staticmethod def getActivationFunction(name): if(name == 'sigmoid'): return lambda x : np.exp(x)/(1+np.exp(x)) elif(name == 'linear'): return lambda x : x elif(name == 'relu'): def relu(x): y = np.copy(x) y[y<0] = 0 return y return relu else: print('Unknown activation function. linear is used') return lambda x: x
[@staticmethod] def getDerivitiveActivationFunction(name): if(name == 'sigmoid'): sig = lambda x : np.exp(x)/(1+np.exp(x)) return lambda x :sig(x)*(1-sig(x)) elif(name == 'linear'): return lambda x: 1 elif(name == 'relu'): def relu_diff(x): y = np.copy(x) y[y>=0] = 1 y[y<0] = 0 return y return relu_diff else: print('Unknown activation function. linear is used') return lambda x: 1
反向傳播
這是本次分享重點(diǎn)
def backpropagation(self,y, z_s, a_s): dw = [] # dC/dW db = [] # dC/dB deltas = [None] * len(self.weights) # delta = dC/dZ 計(jì)算每一層的誤差 # 最后一層誤差 deltas[-1] = ((y-a_s[-1])*(self.getDerivitiveActivationFunction(self.activations[-1]))(z_s[-1])) # 反向傳播 for i in reversed(range(len(deltas)-1)): deltas[i] = self.weights[i+1].T.dot(deltas[i+1])*(self.getDerivitiveActivationFunction(self.activations[i])(z_s[i])) #a= [print(d.shape) for d in deltas] batch_size = y.shape[1] db = [d.dot(np.ones((batch_size,1)))/float(batch_size) for d in deltas] dw = [d.dot(a_s[i].T)/float(batch_size) for i,d in enumerate(deltas)] # 返回權(quán)重(weight)矩陣 and 偏置向量(biases) return dw, db
首先計(jì)算最后一層誤差根據(jù) BP1 等式可以得到下面的式子
deltas[-1] = ((y-a_s[-1])*(self.getDerivitiveActivationFunction(self.activations[-1]))(z_s[-1]))
接下來基于上一層的 誤差來計(jì)算當(dāng)前層
batch_size = y.shape[1] db = [d.dot(np.ones((batch_size,1)))/float(batch_size) for d in deltas] dw = [d.dot(a_s[i].T)/float(batch_size) for i,d in enumerate(deltas)]
開始訓(xùn)練
def train(self, x, y, batch_size=10, epochs=100, lr = 0.01): # update weights and biases based on the output for e in range(epochs): i=0 while(i<len(y)): x_batch = x[i:i+batch_size] y_batch = y[i:i+batch_size] i = i+batch_size z_s, a_s = self.feedforward(x_batch) dw, db = self.backpropagation(y_batch, z_s, a_s) self.weights = [w+lr*dweight for w,dweight in zip(self.weights, dw)] self.biases = [w+lr*dbias for w,dbias in zip(self.biases, db)] # print("loss = {}".format(np.linalg.norm(a_s[-1]-y_batch) ))
到此這篇關(guān)于Python 實(shí)現(xiàn)一個(gè)全連接的神經(jīng)網(wǎng)絡(luò)的文章就介紹到這了,更多相關(guān)Python 全連接神經(jīng)網(wǎng)絡(luò)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 如何用Python 實(shí)現(xiàn)全連接神經(jīng)網(wǎng)絡(luò)(Multi-layer Perceptron)
- Python利用全連接神經(jīng)網(wǎng)絡(luò)求解MNIST問題詳解
- python神經(jīng)網(wǎng)絡(luò)pytorch中BN運(yùn)算操作自實(shí)現(xiàn)
- python神經(jīng)網(wǎng)絡(luò)Keras常用學(xué)習(xí)率衰減匯總
- python神經(jīng)網(wǎng)絡(luò)MobileNetV3?small模型的復(fù)現(xiàn)詳解
- python神經(jīng)網(wǎng)絡(luò)Keras實(shí)現(xiàn)GRU及其參數(shù)量
- python神經(jīng)網(wǎng)絡(luò)Keras實(shí)現(xiàn)LSTM及其參數(shù)量詳解
相關(guān)文章
python使用xslt提取網(wǎng)頁數(shù)據(jù)的方法
這篇文章主要為大家詳細(xì)介紹了Python使用xslt提取網(wǎng)頁數(shù)據(jù)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02Python爬蟲之PhantomJS和handless的使用詳解
這篇文章主要介紹了Python爬蟲之PhantomJS和handless的使用詳解,PhantomJS是一個(gè)基于Webkit的headless瀏覽器,它會(huì)把網(wǎng)站加載到內(nèi)存并使用webkit來編譯解釋執(zhí)行頁面上的JavaScript代碼,由于不進(jìn)行css和gui渲染、不展示圖形界面,需要的朋友可以參考下2023-09-09Python語言實(shí)現(xiàn)百度語音識(shí)別API的使用實(shí)例
這篇文章主要介紹了Python語言實(shí)現(xiàn)百度語音識(shí)別API的使用實(shí)例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12Python批量獲取并保存手機(jī)號(hào)歸屬地和運(yùn)營商的示例
這篇文章主要介紹了Python批量獲取并保存手機(jī)號(hào)的歸屬地和運(yùn)營商的示例,幫助大家更好的利用python處理數(shù)據(jù),感興趣的朋友可以了解下2020-10-10一文搞懂Python的hasattr()、getattr()、setattr()?函數(shù)用法
python中的getattr()、setattr()、hasattr()函數(shù)均是對(duì)類屬性或方法的操作,其中g(shù)etattr()用于獲取類或?qū)嵗兄付ǚ椒ǐ@取屬性的值,setattr()用于設(shè)置類或?qū)嵗袑傩曰蚍椒?hasattr()用于判斷類或?qū)嵗惺欠翊嬖谥付ǖ膶傩曰蚍椒?本文通過例子給大家詳解,一起看看吧2022-04-04使用Python對(duì)Csv文件操作實(shí)例代碼
這篇文章主要介紹了使用Python對(duì)Csv文件操作實(shí)例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05