第01.0讲:一个例子入门机器学习

关于

本讲内容将通过一个例子,入门机器学习。在这一讲中,你将学习到:

  1. 什么是机器学习?
  2. 机器学习能做什么?
  3. 机器学习在代码上具体如何实现?

学习本讲,希望你

  1. 年满18岁。
  2. 如果你需要完成实践部分,需要有基本的 python 知识,你可以通过python快速入门快速了解python如何使用。

机器学习的本质

现在人们通常将机器学习和人工智能联系在一起,实际上,人工智能涉及的领域更加宽泛,机器学习只是其中一种手段。人工智能的起源可以追溯到上世纪50年代,1956年举办的达茂思会议(Dartmouth Conference),在这次会议上,信息论之父Shannon和IBM科学家Nathan Rochester等人,一起探讨了一个议题:精确地描述学习过程和智能的特征并用机器进行模拟。说人话!就是用机器模拟出人类的智能!

人工智能发展的初期,研究者致力于将人类的知识表达为一些逻辑规则,然后利用搜索进行逻辑推理,进而实现智能,到后来演变到利用知识库构造专家系统,实现所谓的智能。这期间,比较有名的成就有IBM的国际象棋程序深蓝打败国际象棋冠军。这一阶段的人工智能实现,更像人类的演绎推理,利用少量的规则,加上知识库,进行推演,从而得出结论。但是,规则的归纳需要人类专家干预,限制了这种模式的发展。2000年以后,随着互联网和摩尔定律的发展,产生了大量的数据和计算资源,使得人们可以利用机器从数据中自动归纳出规则,也就是数据驱动的智能。这其中的工具就是机器学习!

所以,机器学习就是利用一种程序从数据中自动归纳出有价值的知识的一种方法

所谓演绎推理(Deductive Reasoning),就是从一般性的前提出发,通过推导即“演绎”,得出具体陈述或个别结论的过程。演绎推理的逻辑形式对于理性的重要意义在于,它对人的思维保持严密性、一贯性有着不可替代的校正作用。我们熟知的很多数学证明方法,例如通过简单的几条公理,推导出整个欧式几何大厦的推理过程,就是典型的演绎推理。 下面是演绎推理里面一个典型的三段论推理的例子:

  1. 知识分子都是应该受到尊重的,
  2. 人民教师都是知识分子
  3. 所以,人民教师都是应该受到尊重的。

演绎推理的核心思想就是从一般到特殊,将一些已经为真的通用性结论应用到具体的问题当中,得到具体的情况下的结论。这种推理方式保证了推理的严密性!在上述例子中,前两条就是一般性结论,知识分子是比人民教师更大的概念,第三条的结论就是将第一条结论应用到人民教师这个具体的个体上得到的更具体的结论!有趣的是,柯南道尔的著名小说中《福尔摩斯》中的大侦探福尔摩斯也十分推崇“演绎法”!为此,老美还专门拍了一部剧《福尔摩斯:基本演绎法》! 只不过,福尔摩斯所声称的一般性结论和推理方式非常人能理解!

而归纳法是根据一类事物的部分对象具有某种性质的有限观察,推出这类事物的所有对象都具有这种性质的推理,叫做归纳推理(简称归纳)。归纳是从特殊到一般的过程,它属于合情推理。通常归纳法难以保证结论是可靠的,例如,下面就是经典的归纳法的例子:

  1. 欧洲看到过的天鹅都是白色的。
  2. 所以所有的天鹅都是白色的!

黑天鹅事件:17世纪之前,欧洲看到过的天鹅都是白色的,所以当时欧洲人归纳出一个结论:天鹅都是白色的! 直到后来,欧洲人发现了澳洲,看到了当地的黑天鹅,人们才认识到这个结论是错误的!

从有限的经验归纳出来的结论当然不见得是可靠的,但是数学上也有完全归纳法,可以保证结论是可靠的的例子,我们以前学过的数学归纳法。从逻辑推理的角度来看,我们现在所用的机器学习就是先观察到一些数据,然后从这有限的数据中归纳出一些有用的规律的过程! 因此,机器学习本质上就是在做归纳推理,并且是不完全的归纳法!我们前面说到,这种不完全归纳法无法保证结论的正确性,所以如果机器学习模型预测错了,请不要怪他,因为它是在做不完全归纳,肯定会犯错的!但是这并不意味着就没有用,事实上我们人类很多经验都是通过不完全归纳法归纳出来的,甚至可以说几乎所有实际的经验都来源于不完全归纳,完全归纳法只有在数学上才存在。只要归纳的结论大多数情况下是对的,那么他就是有用的!

接下来,我们就用一个实际的例子来解释机器学习是如何从数据中学到有用知识的。

从0开始机器学习

接下来,我们将利用一个简单的分类任务,给读者展示机器学习如何从数据中学到有用知识的。

任务与数据

本任务采用鸢尾花(iris)数据集,你可以从UCI网站上下载https://archive.ics.uci.edu/ml/datasets/Iris。如果已经安装了 scikit-learn,那么可以利用提供的dataset接口直接调用。鸢尾花数据集是著名的统计学家 Fisher 提供的。下面我们采样少量的数据看一看。

sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target
91 6.1 3.0 4.6 1.4 1
77 6.7 3.0 5.0 1.7 1
99 5.7 2.8 4.1 1.3 1
65 6.7 3.1 4.4 1.4 1
14 5.8 4.0 1.2 0.2 0
108 6.7 2.5 5.8 1.8 2
142 5.8 2.7 5.1 1.9 2
127 6.1 3.0 4.9 1.8 2
24 4.8 3.4 1.9 0.2 0
2 4.7 3.2 1.3 0.2 0

该数据集的每一条记录代表一个样本,每一个样本有4个属性变量:

每一个样本有1个目标变量target,target有3个取值,每一种取值的意义如下:

这个数据集一共有150个样本,这三种花都有50个样本。上面显示出来的只是随机选取的一部分数据。每一种鸢尾花的图片如下,从左到右分别是 setosa,versicolor,virginica

Iris

建模

我们的目标是,建立一个模型,输入鸢尾花的4个属性变量,能够对鸢尾花的种类进行判别。这样一旦模型建立好了之后,对新看到的鸢尾花,只要测量了这4个属性,就可以利用模型对它的类别进行预测了。

数学地角度来说,我们要确定一个函数 $f: R^4 \rightarrow \{0,1,2\}$,输入是一个4维向量 $\vec{x} = (x_1, x_2, x_3, x_4)$,每一维代表一个属性变量的值,输出一个分类变量 $y \in \{0,1,2\}$,代表该样本属于哪个类别。所谓的建模过程,就是利用我们已经观测到的数据集,去确定这个函数 $f$ 的具体形式。这里每一个属性我们都称作一个特征,输出分类变量我们称做目标(或建模目标),这里的函数 $f$ 就是我们通常所说的模型。

简单规则模型

在建立复杂模型之前,我们先来建立一种简单规则模型。所谓的简单规则,就是对一个属性,通过规则判定,确定该样本属于哪一个类。比如,我们可以进行数据分析,将收集的数据绘制到以花瓣长度为横坐标、花瓣宽度为纵坐标的坐标图上

svg

可以看到,这三种花在花瓣长度和宽度上都有明显差异,我们可以继续分析,统计出每一种花的萼片长度、萼片宽度、花瓣长度、花瓣宽度的平均值。

svg

通过上述分析,可以看到三种花的花瓣长度(petal length,对应绿色的柱子)平均值差异比较大,setosa的平均花瓣长度在1.5cm左右,versicolor的平均花瓣长度在4.2cm左右,而 virginica的平均花瓣长度在5.6cm左右。因此,一种简单规则模型可以归纳为

$$
target =
\begin{cases}
0, \text{petal length} \lt 2.8 \\
1, \text{petal length} \in [2.8, 4.9) \\
2, \text{petal length} \ge 4.9
\end{cases}
$$

这里分割点的值取的是两种花平均中的平均数。这实际上就是一个分段函数,输入时花瓣长度,输出就是花的类别,因此这就是一个模型。

好了,到目前为止,你已经学会了数据挖掘过程中的最简单情景了。通过数据分析,归纳出规则,然后将规则编码成一个函数,从而得到一个预测模型,可以用来做预测。

很快,我们会发现,这种方法需要人工进行数据分析,总结出规则,那么能不能够让程序自动地找到这些规则,甚至发现更复杂的规则呢?答案是肯定的,决策树就是这样一种模型,自动地发现这些规则,甚至复杂的组合规则。

决策树模型

下图是上面我们人工挖掘出来的规则模型对应的决策树模型,可以看到它由很多节点构成,包括中间节点(椭圆形)和叶子节点(方形)。中间节点是一个规则,每个规则是一个逻辑判断,例如最上面的中间节点(也叫做根节点)的规则是花瓣长度小于2.8厘米。如果规则满足,则进入左边的叶子节点,否则就沿着右子树继续判断,我们把这个过程称作分裂。叶子节点对应模型的输出结果,最左边的叶子节点输出的是setosa,表明满足该叶子节点规则就预测为setosa。每一个叶子节点都对应一条从根节点出发的路径,路径上长度称作叶子节点的深度(也可以认为是规则的数目),叶子节点的最大深度称作树的深度,图中这棵树的深度为2。一个样本过来,沿着根节点开始,不断地按照规则往下移动,直到到达叶子节点。叶子节点对应的输出结果就是模型对这个样本的预测结果。这些规则可以用一棵树状的图描述,因此叫做决策树模型。

决策树

决策树节点上的规则可以从数据中通过算法自动地发现,或者说可以通过算法自动地生成一棵决策树,这个过程称作模型的训练。决策树如何发现这些规则我们暂时不要去深究,作为一个入门课程,我们重点是了解模型能干啥。这里,我们利用 scikit-learn 软件包里面的决策树模型工具,建立模型。模型训练好了之后,我们可以将决策树画出来,进行观察。

svg

算法自动学习出来的决策树是一颗二叉树,根节点对应规则是petal length (cm) <= 2.45。满足这条规则的样本,就到了左子树。左子树是一个叶子节点,图中的values=[50, 0, 0]表示整个数据集的150个样本中满足这条规则的样本只有50个,且全部为setosa这个类别,因此叶子节点预测输出的类别是 setosa。这和前面我们通过数据分析得出的规则 petal length < 2.8 则为setosa,非常接近。

预测

一旦模型建立好了之后,我们就可以利用模型进行预测了,所谓的预测,是指对于一个新的样本,比如我在某个路边看到了一朵鸢尾花,不知道到底是哪一类,就可以利用这个模型进行预测。首先,我们需要测量模型预测所需要的4个数据(特征),花萼的长度和宽度,花瓣的长度和宽度,然后输入的模型中去。

对预测前面简单规则模型,只需要花瓣的长度数据即可预测。对于决策树模型,实际上只需要花瓣的长度和宽度数据也可预测,如果我们将决策树深度变得更深,那么就可能要用到所有数据。首先,决策树从根节点开始搜索,根节点对应一条规则 petal length (cm) <= 2.45,如果满足这条规则,就到左子树,预测输出为setosa。如果不满足,那么就到右子树,右子树根节点还是一个规则 petal width (cm) <= 1.75。我们重复这个过程,直到找到该样本满足规则的叶子节点,叶子节点对应的输出值就是模型预测结果。

总结

这里我们以鸢尾花分类任务为例,构建了一个决策树模型进行预测。总结起来,所谓的建模过程,就是利用已有的标注数据(已知目标变量的值的数据),自动学习到一个函数 $f:R^n \rightarrow Y$,根据观察到的特征向量,计算得到目标变量的值。这个任务就是一个3分类的函数。虽然这个任务简单,但是和更复杂的任务一样都具有以下3个基本步骤:

不同的业务可能收集到的数据不同,收集到的原始数据需要加工成模型能用的数据(即特征)。不同的任务建模目标也不一样,比如预测性别,那么目标变量是男和女;预测年龄,那么目标变量是个0-100之间的连续值;预测股价涨跌,那么目标变量就是涨和跌。根据建模目标可以将问题分为两大类,一类是和这个鸢尾花分类问题类似,目标变量取值有有限的,称作分类问题;另一类和预测房价一样目标变量取值是无限的,称作回归。

相同数据和目标的情况下,也可以选择不同的模型,决策树是一个久经沙场的模型,它的两个变体随机森林梯度提升树应用非常广泛。近年来的深度神经网络,可以利用非常原始的数据进行建模,减少了人工特征的工作量(毕竟去想很多可能有用的变量是一件不少的体力活)。但是本质上,他们都在干同一件事情,从数据中发现规律,这个规律可以表达为一个函数,因此也叫函数拟合!

机器学习的应用举例

具体来讲,机器学习可以用来做很多事情,目前已经有成功案例的就有很多。

点击率预估

点击率预估是很早就开始应用机器学习的场景之一,也是机器学习在工业界应用最为广泛的场景。可以说,过去5年,60%的算法工程师都是在做各种点击率预估。点击率预估在广告、搜索、推荐三个场景中应用最为广泛。

在互联网广告中,如何决定给用户展示哪种广告一直是互联网广告商十分在意的事情。例如,当你在百度中搜索“手机”时,排在搜索前面的几条都是广告,但是广告位是有限的,现在假定只有一个广告位,而手机广告总共有1000个,那么应该展示哪个广告呢?一种简单的策略就是看每个广告过去的点击率,点击率=点击人数/展示人数,点击率高的广告出现的概率高。这个简单的方法没有个性化,那么很容易通过引入个性化来提高广告的总体点击率。例如,对于收入水平低的用户,更应该展示低价的手机,而收入水平高的用户,则更应该展示高价的手机。这表明,如果我们能够知道用户对每一个广告的个性化点击率而不是总体的点击率,那么我们可以让展示的广告更有效率,例如为广告运营商带来更多的广告费。但是,个性化点击率很难通过历史行为统计出来,因为给同一个用户展示同一个广告的次数不可能很多,而且广告是如此之多,以至于每一个用户看过的广告占总的广告比例非常低。例如,百度声称在使用百度推广的企业有62万家,某个用户看过的广告都只是沧海一粟。

在搜索排序中,同样会有上述问题,你在京东上搜索手机时,也会根据你的点击率给你排个序。与广告不同的是,电子商务排序中还会考虑你的下单率,因为在电子商务中,下单才能带来实实在在的收入。在推荐系统中,推荐位置同样是有限的,推荐哪些商品和不推荐哪些商品都是需要用户的个性化估计点击率和下单率。所以最终会根据点击率和下单率加权取得分最高的商品展示在推荐位置上。

点击率预估和下单率预估的方法没有太大差异,这里以广告点击率预估为例进行说明。广告运营商根据用户和广告的历史交互数据,可以知道用户是否点击了某个广告。然后可以找出用户的一些信息,例如性别、年龄等人口学属性,还有广告的标签和分类等信息。利用机器学习的算法,可以建立一个数学模型,该模型可以根据这些信息计算出用户的个性化点击率。如下图所示,是一个点击率预估的决策树模型,实际场景中的决策树比这个要复杂得多,但是基本原理并没有太大差异。决策树的根节点是一个跟用户性别有关的规则,假设我们从历史数据中收集了10000个样本,这些样本每一条都是一次广告的展示。性别为男则进入左子树,否则则进入右子树。假设收集的样本中有4000个样本对应的用户性别为男性,那么左子树有4000个样本,右子树有6000个样本。左子树的根节点是一条与广告有关的规则,“广告类别=汽车”,如果广告类别是汽车则进入最左边的叶子节点,这样的样本有1000个,其中25个用户点击了广告,剩下的975个样本用户没有点击广告。因此,这个叶子节点输出的点击率为2.5%。这个结果是从训练的样本中计算得到的,预测时,如果用户和广告满足这个规则“用户性别=男 && 广告类别=汽车”,那么模型就预测点击率为2.5%。这就是最简单的点击率预估模型,可以看到,这跟很多数据分析师干得事情是十分相似的,最大的区别在于这些规则是通过算法自动生成的,可以设想,当要考虑的因素成千上万时,分析师是多么的抓狂,但是机器不会,机器可以生成大量的这种规则,让预测更加精准,在这件事情上,机器比人更加擅长。

CTR决策树

大数据风控

在传统银行中,对个人和企业的贷款需要进行风险控制,为此往往需要花费大量人力进行背景调查。由于这种调查成本很高,所以银行很难解决小额贷款的风控问题,因为成本太高,买卖不划算,银行要么要你提供不动产抵押才能贷款,要么直接不给你贷。在互联网时代,人们大量的信息都被数字信息记录了下来,通过一些技术手段可以将传统金融风控所需要的信息提取出来,利用机器学习的方法,预测用户的还款意愿和还款能力,这样就可以低成本地完成大量用户的贷款申请审核工作,使得个人小额贷款成为一个赚钱的买卖。

人脸识别

现在的数码相机和智能机上的相机都具备了人脸识别的能力,所谓人脸识别就是可以检测出图片中哪些部分是人脸,哪些地方不是。一般的相机在拍照时,都是实时地识别人脸,一旦识别出来后,会用一个正方向框住人脸。

人脸识别

这件事情看起来十分玄乎,但是一旦你了解了机器学习工作原理之后,这背后的基本原理就变得简单了。现在几乎涉及到图像、视频等检测的场景,背后最主要的算法都是机器学习。人脸识别的大致原理是,如果有一个模型(函数),输入一张图片,它会告诉你这张图片是否为人脸,那么人脸检测就简单了。为了从一张大图中找到哪些区域是人脸,可以先把这张大图按照不同尺寸不同分辨率截取很多小的图片块,对每个块应用上述模型,预测这个小块是否是人脸,然后把所有是人脸的块都标记出来,最后把这些块融合成一个就可以了(参考上图)。

那怎么得到这样一个模型呢?答案是机器学习!可以先收集各种各样的图片,然后人工标记哪些是人脸,哪些不是,得到一个标注的人脸识别数据集。图片在计算机中是用数字表示的,我们知道每一张图片都是有很多像素点构成,对于黑白度图像,每一个像素对应一个数值,这个数值越大,表明这个像素点越亮,这个数值越小,表明这个像素点越暗,这些敏感交替的像素一起构成了整个图片(参考上图)。这里每个像素对应的数值都可以看做特征,虽然我们无法像分类鸢尾花那样,认为某个像素高就更有可能是人脸,但是和分类鸢尾花一样,都是要找到一个函数对输入的这些值进行计算,输出一个结果。只不过,计算人脸的这个函数要比鸢尾花的复杂得多得多,但本质上都是在找到这个函数,而且实现的方法惊人地相似!只要把这些特征全部扔到机器学习模型中,让机器自动地从这些标记的数据中学到一个可以识别人脸的函数就可以了。当然实际的系统比这个要复杂得多,但是基本原理差不太多。这在另外一个方面也体现了机器学习相比人工规则的优越性,人工规则实在是难以找到这样一个复杂的函数来识别是否为人脸。

计算机视觉用计算机来实现人的视觉的研究领域,里面很多问题都和上述人脸识别的问题类似,最终都归结为拟合一个函数的问题,这时机器学习就派上用场了。

出租车派单

在互联网出租车出来以前,打车靠的是运气,当你碰到一个空车并且司机成功看到你的时候,这笔交易就算成功了。很多时候,这种匹配都很困难,尤其是在偏僻的地方和高峰期。当用户和司机都通过手机APP接入互联网之后,这种匹配效率就大大提高了。设想这样一个问题,在一个区域有5个乘客和7个司机,每个司机只能派一单,但是可以把同一个订单派给多个司机,最先抢到的司机就算接单了。由于司机可能因为距离太远、目的地太偏、甚至心情不好等原因拒绝接单,所以这5单即使都派出去也不简单都能在这一次派单中成交,没能成交的订单就需要等待下一次派单。派单的一个简单的目标是让这5单尽可能地成交,如果定义接单率是成交的单数/总单数,那么目标就是让总体的接单率最大化。那么问题来了,怎么把这5单派给司机使得总体接单率最高呢?最简单的方法是就近原则,派给最近的司机。但这种策略不见得是最优的,并不是所有司机都愿意接单,有些司机不愿意接近距离的单,有些司机不愿意接目的地很偏的单。这种只依靠单一因素的策略匹配成功率不会很高,那么如何把所有能够考虑的因素都考虑进来设计派单策略呢?答案是用机器学习的方法预测司机的接单率!收集历史上派单的记录,记录里面会有司机是否接单,然后将需要考虑的因素计算出来,作为模型的特征x,模型预测的目标是不接单(y=0)还是接单(y=1)。然后就可以利用算法自动归纳出一个函数,输入是这些特征,输出是接单还是不接单。也可以像点击率预估模型那样,输出接单的概率,这个概率就可以用来设计更好的派单策略!这个思路就是滴滴在2017年数据挖掘大会(KDD)上提出的派单模型的一部分。

类似的问题还有外卖骑手的接单问题,这类问题已经大量使用机器学习的方法进行优化了。机器学习算法正在影响我们生活的方方面面,从打车到点外卖,几乎无处不在。

复现代码

数据加载

from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sn

plt.style.use('seaborn-talk')

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['target'] = iris.target

# 随机选取几条数据
idx = range(df.shape[0])
np.random.shuffle(idx)

print(iris.target_names)
print(df.iloc[idx].head(10))

鸢尾花数据分析

ax = plt.gca()
df.groupby(by='target').plot(kind='line', x='petal length (cm)', y='petal width (cm)', style='.', ax=ax)
ax.set_xlim([0,8])
ax.set_ylim([0,3])
plt.legend(iris.target_names, loc='best')
plt.xlabel('petal length (cm)')
plt.ylabel('petal width (cm)')

df.groupby(by='target').mean().plot(kind='bar')
plt.xticks([0,1,2], iris.target_names);
plt.xlabel('')
plt.ylabel(u'平均值')

决策树建模

决策树模型分为回归和分类,如果目标变量是类别变量,只能取有限的几个值,这样的问题称为分类,我们这个任务就是分类问题。而如果目标变量是可取连续值变量,例如预测房价,那么这样的问题就是回归。这里我们只用分类就好了,对应的类是 DecisionTreeClassifier,为了便于观察,我们限定树的深度为2。为了让决策树模型能够从数据中学会规则,我们需要调用模型的 fit 方法,并将数据(包括特征iris.data和目标iris.target)传给它。

模型从数据中自动学会这些规则的过程,我们称之为训练或者拟合。因此,fit方法实际上就是在做模型训练

from sklearn.tree import DecisionTreeClassifier, export_graphviz
import graphviz

clf = DecisionTreeClassifier(max_depth=2)
clf.fit(iris.data, iris.target)

dot_data = export_graphviz(clf, feature_names=iris.feature_names,
                           class_names = iris.target_names,
                           out_file=None, filled=True)

graphviz.Source(dot_data)

思考与实践

一个编程小练习,探索决策树的深度与预测的准确率的关系,并解释一下观察到的现象的原因。

在编程之前,请先配置好环境:

可以用python包管理器pip安装相关包,pip默认会使用国外的软件源,在国内下载较慢,建议使用国内的镜像:

from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier

plt.style.use('seaborn-talk')

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['target'] = iris.target

depths = range(2,10)
errors = []

"""下面是你的代码,请完成功能:
   1. 用深度为2-10的不同决策树分别对数据进行建模,计算每一颗决策树的预测准确率。准确率是预测正确的样本数目 / 总样本数目。
   2. 画出深度-误差的折线图

   Hint:
      1. sklearn每一个模型都会有一个`predict`方法,可以用来预测结果。
      2. matplotlib 的画图函数 `plt.plot` 是有用的画图工具。
"""