目录算法
第4章 深层神经网络dom
对于许多机器学习问题来讲,特征提取不是一件简单的事情。在一些复杂问题上,要经过人工的方式设计有效的特征集合,须要不少的时间和精力,有时甚至须要整个领域数十年的研究投入。机器学习
深度学习解决的核心问题之一就是自动地将简单的特征组合成更加复杂的特征。并使用这些组合特征解决问题。深度学习是机器学习的一个分支,它除了能够学习特征和任务之间的关联,还能自动从简单特征中提取更加复杂的特征。编程语言
深度学习在不少领域都有很是出色的表现,例如在计算机视觉、语音识别、天然语言处理和人机博弈等。函数
TensorFlow主要依赖两个工具包——Protocol Buffer和Bazel。工具
Protocol Buffer是谷歌开发的处理结构化数据的工具,和XML或者JSON格式的数据有较大的区别。首先Protocol Buffer序列化以后获得的数据不是可读的字符串,而是二进制流。其次,XML或JSON格式的数据信息都包含在了序列化以后的数据中,不须要任何其余信息就能还原序列化以后的数据。但使用Protocol Buffer时须要先定义数据的格式,还原一个序列化以后的数据将须要使用到这个定义好的数据格式。由于这样的差异,Protocol Buffer序列化出来的数据要比XML格式的数据小3到10倍,解析时间快20到100倍。如下是Protocol Buffer格式的一个定义示例:学习
message user{ optional string name = 1; required int32 id = 2; repeated string email = 3; }
Bazel是从谷歌开源的自动化构建工具,谷歌内部绝大部分的应用都是经过它来编译的。相比传统的Makefile、Ant或者Maven,Bazel在速度、可伸缩性、灵活性以及对不一样程序语言和平台的支持上都要更加出色。TensorFlow自己以及谷歌给出的不少官方样例都是经过Bazel来编译的。
TensorFlow的名字中已经说明了它最重要的两个概念——Tensor和Flow。Tensor就是张量,能够被简单地理解为多维数组。Flow翻译成中文就是“流”,它直观地表达了张量之间经过计算相互转化的过程。TensorFlow是一个经过计算图的形式来表述计算的编程系统,每个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。计算图不只仅能够用来隔离张量和计算,它还提供了管理张量和计算的机制。
在TensorFlow程序中,全部的数据均可以经过张量的形式来表示。从功能的角度来看,张量能够被简单理解为多维数组。其中零阶张量表示标量(scalar),也就是一个数;第一阶张量为向量(vector),也就是一个一维数组;第n阶张量能够理解为一个n维数组。在张量中并无真正保存数字,它保存的是如何获得这些数字的计算过程。例如如下输出结果:
Tensor("add:0", shape=(2,), dtype=float32)
会话拥有并管理TensorFlow程序运行时的全部资源,全部计算完成以后须要关闭会话来帮助系统回收资源,不然就可能出现资源泄露问题。如下代码显示其中一个会话使用模式:
with tf.Session() as sess: print(sess.run(result))
TensorFlow游乐场使用连接:http://playground.tensorflow.org
在TensorFlow中,变量(tf.Variable)的做用就是保存和更新神经网络中的参数,和其余编程语言相似,TensorFlow中的变量也须要指定初始值。由于在神经网络中,给参数赋予随机初始值最为常见,因此通常也使用随机数给TensorFlow中的变量初始化。
如下代码给出了一个完整神经网络实现样例,包含一个隐藏层。
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) 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) X = rdm.rand(128,2) Y = [[int(x1+x2 < 1)] for (x1, x2) in X] with tf.Session() as sess: init_op = tf.global_variables_initializer() sess.run(init_op) # 输出目前(未经训练)的参数取值。 print(sess.run(w1)) print(sess.run(w2)) print("\n") # 训练模型。 STEPS = 5000 for i in range(STEPS): start = (i*batch_size) % 128 end = (i*batch_size) % 128 + batch_size sess.run([train_step, y, y_], 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("\n") print(sess.run(w1)) print(sess.run(w2))
输出结果:
[[-0.8113182 1.4845988 0.06532937] [-2.4427042 0.0992484 0.5912243 ]] [[-0.8113182 ] [ 1.4845988 ] [ 0.06532937]] After 0 training step(s), cross entropy on all data is 1.89805 After 1000 training step(s), cross entropy on all data is 0.655075 After 2000 training step(s), cross entropy on all data is 0.626172 After 3000 training step(s), cross entropy on all data is 0.615096 After 4000 training step(s), cross entropy on all data is 0.610309 [[ 0.02476983 0.56948674 1.6921941 ] [-2.1977348 -0.23668921 1.1143895 ]] [[-0.45544702] [ 0.49110925] [-0.98110336]]
以上程序实现了训练神经网络的所有过程。这段程序能够总结出训练神经网络的过程能够分为如下三个步骤:
(1)定义神经网络的结构和前向传播的输出结果
(2)定义损失函数以及选择反向传播优化的算法
(3)生成会话(tf.Session),而且在训练数据上反复运行反向传播优化算法
不管神经网络的结构如何变化吗,这三个步骤都是不变的。
维基百科对深度学习的精度定义为“一类经过多层非线性变换对高复杂性数据建模算法的合集”。由于深层神经网络是实现“多层非线性变换”最经常使用的一种方法,因此在实际中基本上能够认为深度学习就是深层神经网络的代名词。从维基百科给出的定义能够看出,深度学习有两个很是重要的特性——多层和非线性。
若是在深层神经网络中,前向传播使用线性模型,那么经过线性变换,任意层的全链接神经网络和单层的神经网络并无区别,并且他们都是线性模型。这就是线性模型最大的局限性,也是为何深度学习要强调非线性。
分类问题和回归问题是监督学习的两大种类。分类问题但愿解决的是将不一样的样本分到事先定义好的类别中。交叉熵刻画了两个几率分布之间的距离,它是分类问题中使用比较普遍的一种损失函数。
交叉熵是一个信息论中的概念,它本来是用来估计平均编码长度的。给定两个几率分布p和q,经过q来表示p的交叉熵为:
如何将神经网络前向传播获得的结果变成几率分布呢?SOftmax回归是一个很是经常使用的方法。假设原始的神经网络输出为
,那么通过Softmax回归处理以后的输出为:
与分类问题不一样,回归问题解决的是对具体数值的预测。好比房价预测、销量预测等都是回归问题。对于回归问题,最经常使用的损失函数是均方偏差(MSE,mean squared error)。它的定义以下:
梯度降低算法主要用于优化单个参数的取值,而反向传播算法给出了一个高效的方式在全部参数上使用梯度降低算法,从而使神经网络模型在训练数据上的损失函数尽量小。反向传播算法是训练神经网络的核心算法,它能够根据定义好的损失函数优化神经网络中参数的取值,从而使神经网络模型在训练数据集上的损失函数达到一个较小值。
须要注意的是,梯度降低算法并不能保证被优化的函数达到全局最优解。并且,参数的初始值会很大程度影响最后获得的结果。只有当损失函数为凸函数时,梯度降低算法才能保证达到全局最优解。梯度降低算法另外一个问题就是计算时间太长,由于要在所有训练数据上最小化损失,因此损失函数J( )是在全部训练数据上的损失和。
在训练神经网络时,须要设置学习率(learning rate)控制参数更新的速度。若是学习率设置过大,更新的幅度就比那大,那么可能致使参数在极优值的两侧来回移动。若是学习率设置太小,会致使训练的时间变长。为了解决设定学习率的我呢提,TensorFlow提供了一种更加灵活的学习率设置方法——指数衰减法。tf.train.exponential_decay函数实现了指数衰减学习率。如下代码给出了寻找 的最小值,使用梯度降低法来实现:
TRAINING_STEPS = 100 global_step = tf.Variable(0) LEARNING_RATE = tf.train.exponential_decay(0.1, global_step, 1, 0.96, staircase=True) x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x") y = tf.square(x) train_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y, global_step=global_step) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(TRAINING_STEPS): sess.run(train_op) if i % 10 == 0: LEARNING_RATE_value = sess.run(LEARNING_RATE) x_value = sess.run(x) print ("After %s iteration(s): x%s is %f, learning rate is %f."% (i+1, i+1, x_value, LEARNING_RATE_value))
运行结果:
After 1 iteration(s): x1 is 4.000000, learning rate is 0.096000. After 11 iteration(s): x11 is 0.690561, learning rate is 0.063824. After 21 iteration(s): x21 is 0.222583, learning rate is 0.042432. After 31 iteration(s): x31 is 0.106405, learning rate is 0.028210. After 41 iteration(s): x41 is 0.065548, learning rate is 0.018755. After 51 iteration(s): x51 is 0.047625, learning rate is 0.012469. After 61 iteration(s): x61 is 0.038558, learning rate is 0.008290. After 71 iteration(s): x71 is 0.033523, learning rate is 0.005511. After 81 iteration(s): x81 is 0.030553, learning rate is 0.003664. After 91 iteration(s): x91 is 0.028727, learning rate is 0.002436.
在真实应用中想要的并非让模型尽可能模拟训练数据的行为,而是但愿经过训练出来的模型对未知的数据给出判断。模型在训练数据上的表现并不必定表明了它在未知数据上的表现。过拟合,指的是当一个模型过为复杂以后,它能够很好地”记忆“每个训练数据中随机噪音的部分而忘记了要去“学习”训练数据中通用的趋势。
为了不过拟合问题,一个很是经常使用的方法时正则化(regularization)。正则化的思想就是在损失函数中加入刻画模型复杂程度的指标。经常使用的刻画模型复杂度的函数R(w)有两种,一种时L1正则化,计算公式是:
另外一种是L2正则化,计算公式是:
不管是哪种正则化方法,基本的思想都是但愿经过限制权重的大小,使得模型不能任意拟合训练数据中的随机噪音。但这两种正则化的方法也有很大的区别。首先,L1正则化会让参数变得更稀疏,而L2正则化不会让参数变得稀疏的缘由是当参数很小时,例如0.001,这个参数的平方基本上就能够忽略了。
在采用随机梯度降低算法训练神经网络时,使用滑动平均模型在不少应用中均可以在必定程度提升最终模型在测试数据上的表现。在TensorFlow中提供了tf.train.ExponentialMovingAverage来实现滑动平均模型。在初始化ExponentialMovingAverage时,须要提供一个衰减率(decay)。这个衰减率将用于控制模型的更新速度。在实际应用中,decay通常会设置成很是接近1的数(例如0.999或者0.9999)。