神经网络中提到的线性模型与非线性模型之间的区别就体现在字面意思上:线性模型即仅通过对输入进行线性变换得到输出的网络模型,我们知道在线性代数中矩阵的相乘就是一种线性变换,根据矩阵乘法的结合律可知:一个矩阵依次与多个矩阵相乘等价于这个矩阵乘上这多个矩阵的乘积;类似的,任意线性模型的组合还是线性模型,也就是说,只通过线性变换,任意层的全连接神经网络和单层神经网络的表达能力没有区别。
由于线性模型的表达能力的局限性,非线性模型的优势就凸显出来,通过在线性模型的基础上增加激活函数的机制来实现,具体过程为在每一个神经元在接收上一层的加权和和偏执值之后,在该基础上计算一个非线性函数的函数值作为该神经元的输出,这个非线性函数就是激活函数。
最基础常见的激活函数就有sigmoid、relu、tanh等。那么激活函数到底给神经网络的性能带来了多大的提升呢?
为了验证神经网络非线性模型解决复杂问题的能力的确比线性模型要强,我们可以用Mnist数据集作为实验数据集。Mnist手写体图像数据集拥有6万张训练图像和1万张测试图像,每张图像大小为28*28个像素大小,每张图像都属于阿拉伯数字0~9的十个类别中的某一个类别。因为每张图像总共有28*28=784个像素,对于全连接神经网络模型,我们设置为一个784维的向量作为模型的输入。
这里我们运用最基本的实验思想:单一变量法。也就是说通过保证网络结构相同、训练数据、测试数据、训练参数相同,我们构建一个有激活函数的网络和一个没有激活函数的网络。网络结构如下:
输入层784个节点,每个节点直接输入图片对应像素点的像素值;添加了一个隐藏层,隐藏层含有100个节点;输入层10个节点,输出层结果代表着Mnist手写体图像的分类结果,因为数据集中包含阿拉伯数字0~9总共10类图片;层与层之间都是通过全连接的方式连接;线性模型与非线性模型的区别在于:线性模型每一层直接通过加权和方式输出,非线性模型在加权的基础上还要计算计算激活函数的值作为输出。
其实是否设置激活函数在tensorflow框架下的代码就体现的非常直观,如果y是网络某一层神经元的输出,x为输入,w为权值,b为偏置值,激活函数以sigmoid函数为例,那么线性模型的代码为:
y=tf.matmul(x,w)+b
非线性模型的代码为:
y=tf.sigmoid(tf.matmul(x,w)+b)
为了节省篇幅,在我的训练代码中,使用线性模型时把非线性模型的代码注释,反之亦然:
##线性模型,没有激活函数
#y1=tf.matmul(x,w1)+b1#输入层的输出
#y=tf.matmul(y1,w2)+b2#隐藏层的输出
#非线性模型
y1=tf.sigmoid(tf.matmul(x,w1)+b1)#输入层的输出
y=tf.sigmoid(tf.matmul(y1,w2)+b2)#隐藏层的输出
训练测试程序的完整代码:
# -*- coding:utf-8 -*-
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
#导入Mnist数据集
mnist=input_data.read_data_sets("MNIST_data",one_hot=True)
#数据集常数,输入层为28*28=784维的向量
INPUT_NODE=784
OUTPUT_NODE=10
#构建网络,x为网络输入,y_为训练数据的入口
x=tf.placeholder(tf.float32,shape=(None,INPUT_NODE))#batch_size不固定
y_=tf.placeholder(tf.float32,shape=(None,OUTPUT_NODE))#batch_size不固定
#输入层的权重w1和偏置b1
w1=tf.Variable(tf.random_normal([784,100],stddev=1))
b1=tf.Variable(tf.constant(0.1,shape=[100]))
#隐藏层的节点数量为100
#隐藏层的权重w2和偏置b2
w2=tf.Variable(tf.random_normal([100,10],stddev=1))
b2=tf.Variable(tf.constant(0.1,shape=[10]))
##线性模型,没有激活函数
#y1=tf.matmul(x,w1)+b1#输入层的输出
#y=tf.matmul(y1,w2)+b2#隐藏层的输出
#非线性模型
y1=tf.sigmoid(tf.matmul(x,w1)+b1)#输入层的输出
y=tf.sigmoid(tf.matmul(y1,w2)+b2)#隐藏层的输出
#设置正则化比重为0.0001
get_regularization= tf.contrib.layers.l2_regularizer(0.0001)
regularization=get_regularization(w1)+get_regularization(w2)
#使用交叉熵加softmax作为损失函数
loss=tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=tf.argmax(y_,1))+regularization
#通过梯度下降算法优化器反向传播训练参数,参数学习率为0.001
train_step=tf.train.GradientDescentOptimizer(0.001).minimize(loss)
#correct_prediction为bool变量0或1表示是否预测正确
correct_prediction=tf.equal(tf.argmax(y,1),tf.argmax(y_,1))
#accuracy计算得出当前模型在验证数据集上的准确率
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())#对所有的变量进行初始化
#设置验证集
validate_feed={x:mnist.validation.images,y_:mnist.validation.labels}
test_feed={x:mnist.test.images,y_:mnist.test.labels}
STEPS=200000 #总训练轮数
BATCH_SIZE=100#每批训练数量batch_size
#开始训练
for i in range(STEPS):
if(i%1000==0):
vali_acc=sess.run(accuracy,feed_dict=validate_feed)#当前模型的准确率
print('After %d steps accuracy is %g'%(i,vali_acc))
#print(sess.run(w1))
xs,ys=mnist.train.next_batch(BATCH_SIZE)#获取下一个batch的样本
sess.run(train_step,feed_dict={x:xs,y_:ys})#训练优化
test_acc=sess.run(accuracy,feed_dict=test_feed)#最终测试集上的准确率
print("Finally accuracy is %g"%(test_acc))
分别用线性模型与非线性模型完成训练:
线性模型:
非线性模型:
对于Mnist数据集,0.001的学习率,200000轮的训练可以说已经比较充分了,最后的结果非线性模型比线性模型高出了2个多的百分点,看似好像差地不远,但是要知道在92%准确率已经比较高的情况下,提升一个百分点对于模型而言已经是非常大的提升了,更何况这个实验中只使用了一个隐藏层,对于更复杂的问题,我们当然需要更深层次的网络来保证模型的效果,线性模型对于大多数比较复杂的问题很可能完全达不到我们要的效果,这个时候激活函数的作用就不言而喻了。