前面几篇文章尽量用通俗的语言讲了TensorFlow的基本理解、数据总结、深入理解。接下来的将通过一个个实战用例、由浅入深、分门别类、一步步详细介绍曲线模拟、图片识别简单版、图片识别卷积版、AI写诗、图片识别LSTM等。
每篇文章会先分析这些实例实现的深层原理(为什么这样做?这样做的好处是什么?…),像前3篇文章一样,这些都尽量用通俗的语言简化出来。然后放上注释详细可运行的代码,保证写明白代码运行机制,且保证每一行代码都经过不断地修改与优化。最后附上代码运行过程展示,尽量使用图片、动态图等方式说明机器一步步学习的过程。
这些文章所使用的TensorFlow版本是目前最新的0.12.0-rc1也就是通常所说的1.2版本,如无必要,以后不再说明使用版本情况。
从前面几篇系列文章,我们知道用机器学习来完成某些事情,比直接通过硬编码来完成,其工作量直接从编码转移到了数据的收集。这大大减少了程序员编码的负担。这种变化就像工业革命一样,解放了人的体力劳动;而人工智能则是解放了脑力劳动。这个革命之后,当人体之外的体力与脑力都被替代了,必由外而向内革命,让人自身变得更强、更聪明、更长寿;同时也向地球之外,不断探索。所以乔布斯遗嘱,让他的后代去从事生物方面的事业,不得不说是远见。现在正是脑力劳动大量被替代的过程。先从机器容易从事的、简单的开始,一步步、全部替代。这是时代发展的必然,这也是我们有生之年可见的未来。
本文以曲线拟合为例子,详细说明基本的函数api、TensorFlow程序的基本结构、数据的直观展示等。这个程序虽然看上去简单,但其充分直观地体现了机器学习的代码结构与过程。
1. 曲线拟合原理
给你一条复杂的曲线图,如图1-1,要你设计一种机制,使通过这种机制计算出的点能够尽可能地和这条曲线重合,这个过程就叫曲线拟合。当你面对这样的问题时,我们应该怎么做?
图1-1 曲线数据
如果不使用计算机,我们可能是先观察曲线的形式,猜想其形状中应该有哪些数学公式。如在这个例子中,我们可能会猜测公式中有sin相关的成份,然后再加入什么x,x的平方,x的立方。最终通过解方程大概就能求出这个公式来拟合这条曲线了。用这个方法可以解决上面提出的问题。并且当数据超出这些给定的数据范围(x超出-20到20)时,能更好地拟合将来的数据。如图1-1。当数据超出给定训练结果时,如果你计算出的函数正好与原有的曲线函数相对应,则能很好地预测超出的值。
而如果用机器学习的方法,图1-2中的蓝线就是机器学习的结果。从图中可以看出,当测试数据超出训练数据范围(xf超出-20到20)时,结果与测试的结果根本就对不上。其中的原理在前面3篇文章中详细介绍过:这是由于人强于推理,可以很好地解决推理出的未见的事情;而机器现在比较笨,只能根据你给定的训练数值来模拟。测试数据超出训练范围,机器学习就不再知道应该怎么做。当然,可以把未见的测试数据不断地加入训练数据中,然后机器不断地学习新数据(知识),使其适应范围越来越广。这也是为什么人工智能一开始比较笨,而随着你越使用它,它会变得越来越聪明的原因。
图1-2
机器学习,怎么解决这种曲线拟合的问题呢?机器来解决,我们就不能说像上面提到的人类解决的方法一样:直接把公式中有的成分先写出来,然后根据曲线中的数据,计算出相应的参数。因为曲线的形状有无数种,你对这一条曲线用某些数学公式可以拼凑出来,换一条曲线呢?换另外的曲线公式吗?还是把所有的已知的所有数学公式全部放一个式子里,让计算机去计算每个参数,如y=asin(x)+bx+cxx+dxxx+dxxx…?先不说上面两种方法的可实现性,如果碰到曲线奇里八怪的,根本就不能由数学公式组合出来怎么办。上面的那两种方法显然不行。人来解决这样的问题也够呛。
当然这里,有一种简单统计学的方法,我在训练数据中保存足够的样例数据。然后你给我一个x,我根据这个x去查找x周围,对应的y取值。然后把这些y值,根据距离x的远近,简单加权(离x近的,权重大,远的,权重小),返回给你,曲线拟合完毕。但是这有一个问题,你必须保存大量数据,保证每个区域内都有足够的数据来让你预测出y值。在图1-1中,数据很小,很容易解决。而如果这样的数据分布很大,数据有几个G,几个T,就不能这样做了。
机器解决曲线拟合问题的本质是要设计一种方便且简单可运行的机制,来预测的曲线。曲线中的每个点中的x可以看成元素x,而其y值可以看成结果。机器学习就是要学习元素x到结果y的对应关系。 前面几篇文章讲了,机器学习通过最简单的y=Wx+b来表示属性x到结果y的映射,由于(x,y)形状的千变万化(这里就是曲线形式千变万化),因此,要想让y=Wx+b体现这些变化,必需加入各种非线性的激活函数如sigmoid、tanh等。具体原理请参考系列的前3篇文章。因此,曲线拟合也是这样处理,我们先计算Wx+b,然后通过组合激活函数,来模拟任意形状的曲线。 这种方法,无需参想y=f(x)中有哪些形式和有多少个的各种表达式;也无需保存足够的数据样本,只需要训练出相关参数,保存即可。而且其拟合过程比以上说的方法都更快,更好。其基于Tensorflow的具体代码请看下面的代码详解。
2. 代码详解
2.1 数据生成
我们用y=400np.sin(x)+2x*x+noise来生成要模拟的曲线,其中的noise是随机生成的噪点。生成的训练数据如下图。
2.2 数据表示
可以用Pylot在直角坐标系上很直观方便地画出曲线(x,y)以及学到的曲线。代码中乃至的Pylot图表的相关介绍,请查看我的。其中用到的最主要代码如下。
2.3 曲线模拟形式选择
看上面的代码,我们用单层的网络。计算线性的x*W3+b3。如果加入非线性的sigmoid激活函数,改变Wx+b的值,改为 y = (x, W3) + b3)等相关形式,理论上应该能更好地模拟曲线。但实际的运行结果要么是直线,要么是各种上下不断交错。然后损失值下降不明显。如下图,是训练1万次之后的结果。我分析,应该是由于W与b的初值选得不好,造成sigmoid的中传入的参数值要么太大,要么太小,从而造成sigmoid值的梯度变化基本为0。因此达不到训练的结果。大家如果有什么更好的解释,可以在下面留言一起讨论。我是觉得用单层网络y = (x, W3) + b3)肯定能很好地模拟曲线的。
经过测试,如果直接用y=(x, W3)+b3来做为单层网络,可以比较好的解决曲线拟合问题。但这与我们的目标不符。因此,经过查找与测试研究,终于找到了加入sigmoid激活函数且对各种曲线模拟通用性都比较好曲线模拟办法。通过加入两层网络,如下。
在这种方法中,对W,b的初值选取没有第一种方法那么严格。而且如果模拟的结果不甚理想时,可以简单地加大W与b 中的维度hiddenDim的值,就可得到更好的模拟结果。
2.4 损失与优化
这里没什么可说的,定义y与训练值yTrain之间差的平方的平均值为损失函数。然后就是选择优化算法,不详细解释。现在就知道它们会通过某些机制,让损失函数值loss越来越小,也就代表曲线越来越重合,直到训练过程完毕。
2.5 训练
用上面定义的损失函数与优化方法定义训练过程。不断地在上一步训练结果的基础上,叠代循环训练了10001次。每次理论上都不断地减少了y与yTrain之间的差距,即曲线拟合得越来越好。
2.6 测试训练结果
生成测试数据,用训练出的y表达式,计算出y值。把测试数据与训练计算结果显示在图上。如下,这里用超出训练数据范围的数据作用测试数据,看超出范围时,机器学习的学习成果是否正确。
这就是曲线模拟的所有过程。所有的机器学习过程基本上是这样:先定义训练数据表示形式;再设计元素x到结果y的映射;然后定义损失函数、选择优化方法;最终训练并验证训练结果。
由于代码太长,详细代码放在本文最后。
3. 运行过程与结果
生成的训练数据如下图,x的取值范围由-20到20。
训练过程的动态图如下,可以看到训练出的曲线与训练数据不断拟合。
测试结果如下。从图中看出,当测试结果x在训练数据x的范围内时,其曲线拟合结果很好。但当一超出训练数据x的范围(超出-20到20),曲线拟合就完全对不上,变成了直线。
4. 详细代码