神经网络分为三层:输入层、隐含层、输出层。当引入非线性的隐含层后,理论上只要隐含的节点足够多,就可以拟合任意函数,同时,隐含层越多,越容易拟合更复杂的函数。隐含层的两个属性:每个隐含层的节点数、隐含层的层数。层数越多,每一层需要的节点数就会越少。
本文源码的GitHub地址,位于multi_layer_perception
文件夹。
神经网络问题
神经网络的三大问题:过拟合、局部最优、梯度弥散。
过拟合问题:模型拟合训练数据,泛化性较差,测试数据效果不好。解决方案:Dropout,在神经网络的层中,随机丢弃一些点,并且加强剩余的点,学习含有噪声的数据,增强模型的泛化性。
import tensorflow as tf
in_mat = [2., 3., 2., 1., 4., 2., 3.]
keep_prop = 0.4
print tf.Session().run(tf.nn.dropout(in_mat, keep_prop))
"""
输出:[ 5. 7.5 0. 2.5 10. 0. 7.5]
keep_prop 随机保留40%的数据,并且将所有值增加2.5倍,2.5*0.4=1;
如果keep_prop是0.5,则所有值增加2倍;
"""
局部最优问题:神经网络不是一个凸优化问题,存在大量的局部最优点,然而,神经网络的局部最优正好可以达到比较好的效果,全局最优反而容易拟合。解决方案:使用自适应的Adagrad优化器代替SGD,减少计算参数的复杂度,学习速率先快后慢,恰好达到局部最优。
梯度弥散问题:神经网络的的激活函数,将信息上一层传递至下一层的变换,使模型学习到更加高阶的特征。Sigmoid函数,在反向传播中,梯度会急剧减少;Softplus是单侧抑制,即负值转换为正值,但没有稀疏激活性,即没有0状态。真正的神经元具有稀疏性,根据输入信号,选择响应或是屏蔽。解决方案:ReLU,校正线性单元:Rectified Linear Unit,y=max(0,x),将负值抑制为0,即非激活状态,正值反馈。
import tensorflow as tf
in_mat = [2, 3, 0, -1, -2]
relu = tf.nn.relu(in_mat)
print tf.Session().run(relu)
# 输出 [2 3 0 0 0]
# 将全部负数都转换为0
多层感知机
多层感知机,Multi-Layer Perception,也称为多层神经网络(大于等于3层,即至少含有1层隐含层),也称为全连接神经网络(Fully Connected Network,FCN)。
初始化数据源MNIST,手写数据源;创建可交互的Session,代码中可以直接使用张量的run()和eval()等函数;输入数据源的维数是784,28*28的手写灰度图片,隐含层的维度是300,即784(input) -> 300(hidden) -> 10(output)。
mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True) # Data Set
sess = tf.InteractiveSession() # Session
in_units = 784 # input neuron dimen
h1_units = 300 # hide level neuron dimen
初始化输入层的参数和隐含层的参数,W和b。激活函数是ReLU,需要给隐含层参数W1增加一些噪声,防止正态分布的完全对称,导致梯度为0;使用truncated_normal
将大于2倍标准差的数据,全部重新输出,相当于增加噪声。输出层的参数,可以都设置为0。
# hide level para
# truncated_normal 重新选择超过两个标准差的值
# 矩阵用大写字母,向量用小写字母
W1 = tf.Variable(tf.truncated_normal([in_units, h1_units], stddev=0.1))
b1 = tf.Variable(tf.zeros([h1_units]))
# output para
W2 = tf.Variable(tf.zeros([h1_units, 10]))
b2 = tf.Variable(tf.zeros([10]))
两个需要feed的数据,图片信息,dropout的保留率。保留率在训练中是小于1,随机舍弃;在测试中,是等于1,全保留。
x = tf.placeholder(tf.float32, [None, in_units]) # 任意个in_units维的数
keep_prob = tf.placeholder(tf.float32) # dropout的保留比率
创建多层感知机,使用线性回归y=wx+b
。创建隐含层,激活函数是ReLU,并且在隐含层中使用Dropout()函数,将某些神经元设置为0(抑制)。输出层使用softmax()的激活函数。
hidden1 = tf.nn.relu(tf.matmul(x, W1) + b1) # 隐含层,校正线性单元:Rectified Linear Unit
hidden1_drop = tf.nn.dropout(hidden1, keep_prob) # 隐含层的dropout
y = tf.nn.softmax(tf.matmul(hidden1, W2) + b2) # 输出层
需要feed的Ground Truth标签y_,真实数据使用下划线“_”标记,损失函数使用标准的交叉熵,优化器使用AdagradOptimizer,优化交叉熵函数cross_entropy。最后初始化,全局变量。
y_ = tf.placeholder(tf.float32, [None, 10]) # ground truth
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
train_step = tf.train.AdagradOptimizer(0.3).minimize(cross_entropy)
tf.global_variables_initializer().run() # 初始化全部变量
训练模型,循环3000次,每次随机采样100个数据,feed三类数据:x数据,y_
标签,keep_prob
是dropout的保留率。
for i in range(3000):
batch_xs, batch_ys = mnist.train.next_batch(100) # 随机采样
train_step.run({x: batch_xs, y_: batch_ys, keep_prob: 0.75}) # Feed数据,并且训练
使用测试集,验证模型效果,argmax选择最大数的索引,就是识别的数字,全部数据求平均。测试集的feed数据,同样的是数据与标准,注意keep_prob设置为1,不丢弃任何信息。
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print (accuracy.eval({x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})) # 评估数据
"""
输出结果:0.9811
"""
OK, that's all! Enjoy it!