[英]Neural network backprop not fully training
我有這樣的神經網絡,我訓練過看到它,它起作用,或者至少看起來有效,但問題在於訓練。 我正在嘗試訓練它作為OR門,但它似乎永遠不會到達那里,輸出往往看起來像這樣:
prior to training:
[[0.50181624]
[0.50183743]
[0.50180414]
[0.50182533]]
post training:
[[0.69641759]
[0.754652 ]
[0.75447178]
[0.79431198]]
expected output:
[[0]
[1]
[1]
[1]]
我有這個損失圖:
奇怪的是它似乎是訓練,但同時還沒有達到預期的輸出。 我知道它永遠不會真正實現0和1,但同時我希望它能夠管理並獲得更接近預期輸出的東西。
我有一些問題試圖找出如何支持錯誤,因為我想讓這個網絡有任意數量的隱藏層,所以我將局部漸變存儲在一個層中,沿着權重,並從末尾發送錯誤背部。
我懷疑的主要功能是NeuralNetwork.train和兩種前進方法。
import sys
import math
import numpy as np
import matplotlib.pyplot as plt
from itertools import product
class NeuralNetwork:
class __Layer:
def __init__(self,args):
self.__epsilon = 1e-6
self.localGrad = 0
self.__weights = np.random.randn(
args["previousLayerHeight"],
args["height"]
)*0.01
self.__biases = np.zeros(
(args["biasHeight"],1)
)
def __str__(self):
return str(self.__weights)
def forward(self,X):
a = np.dot(X, self.__weights) + self.__biases
self.localGrad = np.dot(X.T,self.__sigmoidPrime(a))
return self.__sigmoid(a)
def adjustWeights(self, err):
self.__weights -= (err * self.__epsilon)
def __sigmoid(self, z):
return 1/(1 + np.exp(-z))
def __sigmoidPrime(self, a):
return self.__sigmoid(a)*(1 - self.__sigmoid(a))
def __init__(self,args):
self.__inputDimensions = args["inputDimensions"]
self.__outputDimensions = args["outputDimensions"]
self.__hiddenDimensions = args["hiddenDimensions"]
self.__layers = []
self.__constructLayers()
def __constructLayers(self):
self.__layers.append(
self.__Layer(
{
"biasHeight": self.__inputDimensions[0],
"previousLayerHeight": self.__inputDimensions[1],
"height": self.__hiddenDimensions[0][0]
if len(self.__hiddenDimensions) > 0
else self.__outputDimensions[0]
}
)
)
for i in range(len(self.__hiddenDimensions)):
self.__layers.append(
self.__Layer(
{
"biasHeight": self.__hiddenDimensions[i + 1][0]
if i + 1 < len(self.__hiddenDimensions)
else self.__outputDimensions[0],
"previousLayerHeight": self.__hiddenDimensions[i][0],
"height": self.__hiddenDimensions[i + 1][0]
if i + 1 < len(self.__hiddenDimensions)
else self.__outputDimensions[0]
}
)
)
def forward(self,X):
out = self.__layers[0].forward(X)
for i in range(len(self.__layers) - 1):
out = self.__layers[i+1].forward(out)
return out
def train(self,X,Y,loss,epoch=5000000):
for i in range(epoch):
YHat = self.forward(X)
delta = -(Y-YHat)
loss.append(sum(Y-YHat))
err = np.sum(np.dot(self.__layers[-1].localGrad,delta.T), axis=1)
err.shape = (self.__hiddenDimensions[-1][0],1)
self.__layers[-1].adjustWeights(err)
i=0
for l in reversed(self.__layers[:-1]):
err = np.dot(l.localGrad, err)
l.adjustWeights(err)
i += 1
def printLayers(self):
print("Layers:\n")
for l in self.__layers:
print(l)
print("\n")
def main(args):
X = np.array([[x,y] for x,y in product([0,1],repeat=2)])
Y = np.array([[0],[1],[1],[1]])
nn = NeuralNetwork(
{
#(height,width)
"inputDimensions": (4,2),
"outputDimensions": (1,1),
"hiddenDimensions":[
(6,1)
]
}
)
print("input:\n\n",X,"\n")
print("expected output:\n\n",Y,"\n")
nn.printLayers()
print("prior to training:\n\n",nn.forward(X), "\n")
loss = []
nn.train(X,Y,loss)
print("post training:\n\n",nn.forward(X), "\n")
nn.printLayers()
fig,ax = plt.subplots()
x = np.array([x for x in range(5000000)])
loss = np.array(loss)
ax.plot(x,loss)
ax.set(xlabel="epoch",ylabel="loss",title="logic gate training")
plt.show()
if(__name__=="__main__"):
main(sys.argv[1:])
有人可以指出我在這里做錯了什么,我強烈懷疑它與我處理矩陣的方式有關,但同時我也沒有絲毫知道發生了什么。
感謝您抽出寶貴時間閱讀我的問題,並花時間回復(如果相關)。
編輯:實際上這有很多錯誤,但我仍然對如何解決這個問題感到困惑。 雖然損失圖看起來像是它的訓練,但實際上,我上面做的數學運算是錯誤的。
看看訓練功能。
def train(self,X,Y,loss,epoch=5000000):
for i in range(epoch):
YHat = self.forward(X)
delta = -(Y-YHat)
loss.append(sum(Y-YHat))
err = np.sum(np.dot(self.__layers[-1].localGrad,delta.T), axis=1)
err.shape = (self.__hiddenDimensions[-1][0],1)
self.__layers[-1].adjustWeights(err)
i=0
for l in reversed(self.__layers[:-1]):
err = np.dot(l.localGrad, err)
l.adjustWeights(err)
i += 1
注意我如何得到delta = - (Y-Yhat),然后用最后一層的“局部梯度”對它進行點積。 “局部梯度”是局部W梯度。
def forward(self,X):
a = np.dot(X, self.__weights) + self.__biases
self.localGrad = np.dot(X.T,self.__sigmoidPrime(a))
return self.__sigmoid(a)
我正在跳過規則中的一步。 我應該首先乘以W * sigprime(XW + b),因為它是X的局部梯度,然后是局部W梯度。 我試過了,但我仍然遇到問題,這里是新的轉發方法(注意__init__層需要為新變量初始化,並且我將激活函數更改為tanh)
def forward(self, X):
a = np.dot(X, self.__weights) + self.__biases
self.localPartialGrad = self.__tanhPrime(a)
self.localWGrad = np.dot(X.T, self.localPartialGrad)
self.localXGrad = np.dot(self.localPartialGrad,self.__weights.T)
return self.__tanh(a)
並更新了訓練方法,看起來像這樣:
def train(self, X, Y, loss, epoch=5000):
for e in range(epoch):
Yhat = self.forward(X)
err = -(Y-Yhat)
loss.append(sum(err))
print("loss:\n",sum(err))
for l in self.__layers[::-1]:
l.adjustWeights(err)
if(l != self.__layers[0]):
err = np.multiply(err,l.localPartialGrad)
err = np.multiply(err,l.localXGrad)
我得到的新圖表到處都是,我不知道發生了什么。 這是我改變的最后一段代碼:
def adjustWeights(self, err):
perr = np.multiply(err, self.localPartialGrad)
werr = np.sum(np.dot(self.__weights,perr.T),axis=1)
werr = werr * self.__epsilon
werr.shape = (self.__weights.shape[0],1)
self.__weights = self.__weights - werr
您的網絡正在學習,從損失圖表中可以看出,所以backprop實現是正確的(恭喜!)。 這種特殊架構的主要問題是激活函數的選擇: sigmoid
。 我已經用tanh
取代了sigmoid
並且它立即工作得更好。
從關於CV.SE的討論 :
這種選擇有兩個原因(假設您已經規范化了數據,這非常重要):
具有更強的梯度:由於數據以0為中心,因此導數更高。 為了看到這一點,計算tanh函數的導數並注意輸入值在[0,1]范圍內。 tanh函數的范圍是[-1,1],而sigmoid函數的范圍是[0,1]
避免漸變中的偏差。 本文對此進行了很好的解釋,理解這些問題值得閱讀。
雖然我確信基於sigmoid
的NN也可以被訓練,看起來它對輸入值更敏感(注意它們不是以零為中心),因為激活本身不是以零為中心的。 tanh
一切都比sigmoid
好,所以更簡單的方法就是使用激活函數。
關鍵的變化是這樣的:
def __tanh(self, z):
return np.tanh(z)
def __tanhPrime(self, a):
return 1 - self.__tanh(a) ** 2
...而不是__sigmoid
和__sigmoidPrime
。
我還調整了一些超參數,以便網絡現在可以在10萬個時代中學習,而不是5米:
prior to training:
[[ 0. ]
[-0.00056925]
[-0.00044885]
[-0.00101794]]
post training:
[[0. ]
[0.97335842]
[0.97340917]
[0.98332273]]
完整的代碼就在這個要點中 。
好吧,我是個白痴。 我錯了是對的,但我錯了,我錯了。 讓我解釋。
在向后訓練方法中,我得到了正確訓練的最后一層,但是之后的所有層都沒有正確訓練,因此為什么上面的網絡得出結果,它確實是訓練,但只有一層。
那我做錯了什么? 好吧,我只是乘以權重的當地重量與輸出相關,因此鏈規則是部分正確的。
讓我們說損失函數是這樣的:
t = Y-X2
損失= 1/2 *(t)^ 2
a2 = X1W2 + b
X2 =激活(a2)
a1 = X0W1 + b
X1 =激活(a1)
我們知道相對於W2的損失的導數是 - (Y-X2)* X1。 這是在我的培訓功能的第一部分完成的:
def train(self,X,Y,loss,epoch=5000000):
for i in range(epoch):
#First part
YHat = self.forward(X)
delta = -(Y-YHat)
loss.append(sum(Y-YHat))
err = np.sum(np.dot(self.__layers[-1].localGrad,delta.T), axis=1)
err.shape = (self.__hiddenDimensions[-1][0],1)
self.__layers[-1].adjustWeights(err)
i=0
#Second part
for l in reversed(self.__layers[:-1]):
err = np.dot(l.localGrad, err)
l.adjustWeights(err)
i += 1
然而第二部分是我搞砸的地方。 為了計算相對於W1的損失,我必須將原始誤差 - (Y-X2)乘以W2,因為W2是最后一層的局部X梯度,並且由於鏈規則必須首先完成。 然后我可以乘以局部W梯度(X1)來獲得相對於W1的損失。 我沒有首先對局部X梯度進行乘法,所以最后一層確實是訓練,但是之后的所有層都有一個隨着圖層增加而放大的誤差。
為了解決這個問題,我更新了火車方法:
def train(self,X,Y,loss,epoch=10000):
for i in range(epoch):
YHat = self.forward(X)
err = -(Y-YHat)
loss.append(sum(Y-YHat))
werr = np.sum(np.dot(self.__layers[-1].localWGrad,err.T), axis=1)
werr.shape = (self.__hiddenDimensions[-1][0],1)
self.__layers[-1].adjustWeights(werr)
for l in reversed(self.__layers[:-1]):
err = np.multiply(err, l.localXGrad)
werr = np.sum(np.dot(l.weights,err.T),axis=1)
l.adjustWeights(werr)
現在我得到的損失圖看起來像這樣:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.