设置神经网络参数的过程就是神经网络的训练过程。只有经过有效训练的神经网络模型才可以真正地解决分类或者回归问题使用监督学习的方式设置神经网络参数需要有一个标注好的训练数据集。监督学习最重要的思想是,在一直答案的标注数据集上,模拟给出预测结果要尽量逼近真实的答案。通过调整神经网络中地参数对训练数据进行拟合,可以使得模块对未知的样本提供预测的能力在神经网络优化算法中,最常用的方法是反向传播算法(backpropagation)。反向传播算法的具体工作原理如下图
反向传播算法实现了一个迭代过程。在每次迭代的开始,首先需要选取一部分训练数据,这一小部分数据叫做一个batch。然后,这个batch的样例通过前向传播算法得到神经网络模型的预测结果。因为训练数据都是有正确答案标注的,所以可以计算出当前神经网络模型的预测答案与真实答案之间的差距。最后,基于预测值和真实值之间的差距,反向传播算法会相应更新神经网络参数的取值,使得在这个batch上的神经网络模型的预测结果和真实结果更加接近。
通过tensorflow实现反向传播算法的第一步是使用tensorflow表达一个batch的数据。例如使用常量来表达过一个样例:
代码语言:javascript复制x = tf.constant([0.7, 0.9])
但如果每轮迭代中选取的数据都要通过常量来表示,那么tensorflow的计算图将会太大。因为每生成一个常量,tensorflow都会在计算图中增加一个节点。一般来说,一个神经网络的训练过程会需要几百万甚至几亿轮的迭代,这样计算图就会非常大,而且利用率很低。为了避免这个问题,tensorflow提供了placeholder机制用于提供输入数据。placeholder相当于定义了一个位置,这个位置中的数据在程序运行时再指定。这样在程序中就不需要生成大量常数来提供输入数据,而只需要将数据通过placeholder传入tensorflow计算图。在placeholder定义时,这个位置上的数据类型是需要指定的。和其他张量一样,placeholder的类型也是不可以改变的。placeholder中的数据的维度信息可以根据提供的数据推导得出,所以不一定要给出。下面给出了通过placeholder实现前向传播算法的代码。
代码语言:javascript复制import tensorflow as tf
w1 = tf.Variable(tf.random_normal([2, ,3], stddev = 1, seed = 1))
w2 = tf.Variable(tf.random_normal([3, ,1], stddev = 1, seed = 1))
# 定义placeholder作为存放数据的地方。这里维度也不一定要定义。
# 但如果维度是确定的,那么给出维度可以降低出错的概率
x = tf.placeholder(tf.float32, shape=(1,2), name = "input")
a = tf.matmul(x , w1)
y = tf.matmul(a, w2)
sess = tf.Session()
init_oop = tf.global_variable_initializer()
sess.run(init_op)
# 下面一行将报错: InvalidArgumentError: You must feed a value for placeholder
# tensor 'input_1' with dtype float and shape [1, 2]
print(sess.run(y))
print(sess.run(y , feed_dict = {x: {[[0.7, 0.9]]}))
在这段程序中替换了原来通过常量定义的输入x。在新的程序中计算前行传播结果时,需要提供一个feed_dict来指定x的取值。feed_dict是一个字典(map),在字典中需要给出每个用到的placeholder的取值。如果某个需要的placeholder没有被指定取值,那么程序在运行时将会报错。
在上面的样例程序中,如果将输入的1*2矩阵改为n*2的矩阵,那么就可以得到n个样例的前向传播结果了。其中n*2的矩阵的每一行为一个样例数据。这样前向传播的结果为n*1的矩阵,这个矩阵的每一行就代表了一个样例的前向传播结果。以下代码给出了一个示例。
代码语言:javascript复制x = tf.placeholder(tf.float32, shape=(3, 2, name = "inpput"))
... # 中间部分和上面的样例程序一样
# 因为x在定义是制定了n为3,所以在运行前向传播过程时需要提供3个样例数据
print(sess.run(y, feed_dict={x: [[0.7, 0.9],[0.1, 0.4],[0.5, 0.8]]}))
'''
输出结果为:
[[3.95757794]
[1.15376544]
[3.16749191]]
以上样例展示了一次性计算多个样例的前向传播结果。在运行是,需要将三个样例[0.7, 0.9]、[0.1, 0.4]和[0.5, 0.8]组成一个3*2的矩阵传入placeholder。计算得到的结果为3*1的矩阵。其中第一行3.95757794为样例[0.7, 0.9]的前向传播结果:1.15376544为样例[0.1, 0.4]的前向传播结果;3.16749191为样例[0.5, 0.8]的前向传播结果。
在得到一个batch的前向传播结果以后,需要定义一个损失函数来刻画当前的预测值和真实答案之间的差距。然后通过反向传播算法来调整神经网络参数的取值是的差距可以被缩小。下面代码定义了一个简单的损失函数,并通过tensorflow定义了反向传播算法。
代码语言:javascript复制# 使用sigmoid函数将y转换为0~1之间的数值。转换后y代表预测是正样本的概率,1-y代表
# 预测是负样本的概率
y = tf.sigmoid(y)
# 定义损失函数来刻画预测值与真实值得差距
cross_entropy = -tf.reduce_mean(
y* tf.log(tf.clip_by_value(y, 1e-10, 1.0))
(1-y_)*tf.log(tf.clip_by_value(1-y, 1e-10, 1.0)))
# 定义学习率
learning_rate = 0.001
# 定义反向传播算法来优化神经网络中的参数
train_step =
tf.train.AdaOptimizer(learning_rate).minimize(cross_entropy)
在以上代码中,cross_entropy定义了真实值和预测值之间的交叉熵(cross_entropy),这是一个分类问题中一个常用的损失函数。第二行train_step定义了反向传播的优化方法。目前,tensorflow支持10种不同的优化器,常用的优化器有三种:tf.train.GradientDescentOptimizer、tf.train.AdamOotimizer和tf.train.MomentumOptimizer。在定义了反向传播算法之后,通过运行sess.run(train_step)就可以对所有在GraphKeys.TRAINBLE_VARIABLES集合中的变量进行优化,使得在当前batch下损失函数最小。