我有这样的神经网络,我训练过看到它,它起作用,或者至少看起来有效,但问题在于训练。我正在努力训练它作为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:])
有人可以指出我在这里做错了什么,我强烈怀疑这与我处理矩阵的方式有关,但与此同时我也不知道发生了什么。< / p>
感谢您花时间阅读我的问题,并花时间回复(如果相关)。
编辑: 实际上这有很多错误,但我仍然对如何修复它感到困惑。虽然损失图看起来像是它的训练,但实际上,我上面做的数学运算是错误的。
查看训练功能。
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
答案 0 :(得分:8)
您的网络正在学习,从损失表中可以看出,所以backprop实施是正确的(恭喜!)。此特定体系结构的主要问题是激活函数的选择:sigmoid
。我已将sigmoid
替换为tanh
,并且它会立即更好地运行。
这种选择有两个原因(假设你已经标准化了 你的数据,这非常重要):
具有更强的渐变:因为数据以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]]
完整的代码位于this gist。
答案 1 :(得分:1)
我是个白痴。我错了是对的,但我错了,我错了。让我解释一下。
在向后训练方法中,我正确地训练了最后一层,但是之后的所有层都没有正确训练,因此为什么上面的网络得出结果,它确实是训练,但只有一层。 / p>
那我做错了什么?那么我只乘以权重的局部重要性与输出相关,因此链规则是部分正确的。
让我们说损失函数是这样的:
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)
现在我得到的损失图看起来像这样: