我已经实现了以下神经网络来解决Python中的XOR问题。我的神经网络由2个神经元的输入层,2个神经元的隐藏层和1个神经元的输出层组成。我使用Sigmoid函数作为隐藏层的激活函数,使用线性(标识)函数作为输出层的激活函数:
import numpy as np
def sigmoid(z):
return 1/(1+np.exp(-z))
def s_prime(z):
return np.multiply(sigmoid(z), sigmoid(1.0-z))
def init_weights(layers, epsilon):
weights = []
for i in range(len(layers)-1):
w = np.random.rand(layers[i+1], layers[i]+1)
w = w * 2*epsilon - epsilon
weights.append(np.mat(w))
return weights
def fit(X, Y, w, predict=False, x=None):
w_grad = ([np.mat(np.zeros(np.shape(w[i])))
for i in range(len(w))])
for i in range(len(X)):
x = x if predict else X[0]
y = Y[0,i]
# forward propagate
a = x
a_s = []
for j in range(len(w)):
a = np.mat(np.append(1, a)).T
a_s.append(a)
z = w[j] * a
a = sigmoid(z)
if predict: return a
# backpropagate
delta = a - y.T
w_grad[-1] += delta * a_s[-1].T
for j in reversed(range(1, len(w))):
delta = np.multiply(w[j].T*delta, s_prime(a_s[j]))
w_grad[j-1] += (delta[1:] * a_s[j-1].T)
return [w_grad[i]/len(X) for i in range(len(w))]
def predict(x):
return fit(X, Y, w, True, x)
####
X = np.mat([[0,0],
[0,1],
[1,0],
[1,1]])
Y = np.mat([0,1,1,0])
layers = [2,2,1]
epochs = 10000
alpha = 0.5
w = init_weights(layers, 1)
for i in range(epochs):
w_grad = fit(X, Y, w)
print w_grad
for j in range(len(w)):
w[j] -= alpha * w_grad[j]
for i in range(len(X)):
x = X[i]
guess = predict(x)
print x, ":", guess
反向传播似乎都是正确的;我想到的唯一问题是我实施偏差单元的一些问题。无论哪种方式,每次运行代码时,每个输入的所有预测都会收敛到大约0.5。我仔细检查了代码,似乎无法找到错误。任何人都可以指出我的实施有什么问题吗?我感谢任何反馈。
如果由于任何原因它可能会有所帮助,这就是我得到的那种输出:
[[0 0]] : [[ 0.5]]
[[0 1]] : [[ 0.49483673]]
[[1 0]] : [[ 0.52006739]]
[[1 1]] : [[ 0.51610963]]
答案 0 :(得分:4)
您的前向和后向传播的实施或多或少都是正确的。但是,你出错的地方很简单。第一个小错误是查看set yrange[0.001:8]
函数 - 特别是fit
循环中的第一个语句:
for
你是说如果你没有预测(即进行训练),在每次随机梯度下降迭代期间选择的输入例子必须始终是第一个例子,即{{ 1}}(即x = x if predict else X[0]
)。这就是为什么你为所有预测得到0.5的原因,因为你只使用第一个输入进行训练。您需要对其进行更改,以便它读取正确的示例,即示例[0 0]
:
X[0]
您需要进行的最后一项更改是i
功能。 sigmoid函数的衍生物确实就是你所拥有的:
x = x if predict else X[i]
当你计算前向传播时,你已经计算了s_prime
中每个神经元的输出激活,所以当你计算这些神经元的局部导数时,你直接将输出激活提供给def s_prime(z):
return np.multiply(sigmoid(z), sigmoid(1.0-z))
所以你不需要再次计算这些sigmoid。
因此:
a_s
一旦我做了这两个更改,我们现在得到这个输出:
s_prime
您可以看到,这或多或少与XOR门的预期输出一致。我可以推荐的最后一件事是,根据您当前的代码结构,10000次迭代计算得太长。我注意到,通过上述修正,我们能够以更少的迭代次数达到预期的输出。我已将迭代次数减少到1000,并且我将学习率def s_prime(z):
return np.multiply(z, 1.0-z)
提高到0.75。改变我们现在得到的这两件事:
[[0 0]] : [[ 0.00239857]]
[[0 1]] : [[ 0.99816778]]
[[1 0]] : [[ 0.99816596]]
[[1 1]] : [[ 0.0021052]]