人人都可以做深度学习应用:入门篇

spring_007 8年前
   <h2><strong>一、人工智能和新科技革命</strong></h2>    <p>2017年围棋界发生了一件比较重要事,Master(Alphago)以60连胜横扫天下,击败各路世界冠军,人工智能以气势如虹的姿态出现在我们人类的面前。围棋曾经一度被称为“人类智慧的堡垒”,如今,这座堡垒也随之成为过去。从2016年三月份AlphaGo击败李世石开始,AI全面进入我们大众的视野,对于它的讨论变得更为火热起来,整个业界普遍认为,它很可能带来下一次科技革命,并且,在未来可预见的10多年里,深刻得改变我们的生活。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/6968b5a1e44eececf2527e274b15b93c.jpg"></p>    <p>其实,AI除了可以做我们熟知的人脸、语音等识别之外,它可以做蛮多有趣的事情。</p>    <p>例如,让AI学习大量古诗之后写古诗,并且可以写出质量非常不错的古诗。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/d5254e018b5facb5007aa234f033b779.jpg"></p>    <p>又或者,将两部设计造型不同的汽车进行融合,形成全新一种设计风格的新汽车造型。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/8f343a360705adcd3b98a3c0ed1a400f.jpg"></p>    <p>还有,之前大家在朋友圈里可能看过的,将相片转换成对应的艺术风格的画作。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/37009725257a53208603ff89f24b9cc2.jpg"></p>    <p>当前,人工智能已经在图像、语音等多个领域的技术上,取得了全面的突破。与此同时,另外一个问题随之而来,如果这一轮的AI浪潮真的将会掀起新的科技革命,那么在可预见的未来,我们整个互联网都将发生翻天覆地的变化,深刻影响我们的生活。那么作为工程师的我,又应该以何种态度和方式应对这场时代洪流的冲击呢?</p>    <p>在回答这个问题之前,我们先一起看看上一轮由计算机信息技术引领的科技革命中,过去30多年中国程序员的角色变化:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/35cc2c6407b3d7c8a0650514db92086a.jpg"></p>    <p>通过上图可以简总结:编程技术在不断地发展并且走向普及,从最开始掌握在科学家和专家学者手中的技能,逐渐发展为一门大众技能。换而言之,我们公司内很多资深的工程师,如果带着今天对编程和计算机的理解和理念回到1980年,那么他无疑就是那个时代的计算机专家。</p>    <p>如果这一轮AI浪潮真的会带来新的一轮科技革命,那么我们相信,它也会遵循类似的发展轨迹,逐步发展和走向普及。如果基于这个理解,或许,我们可以通过积极学习,争取成为第一代AI工程师。</p>    <h2>二、深度学习技术</h2>    <p>这一轮AI的技术突破,主要源于深度学习技术,而关于AI和深度学习的发展历史我们这里不重复讲述,可自行查阅。我用了一个多月的业务时间,去了解和学习了深度学习技术,在这里,我尝试以一名工程师的视角,以尽量容易让大家理解的方式一起探讨下深度学习的原理,尽管,受限于我个人的技术水平和掌握程度,未必完全准确。</p>    <p><strong>1. 人的智能和神经元</strong></p>    <p>人类智能最重要的部分是大脑,大脑虽然复杂,它的组成单元却是相对简单的,大脑皮层以及整个神经系统,是由神经元细胞组成的。而一个神经元细胞,由树突和轴突组成,它们分别代表输入和输出。连在细胞膜上的分叉结构叫树突,是输入,那根长长的“尾巴”叫轴突,是输出。神经元输出的有电信号和化学信号,最主要的是沿着轴突细胞膜表面传播的一个电脉冲。忽略掉各种细节,神经元,就是一个积累了足够的输入,就产生一次输出(兴奋)的相对简单的装置。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/6af17bcade63401e7a9c796b34aedc69.jpg"></p>    <p>树突和轴突都有大量的分支,轴突的末端通常连接到其他细胞的树突上,连接点上是一个叫“突触”的结构。一个神经元的输出通过突触传递给成千上万个下游的神经元,神经元可以调整突触的结合强度,并且,有的突触是促进下游细胞的兴奋,有的是则是抑制。一个神经元有成千上万个上游神经元,积累它们的输入,产生输出。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/08645a561107efe5857bcfd35632ccc2.jpg"></p>    <p>人脑有1000亿个神经元,1000万亿个突触,它们组成人脑中庞大的神经网络,最终产生的结果即是人的智能。</p>    <p><strong>2. 人工神经元和神经网络</strong></p>    <p>一个神经元的结构相对来说是比较简单的,于是,科学家们就思考,我们的AI是否可以从中获得借鉴?神经元接受激励,输出一个响应的方式,同计算机中的输入输出非常类似,看起来简直就是量身定做的,刚好可以用一个函数来模拟。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/f0ece7403ae2ab1e6f5d4b44eb77dc72.jpg"></p>    <p>通过借鉴和参考神经元的机制,科学家们模拟出了人工神经元和人工神经网络。当然,通过上述这个抽象的描述和图,比较难让大家理解它的机制和原理。我们以“房屋价格测算”作为例子,一起来看看:</p>    <p>一套房子的价格,会受到很多因素的影响,例如地段、朝向、房龄、面积、银行利率等等,这些因素如果细分,可能会有几十个。一般在深度学习模型里,这些影响结果的因素我们称之为特征。我们先假设一种极端的场景,例如影响价格的特征只有一种,就是房子面积。于是我们收集一批相关的数据,例如,50平米50万、93平米95万等一系列样本数据,如果将这些样本数据放到而为坐标里看,则如下图:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/577c5493f6affabddceaca5d845682cb.jpg"></p>    <p>然后,正如我们前面所说的,我们尝试用一个“函数”去拟合这个输入(面积x)和输出(价格y),简而言之,我们就是要通过一条直线或者曲线将这些点“拟合”起来。</p>    <p>假设情况也比较极端,这些点刚好可以用一条“直线”拟合(真实情况通常不会是直线),如下图:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/4d7c1a20704416623ec6d232576aca38.jpg"></p>    <p>那么我们的函数是一个一次元方程f(x) = ax +b,当然,如果是曲线的话,我们得到的将是多次元方程。我们获得这个f(x) = ax +b的函数之后,接下来就可以做房价“预测”,例如,我们可以计算一个我们从未看见的面积案例81.5平方米,它究竟是多少钱?</p>    <p>这个新的样本案例,可以通过直线找到对应的点(黄色的点),如图下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/6ae907076f59c2246d1dde285afc95a0.jpg"></p>    <p>粗略的理解,上面就是AI的概括性的运作方式。这一切似乎显得过于简单了?当然不会,因为,我们前面提到,影响房价其实远不止一个特征,而是有几十个,这样问题就比较复杂了,接下来,这里则要继续介绍深度学习模型的训练方式。这部分内容相对复杂一点,我尽量以业务工程师的视角来做一个粗略而简单的阐述。</p>    <p><strong>3. 深度学习模型的训练方式</strong></p>    <p>当有好几十个特征共同影响价格的时候,自然就会涉及权重分配的问题,例如有一些对房价是主要正权重的,例如地段、面积等,也有一些是负权重的,例如房龄等。</p>    <p><strong>(1)初始化权重计算</strong></p>    <p>那么,第一个步其实是给这些特征加一个权重值,但是,最开始我们根本不知道这些权重值是多少?怎么办呢?不管那么多了,先给它们随机赋值吧。随机赋值,最终计算出来的估算房价肯定是不准确的,例如,它可能将价值100万的房子,计算成了10万。</p>    <p><strong>(2)损失函数</strong></p>    <p>因为现在模型的估值和实际估值差距比较大,于是,我们需要引入一个评估“不准确”程度的衡量角色,也就是损失(loss)函数,它是衡量模型估算值和真实值差距的标准,损失函数越小,则模型的估算值和真实值的察觉越小,而我们的根本目的,就是降低这个损失函数。让刚刚的房子特征的模型估算值,逼近100万的估算结果。</p>    <p><strong>(3)模型调整</strong></p>    <p>通过梯度下降和反向传播,计算出朝着降低损失函数的方向调整权重参数。举一个不恰当的比喻,我们给面积增加一些权重,然后给房子朝向减少一些权重(实际计算方式,并非针对单个个例特征的调整),然后损失函数就变小了。</p>    <p><strong>(4)循环迭代</strong></p>    <p>调整了模型的权重之后,就可以又重新取一批新的样本数据,重复前面的步骤,经过几十万次甚至更多的训练次数,最终估算模型的估算值逼近了真实值结果,这个模型的则是我们要的“函数”。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/1b661cee4b844c60ec157c304861fcce.jpg"></p>    <p>为了让大家更容易理解和直观,采用的例子比较粗略,并且讲述深度学习模型的训练过程,中间省略了比较多的细节。讲完了原理,那么我们就开始讲讲如何学习和搭建demo。</p>    <h2>三、深度学习环境搭建</h2>    <p>在2个月前(2016年11月),人工智能对我来说,只是一个高大上的概念。但是,经过一个多月的业余时间的认真学习,我发现还是能够学到一些东西,并且跑一些demo和应用出来的。</p>    <p><strong>1. 学习的提前准备</strong></p>    <p>(1)部分数学内容的复习,高中数学、概率、线性代数等部分内容。(累计花费了10个小时,,挑了关键的点看了下,其实还是不太够,只能让自己看公式的时候,相对没有那么懵)</p>    <p>(2)Python基础语法学习。(花费了3个小时左右,我以前从未写过Python,因为后面Google的TensorFlow框架的使用是基于Python的)</p>    <p>(3)Google的TensorFlow深度学习开源框架。(花费了10多个小时去看)</p>    <p>数学基础好或者前期先不关注原理的同学,数学部分不看也可以开始做,全凭个人选择。</p>    <p><strong>2. Google的TensorFlow开源深度学习框架</strong></p>    <p>深度学习框架,我们可以粗略的理解为是一个“数学函数”集合和AI训练学习的执行框架。通过它,我们能够更好的将AI的模型运行和维护起来。</p>    <p>深度学习的框架有各种各样的版本( Caffe 、 Torch 、 Theano<a href="/misc/goto?guid=4959741727709527473" rel="nofollow,noindex"> </a> 等等),我只接触了Google的TensorFlow,因此,后面的内容都是基于TensorFlow展开的,它的详细介绍这里不展开讲述,建议直接进入官网查看。非常令人庆幸的是TensorFlow比较早就有中文社区了,尽管里面的内容有一点老,搭建环境方面有一些坑,但是已经属于为数不多的中文文档了,大家且看且珍惜。</p>    <p><strong>3. TensorFlow环境搭建</strong></p>    <p>环境搭建本身并不复杂,主要解决相关的依赖。但是,基础库的依赖可以带来很多问题,因此,建议尽量一步到位,会简单很多。</p>    <p><strong>(1)操作系统</strong></p>    <p>我搭建环境使用的机器是腾讯云上的机器,软件环境如下:</p>    <p>操作系统:CentOS 7.2 64位(GCC 4.8.5)</p>    <p>因为这个框架依赖于python2.7和glibc 2.17。比较旧的版本的CentOS一般都是python2.6以及版本比较低的glibc,会产生比较的多基础库依赖问题。而且,glibc作为Linux的底层库,牵一发动全身,直接对它升级是比较复杂,很可能会带来更多的环境异常问题。</p>    <p><strong>(2)软件环境</strong></p>    <p>我目前安装的Python版本是python-2.7.5,建议可以采用yum install python的方式安装相关的原来软件。然后,再安装 python内的组件包管理器pip,安装好pip之后,接下来的其他软件的安装就相对比较简单了。</p>    <p>例如安装TensorFlow,可通过如下一句命令完成(它会自动帮忙解决一些库依赖问题):</p>    <p>pip install -U tensorflow</p>    <p>这里需要特别注意的是,不要按照TensorFlow的中文社区的指引去安装,因为它会安装一个非常老的版本(0.5.0),用这个版本跑很多demo都会遇到问题的。而实际上,目前通过上述提供的命令安装,是tensorflow (1.0.0)的版本了。</p>    <p><img src="https://simg.open-open.com/show/6a8d50995a5e5e3b4b6b71b33efe84cb.jpg"></p>    <p>Python(2.7.5)下的其他需要安装的关键组件:</p>    <p>tensorflow (0.12.1),深度学习的核心框架</p>    <p>image (1.5.5),图像处理相关,部分例子会用到</p>    <p>PIL (1.1.7),图像处理相关,部分例子会用到</p>    <p>除此之后,当然还有另外的一些依赖组件,通过pip list命令可以查看我们安装的python组件:</p>    <pre>  appdirs (1.4.0)  backports.ssl-match-hostname (3.4.0.2)  chardet (2.2.1)  configobj (4.7.2)  decorator (3.4.0)  Django (1.10.4)  funcsigs (1.0.2)  image (1.5.5)  iniparse (0.4)  kitchen (1.1.1)  langtable (0.0.31)  mock (2.0.0)  numpy (1.12.0)  packaging (16.8)  pbr (1.10.0)  perf (0.1)  PIL (1.1.7)  Pillow (3.4.2)  pip (9.0.1)  protobuf (3.2.0)  pycurl (7.19.0)  pygobject (3.14.0)  pygpgme (0.3)  pyliblzma (0.5.3)  pyparsing (2.1.10)  python-augeas (0.5.0)  python-dmidecode (3.10.13)  pyudev (0.15)  pyxattr (0.5.1)  setuptools (34.2.0)  six (1.10.0)  slip (0.4.0)  slip.dbus (0.4.0)  tensorflow (1.0.0)  urlgrabber (3.10)  wheel (0.29.0)  yum-langpacks (0.4.2)  yum-metadata-parser (1.1.4)     </pre>    <p>按照上述提供的来搭建系统,可以规避不少的环境问题。</p>    <p>搭建环境的过程中,我遇到不少问题。例如:在跑官方的例子时的某个报,AttributeError: ‘module’ object has no attribute ‘gfile’,就是因为安装的TensorFlow的版本比较老,缺少gfile模块导致的。而且,还有各种各样的。</p>    <p>更详细的安装说明:</p>    <p><a href="/misc/goto?guid=4959741727792772784" rel="nofollow,noindex">https://www. tensorflow.org/install/ install_linux </a></p>    <p><strong>(3)TensorFlow环境测试运行</strong></p>    <p>测试是否安装成功,可以采用官方的提供的一个短小的例子,demo生成了一些三维数据, 然后用一个平面拟合它们(官网的例子采用的初始化变量的函数是initialize_all_variables,该函数在新版本里已经被废弃了):</p>    <pre>  #!/usr/bin/python  #coding=utf-8     importtensorflowas tf  importnumpyas np     # 使用 NumPy 生成假数据(phony data), 总共 100 个点.  x_data = np.float32(np.random.rand(2, 100)) # 随机输入  y_data = np.dot([0.100, 0.200], x_data) + 0.300     # 构造一个线性模型  #  b = tf.Variable(tf.zeros([1]))  W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0))  y = tf.matmul(W, x_data) + b     # 最小化方差  loss = tf.reduce_mean(tf.square(y - y_data))  optimizer = tf.train.GradientDescentOptimizer(0.5)  train = optimizer.minimize(loss)     # 初始化变量,旧函数(initialize_all_variables)已经被废弃,替换为新函数  init = tf.global_variables_initializer()     # 启动图 (graph)  sess = tf.Session()  sess.run(init)     # 拟合平面  for stepin xrange(0, 201):      sess.run(train)      if step % 20 == 0:          printstep, sess.run(W), sess.run(b)     # 得到最佳拟合结果 W: [[0.100  0.200]], b: [0.300]     </pre>    <p>运行的结果类似如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/9476b22ce3d7b4195932ca9f8df4ac9e.jpg"></p>    <p>经过200次的训练,模型的参数逐渐逼近最佳拟合的结果(W: [[0.100 0.200]], b: [0.300]),另外,我们也可以从代码的“风格”中,了解到框架样本训练的基本运行方式。虽然,官方的教程后续会涉及越来越多更复杂的例子,但从整体上看,也是类似的模式。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/105a41550badbcb57e3a5b9ab11d3b91.jpg"></p>    <p><strong>步骤划分:</strong></p>    <ol>     <li>准备数据:获得有标签的样本数据(带标签的训练数据称为有监督学习);</li>     <li>设置模型:先构建好需要使用的训练模型,可供选择的机器学习方法其实也挺多的,换而言之就是一堆数学函数的集合;</li>     <li>损失函数和优化方式:衡量模型计算结果和真实标签值的差距;</li>     <li>真实训练运算:训练之前构造好的模型,让程序通过循环训练和学习,获得最终我们需要的结果“参数”;</li>     <li>验证结果:采用之前模型没有训练过的测试集数据,去验证模型的准确率。</li>    </ol>    <p>其中,TensorFlow为了基于python实现高效的数学计算,通常会使用到一些基础的函数库,例如Numpy(采用外部底层语言实现),但是,从外部计算切回到python也是存在开销的,尤其是在几万几十万次的训练过程。因此,Tensorflow不单独地运行单一的函数计算,而是先用图描述一系列可交互的计算操作流程,然后全部一次性提交到外部运行(在其他机器学习的库里,也是类似的实现)。所以,上述流程图中,蓝色部分都只是设置了“计算操作流程”,而绿色部分开始才是真正的提交数据给到底层库进行实际运算,而且,每次训练一般是批量执行一批数据的。</p>    <h2>四、经典入门demo:识别手写数字(MNIST)</h2>    <p>常规的编程入门有“Hello world”程序,而深度学习的入门程序则是MNIST,一个识别28*28像素的图片中的手写数字的程序。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/e22d092180a083b886ecde5cedf88a80.jpg"></p>    <p>深度学习的内容,其背后会涉及比较多的数学原理,作为一个初学者,受限于我个人的数学和技术水平,也许并不足以准确讲述相关的数学原理,因此,本文会更多的关注“应用层面”,不对背后的数学原理进行展开,感谢谅解。</p>    <p><strong>1. 加载数据</strong></p>    <p>程序执行的第一步当然是加载数据,根据我们之前获得的数据集主要包括两部分:60000的训练数据集(mnist.train)和10000的测试数据集(mnist.test)。里面每一行,是一个28*28=784的数组,数组的本质就是将28*28像素的图片,转化成对应的像素点阵。</p>    <p>例如手写字1的图片转换出来的对应矩阵表示如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/61d6aeec85494a25d25e22feac9de69b.jpg"></p>    <p>之前我们经常听说,图片方面的深度学习需要大量的计算能力,甚至需要采用昂贵、专业的GPU(Nvidia的GPU),从上述转化的案例我们就已经可以获得一些答案了。一张784像素的图片,对学习模型来说,就有784个特征,而我们实际的相片和图片动辄几十万、百万级别,则对应的基础特征数也是这个数量级,基于这样数量级的数组进行大规模运算,没有强大的计算能力支持,确实寸步难行。当然,这个入门的MNIST的demo还是可以比较快速的跑完。</p>    <p>Demo中的关键代码(读取并且加载数据到数组对象中,方便后面使用):</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/fef89109158e0f4c4b17435a23522431.jpg"></p>    <p><strong>2. 构建模型</strong></p>    <p>MNIST的每一张图片都表示一个数字,从0到9。而模型最终期望获得的是:给定一张图片,获得代表每个数字的概率。比如说,模型可能推测一张数字9的图片代表数字9的概率是80%但是判断它是8的概率是5%(因为8和9都有上半部分的小圆),然后给予它代表其他数字的概率更小的值。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/a8de5c0d67e15d65a9298dad52968ad6.jpg"></p>    <p>MNIST的入门例子,采用的是softmax回归(softmax regression),softmax模型可以用来给不同的对象分配概率。</p>    <p>为了得到一张给定图片属于某个特定数字类的证据(evidence),我们对图片的784个特征(点阵里的各个像素值)进行加权求和。如果某个特征(像素值)具有很强的证据说明这张图片不属于该类,那么相应的权重值为负数,相反如果某个特征(像素值)拥有有利的证据支持这张图片属于这个类,那么权重值是正数。类似前面提到的房价估算例子,对每一个像素点作出了一个权重分配。</p>    <p>假设我们获得一张图片,需要计算它是8的概率,转化成数学公式则如下:</p>    <p><img src="https://simg.open-open.com/show/99cb4a6c4c43bfa2f4b9b00501da0bf5.jpg"></p>    <p>公式中的i代表需要预测的数字(8),代表预测数字为8的情况下,784个特征的不同权重值,代表8的偏置量(bias),X则是该图片784个特征的值。通过上述计算,我们则可以获得证明该图片是8的证据(evidence)的总和,softmax函数可以把这些证据转换成概率 y。(softmax的数学原理,辛苦各位查询相关资料哈)</p>    <p>将前面的过程概括成一张图(来自官方)则如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/853f8ec6f13545621cf7b4bb52a159af.jpg"></p>    <p>不同的特征x和对应不同数字的权重进行相乘和求和,则获得在各个数字的分布概率,取概率最大的值,则认为是我们的图片预测结果。</p>    <p>将上述过程写成一个等式,则如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/2997f279490d09fe6e6127743b0da3ec.jpg"></p>    <p>该等式在矩阵乘法里可以非常简单地表示,则等价为:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/321f87ba415a3f5af43ce5e5bd799500.jpg"></p>    <p>不展开里面的具体数值,则可以简化为:</p>    <p><img src="https://simg.open-open.com/show/f66308b7d7abad0451a2f295021061d3.jpg"></p>    <p>如果我们对线性代数中矩阵相关内容有适当学习,其实,就会明白矩阵表达在一些问题上,更易于理解。如果对矩阵内容不太记得了,也没有关系,后面我会附加上线性代数的视频。</p>    <p>虽然前面讲述了这么多,其实关键代码就四行:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/767ebfed99b31f3c5e7301d301528a86.jpg"></p>    <p>上述代码都是类似变量占位符,先设置好模型计算方式,在真实训练流程中,需要批量读取源数据,不断给它们填充数据,模型计算才会真实跑起来。tf.zeros则表示,先给它们统一赋值为0占位。X数据是从数据文件中读取的,而w、b是在训练过程中不断变化和更新的,y则是基于前面的数据进行计算得到。</p>    <p><strong>3. 损失函数和优化设置</strong></p>    <p>为了训练我们的模型,我们首先需要定义一个指标来衡量这个模型是好还是坏。这个指标称为成本(cost)或损失(loss),然后尽量最小化这个指标。简单的说,就是我们需要最小化loss的值,loss的值越小,则我们的模型越逼近标签的真实结果。</p>    <p>Demo中使用的损失函数是“交叉熵”(cross-entropy),它的公式如下:</p>    <p><img src="https://simg.open-open.com/show/a2b97cc56e9ba4cfa3ee9d464cc8ca99.jpg"></p>    <p>y 是我们预测的概率分布, y’ 是实际的分布(我们输入的),交叉熵是用来衡量我们的预测结果的不准确性。TensorFlow拥有一张描述各个计算单元的图,也就是整个模型的计算流程,它可以自动地使用 反向传播算法(backpropagation algorithm) ,来确定我们的权重等变量是如何影响我们想要最小化的那个loss值的。然后,TensorFlow会用我们设定好的优化算法来不断修改变量以降低loss值。</p>    <p>其中,demo采用梯度下降算法(gradient descent algorithm)以0.01的学习速率最小化交叉熵。梯度下降算法是一个简单的学习过程,TensorFlow只需将每个变量一点点地往使loss值不断降低的方向更新。</p>    <p>对应的关键代码如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/7aa1213c49aab8a6b8904ade1ba08903.jpg"></p>    <p>备注内容:</p>    <p>交叉熵: <a href="/misc/goto?guid=4959741727874620988" rel="nofollow,noindex"> Visual Information Theory </a></p>    <p>反向传播: <a href="/misc/goto?guid=4959741727953835367" rel="nofollow,noindex"> Calculus on Computational Graphs: Backpropagation </a></p>    <p>在代码中会看见one-hot vector的概念和变量名,其实这个是个非常简单的东西,就是设置一个10个元素的数组,其中只有一个是1,其他都是0,以此表示数字的标签结果。</p>    <p>例如表示数字3的标签值:</p>    <p>[0,0,0,1,0,0,0,0,0,0]</p>    <p><strong>4. 训练运算和模型准确度测试</strong></p>    <p>通过前面的实现,我们已经设置好了整个模型的计算“流程图”,它们都成为TensorFlow框架的一部分。于是,我们就可以启动我们的训练程序,下面的代码的含义是,循环训练我们的模型500次,每次批量取50个训练样本。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/0d24a4c1ae8c057a00b16d7a07fb4ca6.jpg"></p>    <p>其训练过程,其实就是TensorFlow框架的启动训练过程,在这个过程中,python批量地将数据交给底层库进行处理。</p>    <p>我在官方的demo里追加了两行代码,每隔50次则额外计算一次当前模型的识别准确率。它并非必要的代码,仅仅用于方便观察整个模型的识别准确率逐步变化的过程。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/77227da836d564f8e718b75ef60b70f6.jpg"></p>    <p>当然,里面涉及的accuracy(预测准确率)等变量,需要在前面的地方定义占位:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/23174f8da18dceb89c4963267d4f1f1e.jpg"></p>    <p>当我们训练完毕,则到了验证我们的模型准确率的时候,和前面相同:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/8f480db8aa1fa396e2758f0212ce2f51.jpg"></p>    <p>我的demo跑出来的结果如下(softmax回归的例子运行速度还是比较快的),当前的准确率是0.9252:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/432ac3bbbe5e468636efc560d805d18b.jpg"></p>    <p><strong>5. 实时查看参数的数值的方法</strong></p>    <p>刚开始跑官方的demo的时候,我们总想将相关变量的值打印出来看看,是怎样一种格式和状态。从demo的代码中,我们可以看见很多的Tensor变量对象,而实际上这些变量对象都是无法直接输出查看,粗略地理解,有些只是占位符,直接输出的话,会获得类似如下的一个对象:</p>    <pre>  Tensor("Equal:0", shape=(?,), dtype=bool)     </pre>    <p>既然它是占位符,那么我们就必须喂一些数据给它,它才能将真实内容展示出来。因此,正确的方法是,在打印时通常需要加上当前的输入数据给它。</p>    <p>例如,查看y的概率数据:</p>    <pre>  print(sess.run(y, feed_dict={x: batch_xs, y_: batch_ys}))     </pre>    <p>部分非占位符的变量还可以这样输出来:</p>    <pre>  print(W.eval())     </pre>    <p>总的来说,92%的识别准确率是比较令人失望,因此,官方的MNIST其实也有多种模型的不同版本,其中比较适合图片处理的CNN(卷积神经网络)的版本,可以获得99%以上的准确率,当然,它的执行耗时也是比较长的。</p>    <p>(备注:cnn_mnist.py就是卷积神经网络版本的,后面有附带微云网盘的下载url)</p>    <p>前馈神经网络(feed-forward neural network)版本的MNIST,可达到97%:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/f7f4a51ffee70b57afdcce3fdfdb3b9e.jpg"></p>    <p>分享在微云上的数据和源码:</p>    <p>(备注:国外网站下载都比较慢,我这份下载相对会快一些,在环境已经搭建完毕的情况下,执行里面的run.py即可)</p>    <h2>五、和业务场景结合的demo:预测用户是否是超级会员身份</h2>    <p>根据前面的内容,我们对上述基于softmax只是三层(输入、处理、输出)的神经网络模型已经比较熟悉,那么,这个模型是否可以应用到我们具体的业务场景中,其中的难度大吗?为了验证这一点,我拿了一些现网的数据来做了这个试验。</p>    <p><strong>1. 数据准备</strong></p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/f4fb6c56d010f50b5bd12341d41bcdfe.jpg"></p>    <p>我将一个现网的电影票活动的用户参与数据,包括点击过哪些按钮、手机平台、IP地址、参与时间等信息抓取了出来。其实这些数据当中是隐含了用户的身份信息的,例如,某些礼包的必须是超级会员身份才能领取,如果这个按钮用户点击领取成功,则可以证明该用户的身份肯定是超级会员身份。当然,我只是将这些不知道相不相关的数据特征直观的整理出来,作为我们的样本数据,然后对应的标签为超级会员身份。</p>    <p>用于训练的样本数据格式如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/c1e324df259e8caecc8984c962130b41.jpg"></p>    <p>第一列是QQ号码,只做认知标识的,第二列表示是否超级会员身份,作为训练的标签值,后面的就是IP地址,平台标志位以及参与活动的参与记录(0是未成功参与,1表示成功参与)。则获得一个拥有11个特征的数组(经过一些转化和映射,将特别大的数变小):</p>    <pre>  [0.9166666666666666, 0.4392156862745098, 0.984313725490196, 0.7411764705882353, 0.2196078431372549, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]     </pre>    <p>对应的是否是超级数据格式如下,作为监督学习的标签:</p>    <p>超级会员:[0, 1]</p>    <p>非超级会员:[1, 0]</p>    <p>这里需要专门解释下,在实际应用中需要做数据转换的原因。一方面,将这些数据做一个映射转化,有助于简化数据模型。另一方面,是为了规避NaN的问题,当数值过大,在一些数学指数和除法的浮点数运算中,有可能得到一个无穷大的数值,或者其他溢出的情形,在Python里会变为NaN类型,这个类型会破坏掉后续全部计算结果,导致计算异常。</p>    <p>例如下图,就是特征数值过大,在训练过程中,导致中间某些参数累计越来越大,最终导致产生NaN值,后续的计算结果全部被破坏掉:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/75a7317c1f3ddc907e5e1616c67e357f.jpg"></p>    <p>而导致NaN的原因在复杂的数学计算里,会产生无穷大或者无穷小。例如,在我们的这个demo中,产生NaN的原因,主要是因为softmax的计算导致。</p>    <p><img src="https://simg.open-open.com/show/ae0b0f870b7cb9f37c1f9d6c18428a1e.jpg"></p>    <pre>  RuntimeWarning: dividebyzeroencounteredin log     </pre>    <p>刚开始做实际的业务应用,就发现经常跑出极奇怪异的结果(遇到NaN问题,我发现程序也能继续走下去),几经排查才发现是NAN值问题,是非常令人沮丧的。当然,经过仔细分析问题,发现也并非没有排查的方式。因为,NaN值是个奇特的类型,可以采用下述编码方式NaN != NaN来检测自己的训练过程中,是否出现的NaN。</p>    <p>关键程序代码如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/5bb4a6a1f46d8a0fee8e0dfb504fe027.jpg"></p>    <p>我采用上述方法,非常顺利地找到自己的深度学习程序,在学习到哪一批数据时产生的NaN。因此,很多原始数据我们都会做一个除以某个值,让数值变小的操作。例如官方的MNIST也是这样做的,将256的像素颜色的数值统一除以255,让它们都变成一个小于1的浮点数。</p>    <p>MNIST在处理原始图片像素特征数据时,也对特征数据进行了变小处理:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/e1ebfcc72f4ea7728f26a5269647a30b.jpg"></p>    <p>处理NaN问题更专业的方法,就是对输入数据进行归一化处理(min-max标准化或Z-score标准化方法),将值控制在一个可控的范围内。NaN值问题曾一度深深地困扰着我,特别放到这里,避免入门的同学踩坑。</p>    <p><strong>2. 执行结果</strong></p>    <p>我准备的训练集(6700)和测试集(1000)数据并不多,不过,超级会员身份的预测准确率最终可以达到87%。虽然,预测准确率是不高,这个可能和我的训练集数据比较少有关系,不过,整个模型也没有花费多少时间,从整理数据、编码、训练到最终跑出结果,只用了2个晚上的时间。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/f76573ec26cb5bfdf1f8e7a8727fb87c.jpg"></p>    <p>下图是两个实际的测试例子,例如,该模型预测第一个QQ用户有82%的概率是非超级会员用户,17.9%的概率为超级会员用户(该预测是准确的)。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/9a9bc678066756311ae5f93ec3ce4f08.jpg"></p>    <p>通过上面的这个例子,我们会发觉其实对于某些比较简单的场景下应用,我们是可以比较容易就实现的。</p>    <h2>六、其他模型</h2>    <p><strong>1. CIFAR-10识别图片分类的demo(官方)</strong></p>    <p>CIFAR-10数据集的分类是机器学习中一个公开的基准测试问题,它任务是对一组32x32RGB的图像进行分类,这些图像涵盖了10个类别:飞机, 汽车, 鸟, 猫, 鹿, 狗, 青蛙, 马, 船和卡车。</p>    <p>这也是官方的重要demo之一。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/7f65b3bd6205e665c39dde4345f91bdb.jpg"></p>    <p>更详细的介绍内容:</p>    <p><a href="/misc/goto?guid=4959741728033523753" rel="nofollow,noindex">CIFAR-10 and CIFAR-100 datasets </a></p>    <p><a href="/misc/goto?guid=4959741728107166159" rel="nofollow,noindex">TensorFlow 官方文档中文版 </a></p>    <p>该例子执行的过程比较长,需要耐心等待。</p>    <p>我在机器上的执行过程和结果:</p>    <p>cifar10_train.py用于训练:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/4eada75cc9731ededc88f14cb83b90d4.jpg"></p>    <p>cifar10_eval.py用于检验结果:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/91afb2fd45de151b0d06931933f4ec02.jpg"></p>    <p>识别率不高是因为该官方模型的识别率本来就不高:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/3f6dacf084d449ea31725191a5d6af2d.jpg"></p>    <p>另外,官方的例子我首次在1月5日跑的时候,还是有一些小问题的,无法跑起来(最新的官方可能已经修正),建议可以直接使用我放到微云上的版本(代码里面的log和读取文件的路径,需要调整一下)。</p>    <p>源码下载: <a href="/misc/goto?guid=4959741728198686171" rel="nofollow,noindex"> 微云文件 </a></p>    <p>微云盘里,不含训练集和测试集的图片数据,但是,程序如果检测到这些图片不存在,会自行下载:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/cdc81c410d87a8c181e81f0140d6030d.jpg"></p>    <p><strong>2. 是否大于5岁的测试demo</strong></p>    <p>为了检验softma回归模型是否能够学习到一些我自己设定好的规则,我做了一个小demo来测试。我通过随机数生成的方式构造了一系列的数据,让前面的softmax回归模型去学习,最终看看模型能否通过训练集的学习,最终100%预测这个样本数据是否大于5岁。</p>    <p>模型和数据本身都比较简单,构造的数据的方式:</p>    <p>我随机构造一个只有2个特征纬度的样本数据,[year, 1],其中year随机取值0-10,数字1是放进去作为干扰。</p>    <p>如果year大于5岁,则标签设置为:[0, 0, 1];</p>    <p>否则,标签设置为:[0, 1, 0]。</p>    <p>生成了6000条假训练集去训练该模型,最终它能做到100%成功预测准确:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/588254d837fe44cbc3a849f21826a8d6.jpg"></p>    <p><strong>3. 基于RNN的古诗学习</strong></p>    <p>最开头的AI写古诗,非常令人感到惊艳,那个demo是美国的一个研究者做出来的,能够根据主题生成不能的古诗,而且古诗的质量还比较高。于是,我也尝试在自己的机器上也跑一个能够写古诗的模型,后来我找到的是一个基于RNN的模型。RNN 循环神经网络(Recurrent Neural Networks) ,是非常常用的深度学习模型之一。我基于一个外部的demo,进行一些调整后跑起一个能够学习古诗和写古诗的比较简单的程序。</p>    <p>我的程序默认配置是读取三万首诗(做了一些过滤,将格式有误的或者非五言、七言的过滤掉),执行写诗(让它写了十首):</p>    <p>画枕江天月似霜,竹房春雪夜中春。今人已向陵边寺,夜客闲行入画舟。云上古来烟雪晚,月寒溪水夜无穷。何因便是鲈根侣,又是离觞酒到年。</p>    <p>酒渴清秋未可还,年华偏见酒壶钱。无因不自全为叟,自觉平阳似我同。</p>    <p>棋中春草下春城,更被霜衣独入门。若教未开应要老,无妨归梦不离身。青苔白首无山药,落雪飞来夜夜长。何时未过无惆怅,满溪红土绿荷寒。</p>    <p>不辞宫树与仙郎,曾与青春得断魂。为向前溪犹绕药,可因心苦雪时春。不须爱尽春来老,自有文师说了何。不必便从归上信,也为言在此花来。</p>    <p>初时一点锦莲红,曾对仙舆在上河。万卷有时人未见,五金终处更难过。山横海渚人来起,天柳萧森旧殿寒。唯见长州知己所,更从平地作风光。</p>    <p>去事相来别恨情,年边未是我家贫。花声未觉春多病,白雪无多到日寒。三亩有愁多不定,数千终去故心忙。莫将何用知兄用,犹待青林又不迷。</p>    <p>子有心才非病拙,一杯曾有白流愁。三朝独到南山晚,白鹤争生未见山。多在玉壶人爱静,只能安稳更堪怜。只惭旧笔同才力,犹恐长心是主机。</p>    <p>尘末高台入碧霄,故王门户尽知无。黄华只拟相依锁,三落青花亦有时。不见一家相似恨,白衣无限梦长安。</p>    <p>莫惜花声酒醒杯,未央山冷一枝香。花垂未入东窗晓,雨冷山寒白鹤飞。长是玉山心寂漫,何曾便在旧楼间。时闻此夕来还坐,笑作青苔寄旧游。</p>    <p>夹江城暖望潾山,山水清深独倚楼。白云半向三山客,万顷东楼日满林。白日夜声多远浪,绿花犹向梦来迟。明年不觉长相见,欲别寒泉又夜流。</p>    <p>另外,我抽取其中一些个人认为写得比较好的诗句(以前跑出来的,不在上图中):</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/25175d98895acecb98877c0cba736dbd.jpg"></p>    <p>该模型比较简单,写诗的水平不如最前面我介绍的美国研究者demo,但是,所采用的基本方法应该是类似的,只是他做的更为复杂。</p>    <p>另外,这是一个通用模型,可以学习不同的内容(古诗、现代诗、宋词或者英文诗等),就可以生成对应的结果。</p>    <h2>七、深度学习的入门学习体会</h2>    <p>1. 人工智能和深度学习技术并不神秘,更像是一个新型的工具,通过喂数据给它,然后,它能发现这些数据背后的规律,并为我们所用。</p>    <p>2. 数学基础比较重要,这样有助于理解模型背后的数学原理,不过,从纯应用角度来说,并不一定需要完全掌握数学,也可以提前开始做一些尝试和学习。</p>    <p>3. 我深深地感到计算资源非常缺乏,每次调整程序的参数或训练数据后,跑完一次训练集经常要很多个小时,部分场景不跑多一些训练集数据,看不出差别,例如写诗的案例。个人感觉,这个是制约AI发展的重要问题,它直接让程序的“调试”效率非常低下。</p>    <p>4. 中文文档比较少,英文文档也不多,开源社区一直在快速更新,文档的内容过时也比较快。因此,入门学习时遇到的问题会比较多,并且缺乏成型的文档。</p>    <h2>八、小结</h2>    <p>我们不知道人工智能的时代是否真的会来临,也不知道它将要走向何方,但是,毫无疑问,它是一种全新的技术思维模式。更好的探索和学习这种新技术,然后在业务应用场景寻求结合点,最终达到帮助我们的业务获得更好的成果,一直以来,就是我们工程师的不懈追求。另一方面,对发展有重大推动作用的新技术,通常会快速的发展并且走向普及,就如同我们的编程一样,因此,人人都可以做深度学习应用,并非只是一句噱头。</p>    <p>参考文档:</p>    <p><a href="/misc/goto?guid=4959741728275050190" rel="nofollow,noindex">TensorFlow中文社区-首页 </a></p>    <p><a href="/misc/goto?guid=4959741728361791712" rel="nofollow,noindex">https://www. tensorflow.org/ </a></p>    <p> </p>    <p>来自:http://blog.jobbole.com/110557/</p>    <p> </p>