XOR神经网络收敛到0.5

sam*_*sam 5 python machine-learning backpropagation neural-network

我已经实现了以下神经网络来解决Python中的XOR问题。我的神经网络由2个神经元的输入层,2个神经元的1个隐藏层和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
Run Code Online (Sandbox Code Playgroud)

反向传播似乎都是正确的。我想到的唯一问题是我对偏差单元的实施存在一些问题。无论哪种方式,每次我运行代码时,每个输入的所有谓词都收敛到大约0.5。我已经搜索了代码,但似乎找不到问题所在。谁能指出我的实现有什么问题?我感谢任何反馈。

如果出于某种原因可能会有所帮助,这是我得到的输出:

[[0 0]] : [[ 0.5]]
[[0 1]] : [[ 0.49483673]]
[[1 0]] : [[ 0.52006739]]
[[1 1]] : [[ 0.51610963]]
Run Code Online (Sandbox Code Playgroud)

ray*_*ica 4

您对前向和反向传播的实现或多或少是正确的。然而,你出错的地方很简单。第一个小错误是查看fit函数内部 - 特别是循环内的第一个语句for

x = x if predict else X[0]
Run Code Online (Sandbox Code Playgroud)

您是说,如果您不进行预测(即执行训练),则在随机梯度下降的每次迭代期间选择的输入示例必须始终是第一个示例,即[0 0](即X[0])。这就是为什么您的所有预测都得到 0.5 的原因,因为您仅使用第一个输入进行训练。您需要更改此设置,以便它读取正确的示例,即 example i

x = x if predict else X[i]
Run Code Online (Sandbox Code Playgroud)

您需要进行的最后一个更改是您的s_prime功能。sigmoid 函数的导数确实就是您所拥有的:

def s_prime(z):
    return np.multiply(sigmoid(z), sigmoid(1.0-z))
Run Code Online (Sandbox Code Playgroud)

当您计算前向传播时,您已经计算了 中每个神经元的输出激活a_s,因此当您计算这些神经元的局部导数时,您可以直接提供输出激活,s_prime因此无需计算这些神经元的 sigmoid再次。

所以:

def s_prime(z):
    return np.multiply(z, 1.0-z)
Run Code Online (Sandbox Code Playgroud)

进行这两项更改后,我们现在得到以下输出:

[[0 0]] : [[ 0.00239857]]
[[0 1]] : [[ 0.99816778]]
[[1 0]] : [[ 0.99816596]]
[[1 1]] : [[ 0.0021052]]
Run Code Online (Sandbox Code Playgroud)

您可以看到,这或多或少与异或门的预期输出一致。我可以建议的最后一件事是,考虑到您当前的代码结构,10000 次迭代在计算上太长了。我注意到,通过上述修正,我们能够以更少的迭代次数达到预期的输出。我将迭代次数减少到 1000,并将学习率提高alpha到 0.75。改变这两件事我们现在得到:

[[0 0]] : [[ 0.03029435]]
[[0 1]] : [[ 0.95397528]]
[[1 0]] : [[ 0.95371525]]
[[1 1]] : [[ 0.04796917]]
Run Code Online (Sandbox Code Playgroud)

  • @Sam 非常欢迎你。我曾经经历过……我盯着代码看了几个小时,但似乎不知道出了什么问题。我向我的一个朋友展示,他立即注意到了,这是那些小但非常关键的错误之一!如果您能以新的眼光来注意到问题所在,这会有所帮助:)。 (2认同)