簡體   English   中英

神經網絡backprop沒有完全訓練

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM