最近我开始尝试玩弄神经网络。我试图用Tensorflow实现一个AND
门。我在理解何时使用不同的成本和激活函数上遇到了麻烦。这是一个只有输入和输出层的基本神经网络,没有隐藏层。
首先我尝试以这种方式实现它。正如你所见,这是一个糟糕的实现,但我认为它至少在某种程度上完成了任务。所以,我只尝试了真实输出,没有使用独热编码的真实输出。对于激活函数,我使用了Sigmoid函数,对于成本函数,我使用了平方误差成本函数(我想是这么叫的,如果我错了请纠正我)。
我尝试使用ReLU和Softmax作为激活函数(使用相同的成本函数),但它不起作用。我弄清楚了它们为什么不起作用。我还尝试了Sigmoid函数与交叉熵成本函数的组合,它也不起作用。
import tensorflow as tfimport numpytrain_X = numpy.asarray([[0,0],[0,1],[1,0],[1,1]])train_Y = numpy.asarray([[0],[0],[0],[1]])x = tf.placeholder("float",[None, 2])y = tf.placeholder("float",[None, 1])W = tf.Variable(tf.zeros([2, 1]))b = tf.Variable(tf.zeros([1, 1]))activation = tf.nn.sigmoid(tf.matmul(x, W)+b)cost = tf.reduce_sum(tf.square(activation - y))/4optimizer = tf.train.GradientDescentOptimizer(.1).minimize(cost)init = tf.initialize_all_variables()with tf.Session() as sess: sess.run(init) for i in range(5000): train_data = sess.run(optimizer, feed_dict={x: train_X, y: train_Y}) result = sess.run(activation, feed_dict={x:train_X}) print(result)
经过5000次迭代后:
[[ 0.0031316 ][ 0.12012422][ 0.12012422][ 0.85576665]]
问题1 – 是否有其他激活函数和成本函数,可以在不改变参数(意味着不改变W, x, b)的前提下,对上述网络进行学习?
问题2 – 我从StackOverflow的一个帖子中读到这里:
[激活函数]的选择取决于问题本身。
所以,并不是所有的成本函数都可以随处使用?我的意思是,没有一种标准的成本函数可以用于任何神经网络。对吗?请纠正我这一点。
我还用另一种方法实现了AND
门,输出为独热编码的真实值。正如你所见,train_Y
中的[1,0]
表示第0个索引为1,所以答案是0。我希望你能理解。
在这里,我使用了Softmax激活函数,并以交叉熵作为成本函数。使用Sigmoid函数作为激活函数的效果非常差。
import tensorflow as tfimport numpytrain_X = numpy.asarray([[0,0],[0,1],[1,0],[1,1]])train_Y = numpy.asarray([[1,0],[1,0],[1,0],[0,1]])x = tf.placeholder("float",[None, 2])y = tf.placeholder("float",[None, 2])W = tf.Variable(tf.zeros([2, 2]))b = tf.Variable(tf.zeros([2]))activation = tf.nn.softmax(tf.matmul(x, W)+b)cost = -tf.reduce_sum(y*tf.log(activation))optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(cost)init = tf.initialize_all_variables()with tf.Session() as sess: sess.run(init) for i in range(5000): train_data = sess.run(optimizer, feed_dict={x: train_X, y: train_Y}) result = sess.run(activation, feed_dict={x:train_X}) print(result)
经过5000次迭代后
[[ 1.00000000e+00 1.41971401e-09] [ 9.98996437e-01 1.00352429e-03] [ 9.98996437e-01 1.00352429e-03] [ 1.40495342e-03 9.98595059e-01]]
问题3 那么在这种情况下,我可以使用什么成本函数和激活函数?我如何理解应该使用哪种类型的成本和激活函数?是否有标准的方式或规则,还是仅凭经验?是否必须以蛮力方式尝试每一种成本和激活函数?我在这里找到了一个答案。但我希望有更详细的解释。
问题4 我注意到需要很多次迭代才能收敛到近乎准确的预测。我认为收敛速度取决于学习率(使用过大的学习率会错过解决方案)和成本函数(如果我错了请纠正我)。那么,是否有最优的方式(意味着最快)或成本函数来收敛到正确的解决方案?
回答:
我将稍微打乱顺序来回答你的问题,从更一般的答案开始,最后回答与你特定实验相关的问题。
激活函数 不同的激活函数确实具有不同的特性。首先让我们考虑神经网络两层之间的激活函数。激活函数在那里的唯一目的是作为非线性。如果你在两层之间不放置激活函数,那么这两层加在一起的效果将不会比一层更好,因为它们的效果仍然只是一个线性变换。很长一段时间以来,人们一直在使用Sigmoid函数和Tanh函数,选择基本上是任意的,Sigmoid函数更为流行,直到最近ReLU成为主导的非线性。人们在层之间使用ReLU的原因是因为它是非饱和的(而且计算速度也更快)。想想Sigmoid函数的图形。如果x
的绝对值很大,那么Sigmoid函数的导数就很小,这意味着当我们向后传播误差时,误差的梯度会随着我们穿过层而很快消失。使用ReLU时,对于所有正输入,导数是1
,所以那些激活的神经元的梯度不会被激活单元改变,也不会减缓梯度下降的速度。
对于网络的最后一层,激活单元也取决于任务。对于回归,你会想要使用Sigmoid或Tanh激活,因为你希望结果在0和1之间。对于分类,你希望只有一个输出为1,其余都为0,但没有可微分的方式能精确实现这一点,所以你会想要使用Softmax来近似它。
你的例子。现在让我们看看你的例子。你的第一个例子试图以以下形式计算AND
的输出:
sigmoid(W1 * x1 + W2 * x2 + B)
请注意,W1
和W2
总是会收敛到相同的值,因为对于(x1
, x2
)的输出应该等于(x2
, x1
)的输出。因此,你正在拟合的模型是:
sigmoid(W * (x1 + x2) + B)
x1 + x2
只能取三个值中的一个(0, 1或2),你希望在x1 + x2 < 2
的情况下返回0
,在x1 + x2 = 2
的情况下返回1。由于Sigmoid函数相当平滑,需要非常大的W
和B
值才能使输出接近期望值,但由于学习率较小,它们无法快速达到那些大的值。在你的第一个例子中增加学习率将提高收敛速度。
你的第二个例子收敛得更好,因为softmax
函数擅长使一个输出精确等于1
,其余输出为0
。由于这正是你的情况,它确实收敛得很快。请注意,sigmoid
最终也会收敛到好的值,但需要显著更多的迭代(或更高的学习率)。
使用什么。现在回答最后一个问题,如何选择使用哪种激活和成本函数。这些建议适用于大多数情况:
-
如果进行分类,使用
softmax
作为最后一层的非线性,使用cross entropy
作为成本函数。 -
如果进行回归,使用
sigmoid
或tanh
作为最后一层的非线性,使用squared error
作为成本函数。 -
在层之间使用ReLU作为非线性。
-
使用更好的优化器(如
AdamOptimizer
,AdagradOptimizer
)代替GradientDescentOptimizer
,或使用动量来加快收敛速度,