TensorFlow MLP 不训练 XOR

Posted

技术标签:

【中文标题】TensorFlow MLP 不训练 XOR【英文标题】:TensorFlow MLP not training XOR 【发布时间】:2016-03-04 01:01:45 【问题描述】:

我已经使用 Google 的 TensorFlow 库构建了一个 MLP。网络正在运行,但不知何故它拒绝正确学习。无论输入实际是什么,它总是收敛到接近 1.0 的输出。

完整代码可见here。

有什么想法吗?


输入输出(batch size 4)如下:

input_data = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]  # XOR input
output_data = [[0.], [1.], [1.], [0.]]  # XOR output

n_input = tf.placeholder(tf.float32, shape=[None, 2], name="n_input")
n_output = tf.placeholder(tf.float32, shape=[None, 1], name="n_output")

隐藏层配置

# hidden layer's bias neuron
b_hidden = tf.Variable(0.1, name="hidden_bias")

# hidden layer's weight matrix initialized with a uniform distribution
W_hidden = tf.Variable(tf.random_uniform([2, hidden_nodes], -1.0, 1.0), name="hidden_weights")

# calc hidden layer's activation
hidden = tf.sigmoid(tf.matmul(n_input, W_hidden) + b_hidden)

输出层配置

W_output = tf.Variable(tf.random_uniform([hidden_nodes, 1], -1.0, 1.0), name="output_weights")  # output layer's weight matrix
output = tf.sigmoid(tf.matmul(hidden, W_output))  # calc output layer's activation

我的学习方法如下所示:

loss = tf.reduce_mean(cross_entropy)  # mean the cross_entropy
optimizer = tf.train.GradientDescentOptimizer(0.01)  # take a gradient descent for optimizing
train = optimizer.minimize(loss)  # let the optimizer train

我为 cross entropy 尝试了两种设置:

cross_entropy = -tf.reduce_sum(n_output * tf.log(output))

cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(n_output, output)

其中n_outputoutput_data 中描述的原始输出,output 是我的网络预测/计算的值。


for 循环内的训练(n 个 epoch)是这样的:

cvalues = sess.run([train, loss, W_hidden, b_hidden, W_output],
                   feed_dict=n_input: input_data, n_output: output_data)

我将结果保存到 cvalues 用于调试打印 lossW_hidden、...


无论我尝试过什么,当我测试我的网络并尝试验证输出时,它总是会产生如下内容:

(...)
step: 2000
loss: 0.0137040186673
b_hidden: 1.3272010088
W_hidden: [[ 0.23195425  0.53248233 -0.21644847 -0.54775208  0.52298909]
 [ 0.73933059  0.51440752 -0.08397482 -0.62724304 -0.53347367]]
W_output: [[ 1.65939867]
 [ 0.78912479]
 [ 1.4831928 ]
 [ 1.28612828]
 [ 1.12486529]]

(--- finished with 2000 epochs ---)

(Test input for validation:)

input: [0.0, 0.0] | output: [[ 0.99339396]]
input: [0.0, 1.0] | output: [[ 0.99289012]]
input: [1.0, 0.0] | output: [[ 0.99346077]]
input: [1.0, 1.0] | output: [[ 0.99261558]]

所以它没有正确学习,但无论输入哪种输入,它总是收敛到接近 1.0。

【问题讨论】:

您的 b_hidden 变量是标量 - 这是故意的吗?我认为您应该将其创建为b_hidden = tf.Variable(tf.constant(0.1, shape=[hidden_nodes]), name="hidden_bias"),这可能会有所帮助。要尝试的另一件事是将b_output 偏置项添加到您的输出层。 感谢您的评论。事实上,我只是没有注意到b_hidden 也应该是一个向量而不是一个标量......但是,对于每个输入,无论有没有隐藏的偏差,网络仍然收敛到接近 1.0,作为标量或向量和或者没有输出层的偏差。我真的认为我在学习方法或网络架构上遗漏了一些错误:/ 【参考方案1】:

与此同时,在一位同事的帮助下,我能够修复我的解决方案,并希望将其发布以确保完整性。我的解决方案使用交叉熵并且不改变训练数据。此外,它具有所需的输入形状 (1, 2)输出是标量

它利用AdamOptimizerGradientDescentOptimizer 更快地减少错误很多。有关优化器的更多信息(和问题^^),请参阅this post。

事实上,我的网络只需要 400-800 个学习步骤就可以产生相当好的结果。

经过 2000 个学习步骤后,输出几乎“完美”:

step: 2000
loss: 0.00103311243281

input: [0.0, 0.0] | output: [[ 0.00019799]]
input: [0.0, 1.0] | output: [[ 0.99979786]]
input: [1.0, 0.0] | output: [[ 0.99996307]]
input: [1.0, 1.0] | output: [[ 0.00033751]]

import tensorflow as tf    

#####################
# preparation stuff #
#####################

# define input and output data
input_data = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]  # XOR input
output_data = [[0.], [1.], [1.], [0.]]  # XOR output

# create a placeholder for the input
# None indicates a variable batch size for the input
# one input's dimension is [1, 2] and output's [1, 1]
n_input = tf.placeholder(tf.float32, shape=[None, 2], name="n_input")
n_output = tf.placeholder(tf.float32, shape=[None, 1], name="n_output")

# number of neurons in the hidden layer
hidden_nodes = 5


################
# hidden layer #
################

# hidden layer's bias neuron
b_hidden = tf.Variable(tf.random_normal([hidden_nodes]), name="hidden_bias")

# hidden layer's weight matrix initialized with a uniform distribution
W_hidden = tf.Variable(tf.random_normal([2, hidden_nodes]), name="hidden_weights")

# calc hidden layer's activation
hidden = tf.sigmoid(tf.matmul(n_input, W_hidden) + b_hidden)


################
# output layer #
################

W_output = tf.Variable(tf.random_normal([hidden_nodes, 1]), name="output_weights")  # output layer's weight matrix
output = tf.sigmoid(tf.matmul(hidden, W_output))  # calc output layer's activation


############
# learning #
############
cross_entropy = -(n_output * tf.log(output) + (1 - n_output) * tf.log(1 - output))
# cross_entropy = tf.square(n_output - output)  # simpler, but also works

loss = tf.reduce_mean(cross_entropy)  # mean the cross_entropy
optimizer = tf.train.AdamOptimizer(0.01)  # take a gradient descent for optimizing with a "stepsize" of 0.1
train = optimizer.minimize(loss)  # let the optimizer train


####################
# initialize graph #
####################
init = tf.initialize_all_variables()

sess = tf.Session()  # create the session and therefore the graph
sess.run(init)  # initialize all variables  

#####################
# train the network #
#####################
for epoch in xrange(0, 2001):
    # run the training operation
    cvalues = sess.run([train, loss, W_hidden, b_hidden, W_output],
                       feed_dict=n_input: input_data, n_output: output_data)

    # print some debug stuff
    if epoch % 200 == 0:
        print("")
        print("step: :>3".format(epoch))
        print("loss: ".format(cvalues[1]))
        # print("b_hidden: ".format(cvalues[3]))
        # print("W_hidden: ".format(cvalues[2]))
        # print("W_output: ".format(cvalues[4]))


print("")
print("input:  | output: ".format(input_data[0], sess.run(output, feed_dict=n_input: [input_data[0]])))
print("input:  | output: ".format(input_data[1], sess.run(output, feed_dict=n_input: [input_data[1]])))
print("input:  | output: ".format(input_data[2], sess.run(output, feed_dict=n_input: [input_data[2]])))
print("input:  | output: ".format(input_data[3], sess.run(output, feed_dict=n_input: [input_data[3]])))

【讨论】:

【参考方案2】:

我无法发表评论,因为我没有足够的声誉,但我对这个答案有一些疑问,mrry。 $L_2$ 损失函数是有意义的,因为它基本上是 MSE 函数,但为什么交叉熵不起作用呢?当然适用于其他 NN 库。其次,为什么在世界上将您的输入空间从 $[0,1] -> [-1,1]$ 转换会有任何影响尤其是,因为您添加了偏置向量。

编辑这是一个使用交叉熵和从多个来源编译的解决方案 EDIT^2 将代码更改为使用交叉熵,没有任何额外的编码或任何奇怪的目标值移动

import math
import tensorflow as tf
import numpy as np

HIDDEN_NODES = 10

x = tf.placeholder(tf.float32, [None, 2])
W_hidden = tf.Variable(tf.truncated_normal([2, HIDDEN_NODES]))
b_hidden = tf.Variable(tf.zeros([HIDDEN_NODES]))
hidden = tf.nn.relu(tf.matmul(x, W_hidden) + b_hidden)

W_logits = tf.Variable(tf.truncated_normal([HIDDEN_NODES, 1]))
b_logits = tf.Variable(tf.zeros([1]))
logits = tf.add(tf.matmul(hidden, W_logits),b_logits)


y = tf.nn.sigmoid(logits)


y_input = tf.placeholder(tf.float32, [None, 1])



loss = -(y_input * tf.log(y) + (1 - y_input) * tf.log(1 - y))

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss)

init_op = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init_op)

xTrain = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])


yTrain = np.array([[0], [1], [1], [0]])


for i in xrange(2000):
  _, loss_val,logitsval = sess.run([train_op, loss,logits], feed_dict=x: xTrain, y_input: yTrain)

  if i % 10 == 0:
    print "Step:", i, "Current loss:", loss_val,"logits",logitsval

print "---------"
print sess.run(y,feed_dict=x: xTrain)

【讨论】:

使用交叉熵来解决 XOR 作为分类问题当然是可能的(我回答了之前的问题:***.com/questions/33747596/…)。该问题被提出为回归问题,MSE 更适合该问题。我不完全确定为什么需要重新调整输入数据,但也许它陷入了局部最小值? 好吧,但是 XOR 误差面是否包括局部最小值?还是只有一个本地最小值,即全局最小值 另外:为什么没有 one-hot 就不行!如果您可能将目标设为 1 维并更改相应的权重矩阵,则它不起作用——炸毁到 NaN ——我不确定整个 tensorflow 似乎 theano 可能更适合 NN 我刚刚编辑了我的答案,指出重新缩放并不是绝对必要的(在给定的学习率下收敛需要更长的时间)。不过,不确定您所说的需要单热是什么意思:我的回答没有使用它。也许您可以将您的问题作为一个单独的问题提出? 当然,为什么 yTrain = np.array([[1, 0], [0, 1], [0, 1], [1, 0]]) 需要是二维的所需的输出是 1-d [0,1,1,0]?

以上是关于TensorFlow MLP 不训练 XOR的主要内容,如果未能解决你的问题,请参考以下文章

MLP初始化Keras中的LSTM细胞状态

如何用tensorflow实现MLP

小白学习keras教程一基于波士顿住房数据集训练简单的MLP回归模型

TensorFlow篇--Tensorflow框架可视化之Tensorboard

4.2tensorflow多层感知器MLP识别手写数字最易懂实例代码

在 Theano 中训练 MLP