返回 登录
0

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

阅读12133

【在线直播课】《TensorFlow实战》作者、PPmoney大数据算法总监黄文坚老师即将开课:《使用TensorFlow创建可自动玩游戏的DQN模型》,本次课程集中讲解Deep-Q Network及其TensorFlow的实现。点击报名

人工智能和新科技革命

2017年围棋界发生了一件比较重要事,Master(Alphago)以60连胜横扫天下,击败各路世界冠军,人工智能以气势如虹的姿态出现在我们人类的面前。围棋曾经一度被称为“人类智慧的堡垒”,如今,这座堡垒也随之成为过去。从2016年三月份AlphaGo击败李世石开始,AI全面进入我们大众的视野,对于它的讨论变得更为火热起来,整个业界普遍认为,它很可能带来下一次科技革命,并且,在未来可预见的10多年里,深刻得改变我们的生活。

图片描述

其实,AI除了可以做我们熟知的人脸、语音等识别之外,它可以做蛮多有趣的事情。
例如,让AI学习大量古诗之后写古诗,并且可以写出质量非常不错的古诗。

图片描述

又或者,将两部设计造型不同的汽车进行融合,形成全新一种设计风格的新汽车造型。

图片描述

还有,之前大家在朋友圈里可能看过的,将相片转换成对应的艺术风格的画作。

图片描述

当前,人工智能已经在图像、语音等多个领域的技术上,取得了全面的突破。与此同时,另外一个问题随之而来,如果这一轮的AI浪潮真的将会掀起新的科技革命,那么在可预见的未来,我们整个互联网都将发生翻天覆地的变化,深刻影响我们的生活。那么作为程序员和工程师的我们,又应该以何种态度和方式应对这场时代洪流的冲击呢?

在回答这个问题之前,我们先一起看看上一轮由计算机信息技术引领的科技革命中,过去30多年中国程序员的角色变化:

图片描述

通过上图可以简总结:编程技术在不断地发展并且走向普及,从最开始掌握在科学家和专家学者手中的技能,逐渐发展为一门大众技能。换而言之,我们公司内很多资深的工程师,如果带着今天对编程和计算机的理解和理念回到1980年,那么他无疑就是那个时代的计算机专家。

如果这一轮AI浪潮真的会带来新的一轮科技革命,那么我们相信,它也会遵循类似的发展轨迹,逐步发展和走向普及。如果基于这个理解,或许,我们可以通过积极学习,争取成为第一代AI工程师。

深度学习技术

这一轮AI的技术突破,主要源于深度学习技术,而关于AI和深度学习的发展历史我们这里不重复讲述,可自行查阅。我用了一个多月的业务时间,去了解和学习了深度学习技术,在这里,我尝试以一名程序员的视角,以尽量容易让大家理解的方式一起探讨下深度学习的原理,尽管,受限于我个人的技术水平和掌握程度,未必完全准确。

人的智能和神经元

人类智能最重要的部分是大脑,大脑虽然复杂,它的组成单元却是相对简单的,大脑皮层以及整个神经系统,是由神经元细胞组成的。而一个神经元细胞,由树突和轴突组成,它们分别代表输入和输出。连在细胞膜上的分叉结构叫树突,是输入,那根长长的“尾巴”叫轴突,是输出。神经元输出的有电信号和化学信号,最主要的是沿着轴突细胞膜表面传播的一个电脉冲。忽略掉各种细节,神经元,就是一个积累了足够的输入,就产生一次输出(兴奋)的相对简单的装置。

图片描述

树突和轴突都有大量的分支,轴突的末端通常连接到其他细胞的树突上,连接点上是一个叫“突触”的结构。一个神经元的输出通过突触传递给成千上万个下游的神经元,神经元可以调整突触的结合强度,并且,有的突触是促进下游细胞的兴奋,有的是则是抑制。一个神经元有成千上万个上游神经元,积累它们的输入,产生输出。

图片描述

人脑有1000亿个神经元,1000万亿个突触,它们组成人脑中庞大的神经网络,最终产生的结果即是人的智能。

人工神经元和神经网络

一个神经元的结构相对来说是比较简单的,于是,科学家们就思考,我们的AI是否可以从中获得借鉴?神经元接受激励,输出一个响应的方式,同计算机中的输入输出非常类似,看起来简直就是量身定做的,刚好可以用一个函数来模拟。

图片描述

通过借鉴和参考神经元的机制,科学家们模拟出了人工神经元和人工神经网络。当然,通过上述这个抽象的描述和图,比较难让大家理解它的机制和原理。我们以“房屋价格测算”作为例子,一起来看看:

一套房子的价格,会受到很多因素的影响,例如地段、朝向、房龄、面积、银行利率等等,这些因素如果细分,可能会有几十个。一般在深度学习模型里,这些影响结果的因素我们称之为特征。我们先假设一种极端的场景,例如影响价格的特征只有一种,就是房子面积。于是我们收集一批相关的数据,例如,50平米50万、93平米95万等一系列样本数据,如果将这些样本数据放到而为坐标里看,则如下图:

图片描述

然后,正如我们前面所说的,我们尝试用一个“函数”去拟合这个输入(面积x)和输出(价格y),简而言之,我们就是要通过一条直线或者曲线将这些点“拟合”起来。
假设情况也比较极端,这些点刚好可以用一条“直线”拟合(真实情况通常不会是直线),如下图:

图片描述

那么我们的函数是一个一次元方程f(x) = ax +b,当然,如果是曲线的话,我们得到的将是多次元方程。我们获得这个f(x) = ax +b的函数之后,接下来就可以做房价“预测”,例如,我们可以计算一个我们从未看见的面积案例81.5平方米,它究竟是多少钱?

这个新的样本案例,可以通过直线找到对应的点(黄色的点),如图下:

图片描述

粗略的理解,上面就是AI的概括性的运作方式。这一切似乎显得过于简单了?当然不会,因为,我们前面提到,影响房价其实远不止一个特征,而是有几十个,这样问题就比较复杂了,接下来,这里则要继续介绍深度学习模型的训练方式。这部分内容相对复杂一点,我尽量以工程师的视角来做一个粗略而简单的阐述。

深度学习模型的训练方式

当有好几十个特征共同影响价格的时候,自然就会涉及权重分配的问题,例如有一些对房价是主要正权重的,例如地段、面积等,也有一些是负权重的,例如房龄等。

(1)初始化权重计算

那么,第一个步其实是给这些特征加一个权重值,但是,最开始我们根本不知道这些权重值是多少?怎么办呢?不管那么多了,先给它们随机赋值吧。随机赋值,最终计算出来的估算房价肯定是不准确的,例如,它可能将价值100万的房子,计算成了10万。

(2)损失函数

因为现在模型的估值和实际估值差距比较大,于是,我们需要引入一个评估“不准确”程度的衡量角色,也就是损失(loss)函数,它是衡量模型估算值和真实值差距的标准,损失函数越小,则模型的估算值和真实值的察觉越小,而我们的根本目的,就是降低这个损失函数。让刚刚的房子特征的模型估算值,逼近100万的估算结果。

(3)模型调整

通过梯度下降和反向传播,计算出朝着降低损失函数的方向调整权重参数。举一个不恰当的比喻,我们给面积增加一些权重,然后给房子朝向减少一些权重(实际计算方式,并非针对单个个例特征的调整),然后损失函数就变小了。

(4)循环迭代

调整了模型的权重之后,就可以又重新取一批新的样本数据,重复前面的步骤,经过几十万次甚至更多的训练次数,最终估算模型的估算值逼近了真实值结果,这个模型的则是我们要的“函数”。

图片描述

为了让大家更容易理解和直观,采用的例子比较粗略,并且讲述深度学习模型的训练过程,中间省略了比较多的细节。讲完了原理,那么我们就开始讲讲如何学习和搭建demo。

深度学习环境搭建

在2个月前,人工智能对我来说,只是一个高大上的概念。但是,经过一个多月的业余时间的认真学习,我发现还是能够学到一些东西,并且跑一些demo和应用出来的。

学习的提前准备

(1)部分数学内容的复习,高中数学、概率、线性代数等部分内容。(累计花费了10个小时,,挑了关键的点看了下,其实还是不太够,只能让自己看公式的时候,相对没有那么懵)
(2)Python基础语法学习。(花费了3个小时左右,我以前从未写过Python,因为后面Google的TensorFlow框架的使用是基于Python的)
(3)Google的TensorFlow深度学习开源框架。(花费了10多个小时去看)
数学基础好或者前期先不关注原理的同学,数学部分不看也可以开始做,全凭个人选择。

Google的TensorFlow开源深度学习框架

深度学习框架,我们可以粗略的理解为是一个“数学函数”集合和AI训练学习的执行框架。通过它,我们能够更好的将AI的模型运行和维护起来。

深度学习的框架有各种各样的版本(Caffe、Torch、Theano等等),我只接触了Google的TensorFlow,因此,后面的内容都是基于TensorFlow展开的,它的详细介绍这里不展开讲述,建议直接进入官网查看。非常令人庆幸的是TensorFlow比较早就有中文社区了,尽管里面的内容有一点老,搭建环境方面有一些坑,但是已经属于为数不多的中文文档了,大家且看且珍惜。

TensorFlow的中文社区:http://www.tensorfly.cn/
TensorFlow的英文社区:https://www.tensorflow.org/

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,该函数在新版本里已经被废弃了):

#!/usr/bin/python
#coding=utf-8

import tensorflow as tf
import numpy as 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 step in xrange(0, 201):
    sess.run(train)
    if step % 20 == 0:
        print step, sess.run(W), sess.run(b)

# 得到最佳拟合结果 W: [[0.100  0.200]], b: [0.300]

运行的结果类似如下:

图片描述

经过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/

深度学习的内容,其背后会涉及比较多的数学原理,作为一个初学者,受限于我个人的数学和技术水平,也许并不足以准确讲述相关的数学原理,因此,本文会更多的关注“应用层面”,不对背后的数学原理进行展开,感谢谅解。

加载数据

程序执行的第一步当然是加载数据,根据我们之前获得的数据集主要包括两部分:60000的训练数据集(mnist.train)和10000的测试数据集(mnist.test)。里面每一行,是一个28*28=784的数组,数组的本质就是将28*28像素的图片,转化成对应的像素点阵。

例如手写字1的图片转换出来的对应矩阵表示如下:

图片描述

之前我们经常听说,图片方面的深度学习需要大量的计算能力,甚至需要采用昂贵、专业的GPU(Nvidia的GPU),从上述转化的案例我们就已经可以获得一些答案了。一张784像素的图片,对学习模型来说,就有784个特征,而我们实际的相片和图片动辄几十万、百万级别,则对应的基础特征数也是这个数量级,基于这样数量级的数组进行大规模运算,没有强大的计算能力支持,确实寸步难行。当然,这个入门的MNIST的demo还是可以比较快速的跑完。

Demo中的关键代码(读取并且加载数据到数组对象中,方便后面使用):

图片描述

构建模型

MNIST的每一张图片都表示一个数字,从0到9。而模型最终期望获得的是:给定一张图片,获得代表每个数字的概率。比如说,模型可能推测一张数字9的图片代表数字9的概率是80%但是判断它是8的概率是5%(因为8和9都有上半部分的小圆),然后给予它代表其他数字的概率更小的值。

图片描述

MNIST的入门例子,采用的是softmax回归(softmax regression),softmax模型可以用来给不同的对象分配概率。

为了得到一张给定图片属于某个特定数字类的证据(evidence),我们对图片的784个特征(点阵里的各个像素值)进行加权求和。如果某个特征(像素值)具有很强的证据说明这张图片不属于该类,那么相应的权重值为负数,相反如果某个特征(像素值)拥有有利的证据支持这张图片属于这个类,那么权重值是正数。类似前面提到的房价估算例子,对每一个像素点作出了一个权重分配。

假设我们获得一张图片,需要计算它是8的概率,转化成数学公式则如下:

图片描述

公式中的i代表需要预测的数字(8),Wi,j代表预测数字为8的情况下,784个特征的不同权重值,bi代表8的偏置量(bias),X则是该图片784个特征的值。通过上述计算,我们则可以获得证明该图片是8的证据(evidence)的总和,softmax函数可以把这些证据转换成概率 y。(softmax的数学原理,辛苦各位查询相关资料哈)

将前面的过程概括成一张图(来自官方)则如下:

图片描述

不同的特征x和对应不同数字的权重进行相乘和求和,则获得在各个数字的分布概率,取概率最大的值,则认为是我们的图片预测结果。

将上述过程写成一个等式,则如下:

图片描述

该等式在矩阵乘法里可以非常简单地表示,则等价为:

图片描述

不展开里面的具体数值,则可以简化为:

图片描述

如果我们对线性代数中矩阵相关内容有适当学习,其实,就会明白矩阵表达在一些问题上,更易于理解。如果对矩阵内容不太记得了,也没有关系,后面我会附加上线性代数的视频。

虽然前面讲述了这么多,其实关键代码就四行:

图片描述

上述代码都是类似变量占位符,先设置好模型计算方式,在真实训练流程中,需要批量读取源数据,不断给它们填充数据,模型计算才会真实跑起来。tf.zeros则表示,先给它们统一赋值为0占位。X数据是从数据文件中读取的,而w、b是在训练过程中不断变化和更新的,y则是基于前面的数据进行计算得到。

损失函数和优化设置

为了训练我们的模型,我们首先需要定义一个指标来衡量这个模型是好还是坏。这个指标称为成本(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]

训练运算和模型准确度测试

通过前面的实现,我们已经设置好了整个模型的计算“流程图”,它们都成为TensorFlow框架的一部分。于是,我们就可以启动我们的训练程序,下面的代码的含义是,循环训练我们的模型500次,每次批量取50个训练样本。

图片描述

其训练过程,其实就是TensorFlow框架的启动训练过程,在这个过程中,python批量地将数据交给底层库进行处理。

我在官方的demo里追加了两行代码,每隔50次则额外计算一次当前模型的识别准确率。它并非必要的代码,仅仅用于方便观察整个模型的识别准确率逐步变化的过程。

图片描述

当然,里面涉及的accuracy(预测准确率)等变量,需要在前面的地方定义占位:

图片描述

当我们训练完毕,则到了验证我们的模型准确率的时候,和前面相同:

图片描述

我的demo跑出来的结果如下(softmax回归的例子运行速度还是比较快的),当前的准确率是0.9252:

图片描述

实时查看参数的数值的方法

刚开始跑官方的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%:

图片描述

分享在微云上的数据和源码:
http://url.cn/44aZOpP
(备注:国外网站下载都比较慢,我这份下载相对会快一些,在环境已经搭建完毕的情况下,执行里面的run.py即可)

和业务场景结合的demo:预测用户是否是超级会员身份

根据前面的内容,我们对上述基于softmax只是三层(输入、处理、输出)的神经网络模型已经比较熟悉,那么,这个模型是否可以应用到我们具体的业务场景中,其中的难度大吗?为了验证这一点,我拿了一些现网的数据来做了这个试验。

数据准备

图片描述

我将一个现网的电影票活动的用户参与数据,包括点击过哪些按钮、手机平台、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),通过数学方法,让输入参数控制在一个预期内的范围内。

执行结果

我准备的训练集(6700)和测试集(1000)数据并不多,不过,超级会员身份的预测准确率最终可以达到87%。虽然,预测准确率是不高,这个可能和我的训练集数据比较少有关系,不过,整个模型也没有花费多少时间,从整理数据、编码、训练到最终跑出结果,只用了2个晚上的时间。

图片描述

下图是两个实际的测试例子,例如,该模型预测第一个QQ用户有82%的概率是非超级会员用户,17.9%的概率为超级会员用户(该预测是准确的)。

图片描述

通过上面的这个例子,我们会发觉其实对于某些比较简单的场景下应用,我们是可以比较容易就实现的。

其他模型

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和读取文件的路径,需要调整一下)。
源码下载:http://url.cn/44mRzBh

微云盘里,不含训练集和测试集的图片数据,但是,程序如果检测到这些图片不存在,会自行下载:

图片描述

是否大于5岁的测试demo

为了检验softma回归模型是否能够学习到一些我自己设定好的规则,我做了一个小demo来测试。我通过随机数生成的方式构造了一系列的数据,让前面的softmax回归模型去学习,最终看看模型能否通过训练集的学习,最终100%预测这个样本数据是否大于5岁。

模型和数据本身都比较简单,构造的数据的方式:

我随机构造一个只有2个特征纬度的样本数据,[year, 1],其中year随机取值0-10,数字1是放进去作为干扰。
如果year大于5岁,则标签设置为:[0, 0, 1];
否则,标签设置为:[0, 1, 0]。

生成了6000条假训练集去训练该模型,最终它能做到100%成功预测准确:

图片描述

微云下载(源码下载):http://url.cn/44mKFNK

基于RNN的古诗学习

最开头的AI写古诗,非常令人感到惊艳,那个demo是美国的一个研究者做出来的,能够根据主题生成不能的古诗,而且古诗的质量还比较高。于是,我也尝试在自己的机器上也跑一个能够写古诗的模型,后来我找到的是一个基于RNN的模型。RNN循环神经网络(Recurrent Neural Networks),是非常常用的深度学习模型之一。我基于一个外部的demo,进行一些调整后跑起一个能够学习古诗和写古诗的比较简单的程序。

我的程序默认配置是读取三万首诗(做了一些过滤,将格式有误的或者非五言、七言的过滤掉),执行的结果如下:

图片描述

模型已经学完了3万首古诗:

图片描述

执行写诗(让它写了五首):

每从西帝望中庭,何日春心似客中。春气未辞丹岸色,一竿春气落寒风。闲中独自忘人思,却把烟霞是远溪。此去更迟迟日晚,满头春景满园香。
韩字人何用,无由问我还。关中犹可去,山色自依稀。海路临河树,春晴入白蘋。闲山如有酒,日暮夜凉云。若是文陵郡,千峰下翠萝。还应不敢恋,终复一为邻。
佳情无事是明华,不道无情事易伤。今去别时逢旧去,夜离归计自如秋。此中欲醉应惆怅,更欲何堪共有时。更待离思在不极,长沙半日梦吟声。
回塘一岸绿江边,日照烟波入岸中。天际暮山千片雪,山禽飞绕九潭烟。谁当不是归南曲,莫叹何人待客行。莫羡此乡心似梦,空床寂历路斜斜。
饯酒何言住,相邀见日年。一来知道外,谁忆谢平心。
不与风流少几多,不因高卧在前山。世前每忆江头雪,酒倒寒光一点流。长向东风与明酒,一时何用似无言。莫道长有何人见,为谢南山一日中。

该模型比较简单,写诗的水平不如最前面我介绍的美国研究者demo,但是,所采用的基本方法应该是类似的,只是他做的更为复杂。

另外,这是一个通用模型,可以学习不同的内容(古诗、现代诗、宋词或者英文诗等),就可以生成对应的结果。

深度学习的入门学习体会

  1. 人工智能和深度学习技术并不神秘,更像是一个新型的工具,通过喂数据给它,然后,它能发现这些数据背后的规律,并为我们所用。
  2. 数学基础比较重要,这样有助于理解模型背后的数学原理,不过,从纯应用角度来说,并不一定需要完全掌握数学,也可以提前开始做一些尝试和学习。
  3. 我深深地感到计算资源非常缺乏,每次调整程序的参数或训练数据后,跑完一次训练集经常要很多个小时,部分场景不跑多一些训练集数据,看不出差别,例如写诗的案例。个人感觉,这个是制约AI发展的重要问题,它直接让程序的“调试”效率非常低下。
  4. 中文文档比较少,英文文档也不多,开源社区一直在快速更新,文档的内容过时也比较快。因此,入门学习时遇到的问题会比较多,并且缺乏成型的文档。

小结

我们不知道人工智能的时代是否真的会来临,也不知道它将要走向何方,但是,毫无疑问,它是一种全新的技术思维模式。更好的探索和学习这种新技术,然后在业务应用场景寻求结合点,最终达到帮助我们的业务获得更好的成果,一直以来,就是我们工程师的不懈追求。另一方面,对发展有重大推动作用的新技术,通常会快速的发展并且走向普及,就如同我们的编程一样,因此,人人都可以做深度学习应用,并非只是一句噱头。

参考文档:
http://www.tensorfly.cn/
https://www.tensorflow.org/

数学相关的内容:
高中和大学数学部分内容http://url.cn/44r6LAQ
线性代数视频:http://open.163.com/special/opencourse/daishu.html

作者简介:徐汉彬,腾讯高级工程师(SNG增值产品部),QQ会员体系合作线技术团队负责人。曾就职于阿里巴巴、小满科技。QQ会员活动运营平台的初始开发,在五年的时间里,将该Web系统从日请求百万级升级至十亿量级(日请求4-12亿),负责该平台的架构设计和研发建设,在Web系统架构方面拥有比较丰富的实践和积累。
责编:何永灿,欢迎人工智能领域技术投稿、约稿、给文章纠错,请发送邮件至heyc@csdn.net ,本文为CSDN原创投稿,未经作者允许不得转载。




SDCC 2017•上海站将于2017年3月17-19日登陆申城。截至3月5日前门票八折优惠中,5人以上团购立减1500元,详情点击注册参会

评论