在本文中会先介绍一些概念,然后给出一个简单的完整神经网络样例程序。
激活函数:
首先激活函数可以解决线性不可分问题。读者可以访问https://playground.tensorflow.org/。通过网页浏览器就可以训练简单神经网络并实现可视化过程。截图如下所示:
对于这个网页怎么用,读者可以自行百度学习一下。上图有一个Activation,如果选择Linear则根本不能训练出很好的分类结果。目前TensorFlow提供了7种不同的非线性激活函数,tf.nn.relu、tf.sigmoid、tf.tanh是比较常用的几个。当然TensorFlow也支持自己定义的激活函数。
损失函数:
神经网络模型的效果以及优化的目标是通过损失函数(loss function)来定义的。
分类问题(如手写数字识别),如何判断一个输出向量和期望向量有多接近。交叉熵是常用的评判方法之一。交叉熵刻画了两个概率分布之间的距离,它是分类问题中使用比较广的一种损失函数。因为交叉熵一般会与softmax回归一起使用,所以TensorFlow对这两个功能进行了封装,并提供了tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y),其中y表示神经网络的输出结果,y_表示标准答案。
与分类问题不同,回归问题解决的是对具体数值的预测。对于回归问题,最常用的损失函数是均方误差(MSE mean squared error)。当然也可以使用自己定义的损失函数。下面通过一个简单的神经网络程序来讲解损失函数对模型训练结果的影响。这个程序有两个输入节点、一个输出节点,没有隐藏层。
import tensorflow as tf
from numpy.random import RandomState
batch_size = 8
#两个输入节点
x = tf.placeholder(tf.float32,shape=(None,2),name='x-input')
#回归问题一般只有一个输出节点
y_ = tf.placeholder(tf.float32,shape=(None,1),name='y-input')
#定义一个单层的神经网络前向传播的过程,这里就是简单的加权和
w1 = tf.Variable(tf.random_normal([2,1],stddev=1,seed=1))
y = tf.matmul(x,w1)
#定义预测多了和预测少了的成本
loss_less = 10
loss_more = 1
loss = tf.reduce_sum(tf.where(tf.greater(y,y_),
(y-y_)*loss_more,
(y_-y)*loss_less))
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
#通过随机数生成一个模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size,2)
Y = [[x1+x2+rdm.rand()/10.0-0.05] for (x1,x2) in X]
# 训练神经网络
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
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]})
print(sess.run(w1))
运行以上代码会得到w1的值为[1.019,1.042],也就是得到的预测函数1.02x1+1.04x2,这比x1+x2要大。但如果将loss_less值调整为1,less_more调整为10,那么最后的w1中的参数会略小于1,所以对于相同的神经网络,不同的损失函数会对训练得到的模型产生重要的影响。
训练神经网络的过程可以分为以下三个步骤:
- 定义神经网络的结构和前向传播的输出结果。
- 定义损失函数以及选择反向传播优化的算法。
- 生成会话(tf.Session)并且在训练数据上反复运行反向传播优化算法。
下面为在一个模拟数据集上进行的神经网络训练。
import tensorflow as tf
#通过numpy工具包生成模拟数据集
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)
#定义损失函数和反向传播算法
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)))
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
#通过随机数生成一个模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size,2)
Y = [[int(x1+x2<1)] for (x1,x2) in X]
#创建一个会话来运行TensorFlow程序
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
#初始化变量
sess.run(init_op)
print(sess.run(w1))
print(sess.run(w2))
#设定训练的轮数
STEPS = 10000
for i in range(STEPS):
#每次选取batch_size个样本进行训练
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))