TensorFlow实现神经网络
分类问题解决思路
使用神经网络解决分类问题主要可以分为以下四个步骤:
1. 提取问题中实体的特征向量作为神经网络的输入。
2. 定义神经网络的结构,并定义如何从神经网络的输入得到输出。即前向传播算法。
3. 通过训练数据来调整神经网络中参数的取值,即训练神经网络。
4. 使用训练好的神经网络来预测未知的数据。
前向传播算法的实现
x为输入数据,w1为从输入层到隐藏层的参数矩阵,w2为从隐藏层到输出层的参数矩阵,则前向传播过程代码为
a = tf.matmul(x, w1)
b = tf.matmul(a, w2)
其中tf.matmul实现了矩阵乘法的功能。
TensorFlow变量
在TensorFlow中,变量(tf.Variable)的作用就是保存和更新神经网络中的参数。
声明一个2*3的矩阵变量的方法
weights = tf.Variable(tf.random_normal([2,3], stddev=2))
TensorFlow中变量的初始值可以设置成随机数、常数或者是通过其他变量的初始值计算得到。


对于变量,初始化是非常重要的。虽然在变量定义的时候给出了变量初始化的方法,但是这个方法并没有被真正运行,需要通过variable_name.initializer来给变量赋值。
TensorFlow提供了一种更加便捷的方式来完成变量初始化过程。
init_op = tf.initialize_all_variables()
sess.run(init_op)
通过tf.initialize_all_variables函数就不需要将变量一个一个初始化了。
集合的概念
TensorFlow中有集合的概念。比如,所有的变量都会被自动的加入到GraphKeys.VARIABLES
这个集合。通过tf.all_variables
函数可以拿到当前计算图上所有的变量。
在构建机器学习模型的时候,比如神经网络,可以通过变量声明函数中的trainable参数来区分需要优化的参数(比如神经网络中通过反向传播优化的参数)和其他参数(比如迭代的轮数)。如果trainable参数为True,那么这个变量将会加入GraphKeys.TRAINABLE_VARIABLES
集合。TensorFlow中可以通过tf.trainable_variables
函数得到所有需要优化的参数。TensorFlow中提供的神经网络优化算法会将GraphKeys.TRAINABLE_VARIABLES
中的变量作为默认的优化对象。
变量的类型是不可变的,但和类型不一样的是,维度在程序运行中是有可能改变的,但是需要通过设置参数validate_shape=False
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name="w1")
w2 = tf.Variable(tf.random_normal([2, 2], stddev=1), name="w2")
tf.assign(w1, w2)#不成功,报维度不匹配错误
tf.assign(w1, w2, validate_shape=False)#成功执行并且改变了w1的维度
通过TensorFlow训练神经网络模型

反向传播算法的第一步是使用TensorFlow表达一个batch的数据
x = tf.constant([[0.7,0.9]])
但如果每轮迭代中选取的数据都通过常量表示,那么计算图太大。TensorFlow提供了placeholder机制用于提供输入数据。
placeholder相当于定义了一个位置,这个位置中的数据在程序运行时再指定。
这样在程序中就不需要生成大量常量来提供输入数据,而只需要将数据通过placeholder传入TensorFlow计算图。
placeholder的类型也不可以改变。在实践中,placeholder可以用来保存输入层的训练集中的不同样例。
import tensorflow as tf
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1))
#定义placeholder存放输入数据,维度不一定要定义可以用None表示,也可以用确定的维度
x = tf.placeholder(tf.float32, shape=(1,2), name="input")
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = tf.Session()
init_op = tf.initialize_all_variables()
sess.run(init_op)
print(sess.run(y, feed_dict={x: [[0.7, 0.9]]}))#只计算一个样例的前向传播结果
#feed_dict参数用字典的方式对placeholder的参数进行赋值,这样可以在每次运行时赋予不同的值。
在新的程序中计算前向传播的结果时,需要提供一个feed_dict来指定x的取值。feed_dict是一个字典,在字典中需要给出每个用到的placeholder的取值。
损失函数
在得到一个batch的前向传播结果之后,需要定义一个损失函数来刻画当前的预测值和真实答案之间的差距。然后通过反向传播算法来调整神经网络参数的取值使得差距可以被缩小。
#用损失函数刻画预测值与真实值的差距
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))
#定义学习率
learning_rate = 0.001
#定义反向传播算法来优化神经网络中的参数
train_step = tf.train.AdamOptimizer(learing_rate).minimize(cross_entropy)
cross_entropy定义了真实值与预测值之间的交叉熵,这是分类问题中一个常用的损失函数。关于该值的详细解释参见下一节。
完整的神经网络样例程序
下列程序在一个模拟数据集上训练神经网络,解决二分类问题。
import tensorflow as tf
from numpy.random import RandomState
batch_size = 8
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
x = tf.placeholder(tf.float32, shape=(None, 2), name='x-input')
y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
#随机数生成模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
#定义规则给出样本标签。所有x1+x2<1的样例都被认为是正样本,而其他为负样本。
Y = [[int(x1+x2<1)] for (x1,x2) in X]
with tf.Session() as sess:
init_op = tf.initialize_all_variables()
sess.run(init_op)
print sess.run(w1)
print sess.run(w2)#训练之前神经网络参数的值打印出来
STEPS = 5000
for i in range(STEPS):
start = (i * batch_size) % dataset_size
end = min(start+batch_size, dataset_size)
sess.run(train_step, feed_dict = {x: X[start:end], y_: Y[start:end]}}
if i % 1000 == 0:
total_cross_entropy = sess.run(cross_entropy, feed_dict={x: X, y_: Y})
print("After %d training step(s), cross entropy on all data is %g" % (i, total_cross_entropy))
print sess.run(w1)
print sess.run(w2)
总结,神经网络训练的过程可以分为以下三个步骤:
1. 定义神经网络的结构和前向传播的输出结果
2. 定义损失函数以及选择反向传播优化的算法
3. 生成会话(tf.Session)并且在训练数据上反复运行反向传播优化算法