一、人工智能和新科技革命
2017 年围棋界发生了一件比较重要事,Master(Alphago)以 60 连胜横扫天下,击败各路世界冠军,人工智能以气势如虹的姿态出现在我们人类的面前。围棋曾经一度被称为“人类智慧的堡垒”,如今,这座堡垒也随之成为过去。从 2016 年三月份 AlphaGo 击败李世石开始,AI 全面进入我们大众的视野,对于它的讨论变得更为火热起来,整个业界普遍认为,它很可能带来下一次科技革命,并且,在未来可预见的 10 多年里,深刻得改变我们的生活。
其实,AI 除了可以做我们熟知的人脸、语音等识别之外,它可以做蛮多有趣的事情。
例如,让 AI 学习大量古诗之后写古诗,并且可以写出质量非常不错的古诗。
又或者,将两部设计造型不同的汽车进行融合,形成全新一种设计风格的新汽车造型。
还有,之前大家在朋友圈里可能看过的,将相片转换成对应的艺术风格的画作。
当前,人工智能已经在图像、语音等多个领域的技术上,取得了全面的突破。与此同时,另外一个问题随之而来,如果这一轮的 AI 浪潮真的将会掀起新的科技革命,那么在可预见的未来,我们整个互联网都将发生翻天覆地的变化,深刻影响我们的生活。那么作为程序员和工程师的我们,又应该以何种态度和方式应对这场时代洪流的冲击呢?
在回答这个问题之前,我们先一起看看上一轮由计算机信息技术引领的科技革命中,过去 30 多年中国程序员的角色变化:
通过上图可以简总结:编程技术在不断地发展并且走向普及,从最开始掌握在科学家和专家学者手中的技能,逐渐发展为一门大众技能。换而言之,我们公司内很多资深的工程师,如果带着今天对编程和计算机的理解和理念回到 1980 年,那么他无疑就是那个时代的计算机专家。
如果这一轮 AI 浪潮真的会带来新的一轮科技革命,那么我们相信,它也会遵循类似的发展轨迹,逐步发展和走向普及。如果基于这个理解,或许,我们可以通过积极学习,争取成为第一代 AI 工程师。
二、深度学习技术
这一轮 AI 的技术突破,主要源于深度学习技术,而关于 AI 和深度学习的发展历史我们这里不重复讲述,可自行查阅。我用了一个多月的业务时间,去了解和学习了深度学习技术,在这里,我尝试以一名工程师的视角,以尽量容易让大家理解的方式一起探讨下深度学习的原理,尽管,受限于我个人的技术水平和掌握程度,未必完全准确。
1. 人的智能和神经元
人类智能最重要的部分是大脑,大脑虽然复杂,它的组成单元却是相对简单的,大脑皮层以及整个神经系统,是由神经元细胞组成的。而一个神经元细胞,由树突和轴突组成,它们分别代表输入和输出。连在细胞膜上的分叉结构叫树突,是输入,那根长长的“尾巴”叫轴突,是输出。神经元输出的有电信号和化学信号,最主要的是沿着轴突细胞膜表面传播的一个电脉冲。忽略掉各种细节,神经元,就是一个积累了足够的输入,就产生一次输出(兴奋)的相对简单的装置。
树突和轴突都有大量的分支,轴突的末端通常连接到其他细胞的树突上,连接点上是一个叫“突触”的结构。一个神经元的输出通过突触传递给成千上万个下游的神经元,神经元可以调整突触的结合强度,并且,有的突触是促进下游细胞的兴奋,有的是则是抑制。一个神经元有成千上万个上游神经元,积累它们的输入,产生输出。
人脑有 1000 亿个神经元,1000 万亿个突触,它们组成人脑中庞大的神经网络,最终产生的结果即是人的智能。
2. 人工神经元和神经网络
一个神经元的结构相对来说是比较简单的,于是,科学家们就思考,我们的 AI 是否可以从中获得借鉴?神经元接受激励,输出一个响应的方式,同计算机中的输入输出非常类似,看起来简直就是量身定做的,刚好可以用一个函数来模拟。
通过借鉴和参考神经元的机制,科学家们模拟出了人工神经元和人工神经网络。当然,通过上述这个抽象的描述和图,比较难让大家理解它的机制和原理。我们以“房屋价格测算”作为例子,一起来看看:
一套房子的价格,会受到很多因素的影响,例如地段、朝向、房龄、面积、银行利率等等,这些因素如果细分,可能会有几十个。一般在深度学习模型里,这些影响结果的因素我们称之为特征。我们先假设一种极端的场景,例如影响价格的特征只有一种,就是房子面积。于是我们收集一批相关的数据,例如,50 平米 50 万、93 平米 95 万等一系列样本数据,如果将这些样本数据放到而为坐标里看,则如下图:
然后,正如我们前面所说的,我们尝试用一个“函数”去拟合这个输入(面积 x)和输出(价格 y),简而言之,我们就是要通过一条直线或者曲线将这些点“拟合”起来。
假设情况也比较极端,这些点刚好可以用一条“直线”拟合(真实情况通常不会是直线),如下图:
那么我们的函数是一个一次元方程 f(x) = ax +b,当然,如果是曲线的话,我们得到的将是多次元方程。我们获得这个 f(x) = ax +b 的函数之后,接下来就可以做房价“预测”,例如,我们可以计算一个我们从未看见的面积案例 81.5 平方米,它究竟是多少钱?
这个新的样本案例,可以通过直线找到对应的点(黄色的点),如图下:
粗略的理解,上面就是 AI 的概括性的运作方式。这一切似乎显得过于简单了?当然不会,因为,我们前面提到,影响房价其实远不止一个特征,而是有几十个,这样问题就比较复杂了,接下来,这里则要继续介绍深度学习模型的训练方式。这部分内容相对复杂一点,我尽量以工程师的视角来做一个粗略而简单的阐述。
3. 深度学习模型的训练方式
当有好几十个特征共同影响价格的时候,自然就会涉及权重分配的问题,例如有一些对房价是主要正权重的,例如地段、面积等,也有一些是负权重的,例如房龄等。
(1)初始化权重计算
那么,第一个步其实是给这些特征加一个权重值,但是,最开始我们根本不知道这些权重值是多少?怎么办呢?不管那么多了,先给它们随机赋值吧。随机赋值,最终计算出来的估算房价肯定是不准确的,例如,它可能将价值 100 万的房子,计算成了 10 万。
(2)损失函数
因为现在模型的估值和实际估值差距比较大,于是,我们需要引入一个评估“不准确”程度的衡量角色,也就是损失(loss)函数,它是衡量模型估算值和真实值差距的标准,损失函数越小,则模型的估算值和真实值的察觉越小,而我们的根本目的,就是降低这个损失函数。让刚刚的房子特征的模型估算值,逼近 100 万的估算结果。
(3)模型调整
通过梯度下降和反向传播,计算出朝着降低损失函数的方向调整权重参数。举一个不恰当的比喻,我们给面积增加一些权重,然后给房子朝向减少一些权重(实际计算方式,并非针对单个个例特征的调整),然后损失函数就变小了。
(4)循环迭代
调整了模型的权重之后,就可以又重新取一批新的样本数据,重复前面的步骤,经过几十万次甚至更多的训练次数,最终估算模型的估算值逼近了真实值结果,这个模型的则是我们要的“函数”。
为了让大家更容易理解和直观,采用的例子比较粗略,并且讲述深度学习模型的训练过程,中间省略了比较多的细节。讲完了原理,那么我们就开始讲讲如何学习和搭建 demo。
三、深度学习环境搭建
在 2 个月前,人工智能对我来说,只是一个高大上的概念。但是,经过一个多月的业余时间的认真学习,我发现还是能够学到一些东西,并且跑一些 demo 和应用出来的。
1. 学习的提前准备
(1)部分数学内容的复习,高中数学、概率、线性代数等部分内容。(累计花费了 10 个小时,挑了关键的点看了下,其实还是不太够,只能让自己看公式的时候,相对没有那么懵)
(2)Python 基础语法学习。(花费了 3 个小时左右,我以前从未写过 Python,因为后面 Google 的 TensorFlow 框架的使用是基于 Python 的)
(3)Google 的 TensorFlow 深度学习开源框架。(花费了 10 多个小时去看)
数学基础好或者前期先不关注原理的同学,数学部分不看也可以开始做,全凭个人选择。
2. Google 的 TensorFlow 开源深度学习框架
深度学习框架,我们可以粗略的理解为是一个“数学函数”集合和 AI 训练学习的执行框架。通过它,我们能够更好的将 AI 的模型运行和维护起来。
深度学习的框架有各种各样的版本(Caffe、Torch、Theano 等等),我只接触了 Google 的 TensorFlow,因此,后面的内容都是基于 TensorFlow 展开的,它的详细介绍这里不展开讲述,建议直接进入官网查看。非常令人庆幸的是 TensorFlow 比较早就有中文社区了,尽管里面的内容有一点老,搭建环境方面有一些坑,但是已经属于为数不多的中文文档了,大家且看且珍惜。
TensorFlow 的中文社区:
TensorFlow 的英文社区:
3. TensorFlow 环境搭建
环境搭建本身并不复杂,主要解决相关的依赖。但是,基础库的依赖可以带来很多问题,因此,建议尽量一步到位,会简单很多。
(1)操作系统
我搭建环境使用的机器是腾讯云上的机器,软件环境如下:
操作系统:CentOS 7.2 64 位(GCC 4.8.5)
因为这个框架依赖于 python2.7 和 glibc 2.17。比较旧的版本的 CentOS 一般都是 python2.6 以及版本比较低的 glibc,会产生比较的多基础库依赖问题。而且,glibc 作为 Linux 的底层库,牵一发动全身,直接对它升级是比较复杂,很可能会带来更多的环境异常问题。
(2)软件环境
我目前安装的 Python 版本是 python-2.7.5,建议可以采用 yum install python 的方式安装相关的原来软件。然后,再安装 python 内的组件包管理器 pip,安装好 pip 之后,接下来的其他软件的安装就相对比较简单了。
例如安装 TensorFlow,可通过如下一句命令完成(它会自动帮忙解决一些库依赖问题):
pip install -U tensorflow
这里需要特别注意的是,不要按照 TensorFlow 的中文社区的指引去安装,因为它会安装一个非常老的版本(0.5.0),用这个版本跑很多 demo 都会遇到问题的。而实际上,目前通过上述提供的命令安装,是 tensorflow (1.0.0)的版本了。
Python(2.7.5)下的其他需要安装的关键组件:
tensorflow (0.12.1),深度学习的核心框架
image (1.5.5),图像处理相关,部分例子会用到
PIL (1.1.7),图像处理相关,部分例子会用到
除此之后,当然还有另外的一些依赖组件,通过 pip list 命令可以查看我们安装的 python 组件:
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)
按照上述提供的来搭建系统,可以规避不少的环境问题。
搭建环境的过程中,我遇到不少问题。例如:在跑官方的例子时的某个报错,AttributeError: ‘module’ object has no attribute ‘gfile’,就是因为安装的 TensorFlow 的版本比较老,缺少 gfile 模块导致的。而且,还有各种各样的。(不要问我是怎么知道的,说多了都是泪啊~)
更详细的安装说明:
https://www.tensorflow.org/install/install_linux
(3)TensorFlow 环境测试运行
测试是否安装成功,可以采用官方的提供的一个短小的例子,demo 生成了一些三维数据, 然后用一个平面拟合它们(官网的例子采用的初始化变量的函数是 initialize_all_variables,该函数在新版本里已经被废弃了):
运行的结果类似如下:
经过 200 次的训练,模型的参数逐渐逼近最佳拟合的结果(W: [[0.100 0.200]], b: [0.300]),另外,我们也可以从代码的“风格”中,了解到框架样本训练的基本运行方式。虽然,官方的教程后续会涉及越来越多更复杂的例子,但从整体上看,也是类似的模式。
步骤划分:
准备数据:获得有标签的样本数据(带标签的训练数据称为有监督学习);
设置模型:先构建好需要使用的训练模型,可供选择的机器学习方法其实也挺多的,换而言之就是一堆数学函数的集合;
损失函数和优化方式:衡量模型计算结果和真实标签值的差距;
真实训练运算:训练之前构造好的模型,让程序通过循环训练和学习,获得最终我们需要的结果“参数”;
验证结果:采用之前模型没有训练过的测试集数据,去验证模型的准确率。
其中,TensorFlow 为了基于 python 实现高效的数学计算,通常会使用到一些基础的函数库,例如 Numpy(采用外部底层语言实现),但是,从外部计算切回到 python 也是存在开销的,尤其是在几万几十万次的训练过程。因此,Tensorflow 不单独地运行单一的函数计算,而是先用图描述一系列可交互的计算操作流程,然后全部一次性提交到外部运行(在其他机器学习的库里,也是类似的实现)。所以,上述流程图中,蓝色部分都只是设置了“计算操作流程”,而绿色部分开始才是真正的提交数据给到底层库进行实际运算,而且,每次训练一般是批量执行一批数据的。
四、经典入门 demo:识别手写数字(MNIST)
常规的编程入门有“Hello world”程序,而深度学习的入门程序则是 MNIST,一个识别 28*28 像素的图片中的手写数字的程序。
MNIST 的数据和官网:
http://yann.lecun.com/exdb/mnist/
深度学习的内容,其背后会涉及比较多的数学原理,作为一个初学者,受限于我个人的数学和技术水平,也许并不足以准确讲述相关的数学原理,因此,本文会更多的关注“应用层面”,不对背后的数学原理进行展开,感谢谅解。
1. 加载数据
程序执行的第一步当然是加载数据,根据我们之前获得的数据集主要包括两部分:60000 的训练数据集(mnist.train)和 10000 的测试数据集(mnist.test)。里面每一行,是一个 28 * 28=784 的数组,数组的本质就是将 28 * 28 像素的图片,转化成对应的像素点阵。
例如手写字 1 的图片转换出来的对应矩阵表示如下:
之前我们经常听说,图片方面的深度学习需要大量的计算能力,甚至需要采用昂贵、专业的 GPU(Nvidia 的 GPU),从上述转化的案例我们就已经可以获得一些答案了。一张 784 像素的图片,对学习模型来说,就有 784 个特征,而我们实际的相片和图片动辄几十万、百万级别,则对应的基础特征数也是这个数量级,基于这样数量级的数组进行大规模运算,没有强大的计算能力支持,确实寸步难行。当然,这个入门的 MNIST 的 demo 还是可以比较快速的跑完。
Demo 中的关键代码(读取并且加载数据到数组对象中,方便后面使用):
2. 构建模型
MNIST 的每一张图片都表示一个数字,从 0 到 9。而模型最终期望获得的是:给定一张图片,获得代表每个数字的概率。比如说,模型可能推测一张数字 9 的图片代表数字 9 的概率是 80%但是判断它是 8 的概率是 5%(因为 8 和 9 都有上半部分的小圆),然后给予它代表其他数字的概率更小的值。
MNIST 的入门例子,采用的是 softmax 回归(softmax regression),softmax 模型可以用来给不同的对象分配概率。
为了得到一张给定图片属于某个特定数字类的证据(evidence),我们对图片的 784 个特征(点阵里的各个像素值)进行加权求和。如果某个特征(像素值)具有很强的证据说明这张图片不属于该类,那么相应的权重值为负数,相反如果某个特征(像素值)拥有有利的证据支持这张图片属于这个类,那么权重值是正数。类似前面提到的房价估算例子,对每一个像素点作出了一个权重分配。
假设我们获得一张图片,需要计算它是 8 的概率,转化成数学公式则如下:
公式中的 i 代表需要预测的数字(8),代表预测数字为 8 的情况下,784 个特征的不同权重值,代表 8 的偏置量(bias),X 则是该图片 784 个特征的值。通过上述计算,我们则可以获得证明该图片是 8 的证据(evidence)的总和,softmax 函数可以把这些证据转换成概率 y。(softmax 的数学原理,辛苦各位查询相关资料哈)
将前面的过程概括成一张图(来自官方)则如下:
不同的特征 x 和对应不同数字的权重进行相乘和求和,则获得在各个数字的分布概率,取概率最大的值,则认为是我们的图片预测结果。
将上述过程写成一个等式,则如下:
该等式在矩阵乘法里可以非常简单地表示,则等价为:
不展开里面的具体数值,则可以简化为:
如果我们对线性代数中矩阵相关内容有适当学习,其实,就会明白矩阵表达在一些问题上,更易于理解。如果对矩阵内容不太记得了,也没有关系,后面我会附加上线性代数的视频。
虽然前面讲述了这么多,其实关键代码就四行:
上述代码都是类似变量占位符,先设置好模型计算方式,在真实训练流程中,需要批量读取源数据,不断给它们填充数据,模型计算才会真实跑起来。tf.zeros 则表示,先给它们统一赋值为 0 占位。X 数据是从数据文件中读取的,而 w、b 是在训练过程中不断变化和更新的,y 则是基于前面的数据进行计算得到。
3. 损失函数和优化设置
为了训练我们的模型,我们首先需要定义一个指标来衡量这个模型是好还是坏。这个指标称为成本(cost)或损失(loss),然后尽量最小化这个指标。简单的说,就是我们需要最小化 loss 的值,loss 的值越小,则我们的模型越逼近标签的真实结果。
Demo 中使用的损失函数是“交叉熵”(cross-entropy),它的公式如下:
y 是我们预测的概率分布, y’ 是实际的分布(我们输入的),交叉熵是用来衡量我们的预测结果的不准确性。TensorFlow 拥有一张描述各个计算单元的图,也就是整个模型的计算流程,它可以自动地使用反向传播算法(backpropagation algorithm),来确定我们的权重等变量是如何影响我们想要最小化的那个 loss 值的。然后,TensorFlow 会用我们设定好的优化算法来不断修改变量以降低 loss 值。
其中,demo 采用梯度下降算法(gradient descent algorithm)以 0.01 的学习速率最小化交叉熵。梯度下降算法是一个简单的学习过程,TensorFlow 只需将每个变量一点点地往使 loss 值不断降低的方向更新。
对应的关键代码如下:
备注内容:
交叉熵:http://colah.github.io/posts/2015-09-Visual-Information/
反向传播:http://colah.github.io/posts/2015-08-Backprop/
在代码中会看见 one-hot vector 的概念和变量名,其实这个是个非常简单的东西,就是设置一个 10 个元素的数组,其中只有一个是 1,其他都是 0,以此表示数字的标签结果。
例如表示数字 3 的标签值:
[0,0,0,1,0,0,0,0,0,0]
4. 训练运算和模型准确度测试
通过前面的实现,我们已经设置好了整个模型的计算“流程图”,它们都成为 TensorFlow 框架的一部分。于是,我们就可以启动我们的训练程序,下面的代码的含义是,循环训练我们的模型 500 次,每次批量取 50 个训练样本。
其训练过程,其实就是 TensorFlow 框架的启动训练过程,在这个过程中,python 批量地将数据交给底层库进行处理。
我在官方的 demo 里追加了两行代码,每隔 50 次则额外计算一次当前模型的识别准确率。它并非必要的代码,仅仅用于方便观察整个模型的识别准确率逐步变化的过程。
当然,里面涉及的 accuracy(预测准确率)等变量,需要在前面的地方定义占位:
当我们训练完毕,则到了验证我们的模型准确率的时候,和前面相同:
我的 demo 跑出来的结果如下(softmax 回归的例子运行速度还是比较快的),当前的准确率是 0.9252:
5. 实时查看参数的数值的方法
刚开始跑官方的 demo 的时候,我们总想将相关变量的值打印出来看看,是怎样一种格式和状态。从 demo 的代码中,我们可以看见很多的 Tensor 变量对象,而实际上这些变量对象都是无法直接输出查看,粗略地理解,有些只是占位符,直接输出的话,会获得类似如下的一个对象:
Tensor(“Equal:0”, shape=(?,), dtype=bool)
既然它是占位符,那么我们就必须喂一些数据给它,它才能将真实内容展示出来。因此,正确的方法是,在打印时通常需要加上当前的输入数据给它。
例如,查看 y 的概率数据:
print(sess.run(y, feed_dict={x: batch_xs, y_: batch_ys}))
部分非占位符的变量还可以这样输出来:
print(W.eval())
总的来说,92%的识别准确率是比较令人失望,因此,官方的 MNIST 其实也有多种模型的不同版本,其中比较适合图片处理的 CNN(卷积神经网络)的版本,可以获得 99%以上的准确率,当然,它的执行耗时也是比较长的。
(备注:cnn_mnist.py 就是卷积神经网络版本的,后面有附带微云网盘的下载 url)
前馈神经网络(feed-forward neural network)版本的 MNIST,可达到 97%:
分享在微云上的数据和源码:
(备注:国外网站下载都比较慢,我这份下载相对会快一些,在环境已经搭建完毕的情况下,执行里面的 run.py 即可)
五、和业务场景结合的 demo:预测用户是否是超级会员身份
根据前面的内容,我们对上述基于 softmax 只是三层(输入、处理、输出)的神经网络模型已经比较熟悉,那么,这个模型是否可以应用到我们具体的业务场景中,其中的难度大吗?为了验证这一点,我拿了一些现网的数据来做了这个试验。
1. 数据准备
我将一个现网的电影票活动的用户参与数据,包括点击过哪些按钮、手机平台、IP 地址、参与时间等信息抓取了出来。其实这些数据当中是隐含了用户的身份信息的,例如,某些礼包的必须是超级会员身份才能领取,如果这个按钮用户点击领取成功,则可以证明该用户的身份肯定是超级会员身份。当然,我只是将这些不知道相不相关的数据特征直观的整理出来,作为我们的样本数据,然后对应的标签为超级会员身份。
用于训练的样本数据格式如下:
第一列是 QQ 号码,只做认知标识的,第二列表示是否超级会员身份,作为训练的标签值,后面的就是 IP 地址,平台标志位以及参与活动的参与记录(0 是未成功参与,1 表示成功参与)。则获得一个拥有 11 个特征的数组(经过一些转化和映射,将特别大的数变小):
[0.9166666666666666, 0.4392156862745098, 0.984313725490196, 0.7411764705882353, 0.2196078431372549, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]
对应的是否是超级数据格式如下,作为监督学习的标签:
超级会员:[0, 1]
非超级会员:[1, 0]
这里需要专门解释下,在实际应用中需要做数据转换的原因。一方面,将这些数据做一个映射转化,有助于简化数据模型。另一方面,是为了规避 NaN 的问题,当数值过大,在一些数学指数和除法的浮点数运算中,有可能得到一个无穷大的数值,或者其他溢出的情形,在 Python 里会变为 NaN 类型,这个类型会破坏掉后续全部计算结果,导致计算异常。
例如下图,就是特征数值过大,在训练过程中,导致中间某些参数累计越来越大,最终导致产生 NaN 值,后续的计算结果全部被破坏掉:
而导致 NaN 的原因在复杂的数学计算里,会产生无穷大或者无穷小。例如,在我们的这个 demo 中,产生 NaN 的原因,主要是因为 softmax 的计算导致。
RuntimeWarning: divide by zero encountered in log
刚开始做实际的业务应用,就发现经常跑出极奇怪异的结果(遇到 NaN 问题,我发现程序也能继续走下去),几经排查才发现是 NAN 值问题,是非常令人沮丧的。当然,经过仔细分析问题,发现也并非没有排查的方式。因为,NaN 值是个奇特的类型,可以采用下述编码方式 NaN != NaN 来检测自己的训练过程中,是否出现的 NaN。
关键程序代码如下:
我采用上述方法,非常顺利地找到自己的深度学习程序,在学习到哪一批数据时产生的 NaN。因此,很多原始数据我们都会做一个除以某个值,让数值变小的操作。例如官方的 MNIST 也是这样做的,将 256 的像素颜色的数值统一除以 255,让它们都变成一个小于 1 的浮点数。
MNIST 在处理原始图片像素特征数据时,也对特征数据进行了变小处理:
处理 NaN 问题更专业的方法,就是对输入数据进行归一化处理(min-max 标准化或 Z-score),通过数学方法,让输入参数控制在一个预期内的范围内。
2. 执行结果
我准备的训练集(6700)和测试集(1000)数据并不多,不过,超级会员身份的预测准确率最终可以达到 87%。虽然,预测准确率是不高,这个可能和我的训练集数据比较少有关系,不过,整个模型也没有花费多少时间,从整理数据、编码、训练到最终跑出结果,只用了 2 个晚上的时间。
下图是两个实际的测试例子,例如,该模型预测第一个 QQ 用户有 82%的概率是非超级会员用户,17.9%的概率为超级会员用户(该预测是准确的)。
通过上面的这个例子,我们会发觉其实对于某些比较简单的场景下应用,我们是可以比较容易就实现的。
六、其他模型
1. CIFAR-10 识别图片分类的 demo(官方)
CIFAR-10 数据集的分类是机器学习中一个公开的基准测试问题,它任务是对一组 32x32RGB 的图像进行分类,这些图像涵盖了 10 个类别:飞机, 汽车, 鸟, 猫, 鹿, 狗, 青蛙, 马, 船和卡车。
这也是官方的重要 demo 之一。
更详细的介绍内容:
http://www.cs.toronto.edu/~kriz/cifar.html
http://tensorfly.cn/tfdoc/tutorials/deep_cnn.html
该例子执行的过程比较长,需要耐心等待。
我在机器上的执行过程和结果:
cifar10_train.py 用于训练:
cifar10_eval.py 用于检验结果:
识别率不高是因为该官方模型的识别率本来就不高:
另外,官方的例子我首次在 1 月 5 日跑的时候,还是有一些小问题的,无法跑起来(最新的官方可能已经修正),建议可以直接使用我放到微云上的版本(代码里面的 log 和读取文件的路径,需要调整一下)。
微云盘里,不含训练集和测试集的图片数据,但是,程序如果检测到这些图片不存在,会自行下载:
2. 是否大于 5 岁的测试 demo
为了检验 softma 回归模型是否能够学习到一些我自己设定好的规则,我做了一个小 demo 来测试。我通过随机数生成的方式构造了一系列的数据,让前面的 softmax 回归模型去学习,最终看看模型能否通过训练集的学习,最终 100%预测这个样本数据是否大于 5 岁。
模型和数据本身都比较简单,构造的数据的方式:
我随机构造一个只有 2 个特征纬度的样本数据,[year, 1],其中 year 随机取值 0-10,数字 1 是放进去作为干扰。
如果 year 大于 5 岁,则标签设置为:[0, 0, 1];
否则,标签设置为:[0, 1, 0]。
生成了 6000 条假训练集去训练该模型,最终它能做到 100%成功预测准确:
微云下载(源码下载):
3. 基于 RNN 的古诗学习
最开头的 AI 写古诗,非常令人感到惊艳,那个 demo 是美国的一个研究者做出来的,能够根据主题生成不能的古诗,而且古诗的质量还比较高。于是,我也尝试在自己的机器上也跑一个能够写古诗的模型,后来我找到的是一个基于 RNN 的模型。RNN 循环神经网络(Recurrent Neural Networks),是非常常用的深度学习模型之一。我基于一个外部的 demo,进行一些调整后跑起一个能够学习古诗和写古诗的比较简单的程序。
执行写诗(让它写了五首):
每从西帝望中庭,何日春心似客中。春气未辞丹岸色,一竿春气落寒风。闲中独自忘人思,却把烟霞是远溪。此去更迟迟日晚,满头春景满园香。
韩字人何用,无由问我还。关中犹可去,山色自依稀。海路临河树,春晴入白蘋。闲山如有酒,日暮夜凉云。若是文陵郡,千峰下翠萝。还应不敢恋,终复一为邻。
佳情无事是明华,不道无情事易伤。今去别时逢旧去,夜离归计自如秋。此中欲醉应惆怅,更欲何堪共有时。更待离思在不极,长沙半日梦吟声。
回塘一岸绿江边,日照烟波入岸中。天际暮山千片雪,山禽飞绕九潭烟。谁当不是归南曲,莫叹何人待客行。莫羡此乡心似梦,空床寂历路斜斜。
饯酒何言住,相邀见日年。一来知道外,谁忆谢平心。
不与风流少几多,不因高卧在前山。世前每忆江头雪,酒倒寒光一点流。长向东风与明酒,一时何用似无言。莫道长有何人见,为谢南山一日中。
该模型比较简单,写诗的水平不如最前面我介绍的美国研究者 demo,但是,所采用的基本方法应该是类似的,只是他做的更为复杂。
另外,这是一个通用模型,可以学习不同的内容(古诗、现代诗、宋词或者英文诗等),就可以生成对应的结果。
七、深度学习的入门学习体会
人工智能和深度学习技术并不神秘,更像是一个新型的工具,通过喂数据给它,然后,它能发现这些数据背后的规律,并为我们所用。
数学基础比较重要,这样有助于理解模型背后的数学原理,不过,从纯应用角度来说,并不一定需要完全掌握数学,也可以提前开始做一些尝试和学习。
我深深地感到计算资源非常缺乏,每次调整程序的参数或训练数据后,跑完一次训练集经常要很多个小时,部分场景不跑多一些训练集数据,看不出差别,例如写诗的案例。个人感觉,这个是制约 AI 发展的重要问题,它直接让程序的“调试”效率非常低下。
中文文档比较少,英文文档也不多,开源社区一直在快速更新,文档的内容过时也比较快。因此,入门学习时遇到的问题会比较多,并且缺乏成型的文档。
八、小结
我不知道人工智能的时代是否真的会来临,也不知道它将要走向何方,但是,毫无疑问,它是一种全新的技术思维模式。更好的探索和学习这种新技术,然后在业务应用场景寻求结合点,最终达到帮助我们的业务获得更好的成果,一直以来,就是我们工程师的核心宗旨。另一方面,对发展有重大推动作用的新技术,通常会快速的发展并且走向普及,就如同我们的编程一样,因此,人人都可以做深度学习应用,并非只是一句噱头。
作者介绍:
唐云兵,在腾讯从事后台开发 6 年多,手 Q 个性装扮和动漫后台 leader。喜欢了解一些后台优秀组件设计,喜欢徒步和羽毛球。最近学习和实践个性推荐。
本文转载自公众号小时光茶舍(ID:gh_7322a0f167b5)。
原文链接:
https://mp.weixin.qq.com/s/Y1x0GUkfRbWEz6YsJrMTAQ
评论