我正在尝试使用TensorFlow(Python 3版本)实现一个简单的全连接前馈神经网络。该网络有2个输入和1个输出,我试图训练它输出两个输入的异或(XOR)值。我的代码如下:
import numpy as npimport tensorflow as tfsess = tf.InteractiveSession()inputs = tf.placeholder(tf.float32, shape = [None, 2])desired_outputs = tf.placeholder(tf.float32, shape = [None, 1])weights_1 = tf.Variable(tf.zeros([2, 3]))biases_1 = tf.Variable(tf.zeros([1, 3]))layer_1_outputs = tf.nn.sigmoid(tf.matmul(inputs, weights_1) + biases_1)weights_2 = tf.Variable(tf.zeros([3, 1]))biases_2 = tf.Variable(tf.zeros([1, 1]))layer_2_outputs = tf.nn.sigmoid(tf.matmul(layer_1_outputs, weights_2) + biases_2)error_function = -tf.reduce_sum(desired_outputs * tf.log(layer_2_outputs))train_step = tf.train.GradientDescentOptimizer(0.05).minimize(error_function)sess.run(tf.initialize_all_variables())training_inputs = [[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]training_outputs = [[0.0], [1.0], [1.0], [0.0]]for i in range(10000): train_step.run(feed_dict = {inputs: np.array(training_inputs), desired_outputs: np.array(training_outputs)})print(sess.run(layer_2_outputs, feed_dict = {inputs: np.array([[0.0, 0.0]])}))print(sess.run(layer_2_outputs, feed_dict = {inputs: np.array([[0.0, 1.0]])}))print(sess.run(layer_2_outputs, feed_dict = {inputs: np.array([[1.0, 0.0]])}))print(sess.run(layer_2_outputs, feed_dict = {inputs: np.array([[1.0, 1.0]])}))
看起来很简单,但最后的打印语句显示神经网络的输出与期望的输出相差甚远,无论训练迭代次数或学习率如何。有人能看出我做错了什么吗?
谢谢。
编辑:我还尝试了以下另一种错误函数:
error_function = 0.5 * tf.reduce_sum(tf.sub(layer_2_outputs, desired_outputs) * tf.sub(layer_2_outputs, desired_outputs))
该错误函数是误差平方的总和。它总是导致网络输出精确的0.5值——这是代码中存在错误的另一个迹象。
编辑2:我发现我的代码对AND和OR操作正常,但对XOR操作不行。我现在非常困惑。
回答:
你的代码中存在几个问题。以下我将逐行注释,带你找到解决方案。
注意:XOR不是线性可分的。你需要超过1个隐藏层。
注意:以# [!]
开头的行是你出错的地方。
import numpy as npimport tensorflow as tfsess = tf.InteractiveSession()# 一批每个包含2个值的输入inputs = tf.placeholder(tf.float32, shape=[None, 2])# 一批每个包含1个值的输出desired_outputs = tf.placeholder(tf.float32, shape=[None, 1])# [!] 定义第一层的隐藏单元数量HIDDEN_UNITS = 4 # 将2个输入连接到3个隐藏单元# [!] 使用随机数初始化权重,使网络能够学习weights_1 = tf.Variable(tf.truncated_normal([2, HIDDEN_UNITS]))# [!] 每个隐藏单元的偏置是一个单值biases_1 = tf.Variable(tf.zeros([HIDDEN_UNITS]))# 将2个输入连接到每个隐藏单元。添加偏置layer_1_outputs = tf.nn.sigmoid(tf.matmul(inputs, weights_1) + biases_1)# [!] XOR的问题在于函数不是线性可分的# [!] 多层感知器(MLP)可以学习分离非线性可分的点(你可以# 认为它将学习超曲线,而不仅仅是超平面)# [!] 让我们添加一个新层,并将第二层改为输出超过1个值# 将第一层的隐藏单元连接到第二隐藏层的2个隐藏单元weights_2 = tf.Variable(tf.truncated_normal([HIDDEN_UNITS, 2]))# [!] 同上biases_2 = tf.Variable(tf.zeros([2]))# 将隐藏单元连接到第二隐藏层layer_2_outputs = tf.nn.sigmoid( tf.matmul(layer_1_outputs, weights_2) + biases_2)# [!] 创建新层weights_3 = tf.Variable(tf.truncated_normal([2, 1]))biases_3 = tf.Variable(tf.zeros([1]))logits = tf.nn.sigmoid(tf.matmul(layer_2_outputs, weights_3) + biases_3)# [!] 所选择的错误函数适用于多类分类任务,不适用于XOR.error_function = 0.5 * tf.reduce_sum(tf.sub(logits, desired_outputs) * tf.sub(logits, desired_outputs))train_step = tf.train.GradientDescentOptimizer(0.05).minimize(error_function)sess.run(tf.initialize_all_variables())training_inputs = [[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]training_outputs = [[0.0], [1.0], [1.0], [0.0]]for i in range(20000): _, loss = sess.run([train_step, error_function], feed_dict={inputs: np.array(training_inputs), desired_outputs: np.array(training_outputs)}) print(loss)print(sess.run(logits, feed_dict={inputs: np.array([[0.0, 0.0]])}))print(sess.run(logits, feed_dict={inputs: np.array([[0.0, 1.0]])}))print(sess.run(logits, feed_dict={inputs: np.array([[1.0, 0.0]])}))print(sess.run(logits, feed_dict={inputs: np.array([[1.0, 1.0]])}))
我增加了训练迭代次数,以确保无论随机初始化值如何,网络都能收敛。
经过20000次训练迭代后的输出是:
[[ 0.01759939]][[ 0.97418505]][[ 0.97734243]][[ 0.0310041]]
看起来相当不错。