前几天,红色石头在公众号发文,给大家介绍了一本机器学习入门与实战非常不错的书籍《Hands-On Machine Learning with Scikit-Learn & TensorFlow》,文章链接如下:
之前我说过会带着大家一起来学习这本书,主要包括两个方面来学习:
1. 建立 GitHub 项目,把本书各章节的重要知识点整理出来(不仅仅是翻译)。目前才上传了第一章内容。地址如下:
https://github.com/RedstoneWill/Hands-On-Machine-Learning-with-Sklearn-TensorFlow
2. 把本书各章节的一些知识点提炼出来,以笔记的形式发布到微信公众号上,方便大家查看。
好了,言归正传!今天给大家整理出第一章前半部分内容:机器学习必须知道的基础知识!
1. 什么是机器学习?
机器学习是一门通过编程从而在数据中学习的科学技术,或者称之为艺术。这里有一个更一般的定义:
在不直接针对问题进行编程的情况下,赋予计算机学习能力的一个研究领域。 —— Arthur Samuel, 1959
另外一个更加工程化的定义:
对于某类任务T和性能度量P,如果计算机程序在T上以P衡量的性能随着经验E而自我完善,那么就称这个计算机程序从经验E学习。 —— Tom Mitchell, 1997
例如垃圾邮件过滤系统就是一个机器学习程序,可以用来区分垃圾邮件和非垃圾邮件。机器学习系统用来学习的样本称之为训练样本(training set),每个训练样本称之为训练实例(training instance)或样本。在这个例子中,任务 T 就是要对新邮件进行区分,经验 E 就是训练数据,性能度量 P 需要定义,例如使用分类正确的邮件所占的比例。这种度量方式称之为准确率(accuracy),经常应用在分类问题中。
2. 为什么使用机器学习?
我们先来思考,使用传统编程技术如何区分垃圾邮件和非垃圾邮件呢?(如下图所示)
1.首先你应该考虑典型的垃圾邮件具有什么特点。例如在邮件的主题中包含了“4U”、“信用卡”、“免费”等词语。又或者是在邮件的发件人、邮件内容中有一些垃圾邮件常出现的特定的词语等。
2.然后你就可以对这些情况编写垃圾邮件检测算法,若一定数量的词汇检测到了,则可以判定是垃圾邮件。
3.最后验证程序,重复 1 和 2,直到检测算法的准确率足够高。
因为这个问题并不简单,你按照上面思路编写的程序可能很复杂、冗长,而且很难把控。
对比而言,同样是垃圾邮件分类,机器学习可以自动从垃圾邮件和非垃圾邮件中检测出哪些单词是经常出现在垃圾邮件中的(如下图所示),而不需要人为指出出现哪些单词可能就是垃圾邮件。这样使得问题更加简化、容易掌控,而且分类的准确率更高。
而且,如果给你发垃圾邮件的人知道了他们的关键词“4 U”被锁定的话,他们可能会把这个关键词替换成“For U”。如果使用传统编程技术,就要针对“For U”重新编写程序。那么,垃圾邮件关键词每次替换,都需要重新针对新的关键词改写程序,相当麻烦。
而机器学习则不用这么麻烦,只需将新的邮件样本交给模型,机器会自动检测出哪些单词是可能出现在垃圾邮件中的。整个过程不需要人工排查和干预(如下图所示)。
机器学习强大的另一个原因是,某些问题非常复杂,传统编程很难有较好的方案去解决。例如语音识别、图像识别,使用传统人工编程方式来解决效率低而且准确率不高。相比之下,最好的解决方法就是使用机器学习,给出足够多的数据,让机器自己去学习,得到较高的识别率。
最后,机器学习还可以帮助人们去学习(如下图所示)。为什么这样说呢?因为 ML 算法可以从数据中提取出有用和关键的信息。例如垃圾邮件分类,经过好的训练得到的 ML 模型能够揭示出哪些单词或者哪些单词的组合是预测为垃圾邮件最重要的特征参考。机器得到的这些信息可以帮助人们更好地揭示问题的本质,从而更好地解决问题。
将 ML 技术应用到大数据中,挖掘数据本身蕴含的规律和模式,这就是数据挖掘。
总结下来,机器学习的强大之处体现在以下几个方面:
- 传统的编程方式解决问题通常需要人工调试、建立冗长规则,而机器学习算法可以极大地简化代码且性能更好。
- 对于某些复杂问题,例如语音识别、图像识别,传统编程没有一个较好的解决方案,而机器学习通常表现得很好。
- 机器学习系统可以自适应新的数据,只需采集新的数据,重新训练 ML 模型即可。
- 机器学习可以揭示复杂问题和大数据内在规律和特性。
3. 机器学习类型
机器学习类型可以按照不同的方式来划分,具体表现在:
- 是否需要人类的监督:监督式学习、非监督式学习、半监督式学习、强化学习
- 是否可以在线上进行学习:在线学习、批量学习
- 是否可以简单地把新的数据集与已知数据集进行比较得出结果,还是从训练集中检测模式再建立预测模型:基于实例学习、基于模型学习。
当然以上这些划分方式并不是各自独立的,可以相互结合。例如一个垃圾邮件检测系统,可以是在线的、基于神经网路模型的监督式学习。
接下来我们详细看看这些机器学习类型。
3.1 监督式/非监督式学习
机器学习可以根据再训练过程中受到的监督类型和程度进行划分,类型包括:监督式学习、非监督式学习、半监督式学习、强化学习。
监督式学习
监督式学习,训练集包含了样本的输出,即 label(如下图所示)。
一个典型的监督式学习就是分类问题,例如垃圾邮件分类,通过对邮件内容包括它们对应的标签(垃圾、非垃圾)进行训练。最终得到的模型对新的邮件进行预测,判断它是不是垃圾邮件。
另一个典型的监督式学习就是回归问题(如下图所示),例如汽车价格预测,通过对汽车特征(商标、车龄等)包括它们对应的价格进行训练。最终得到的模型对新的汽车进行预测,得到它的价格。
值得注意的是,有些回归算法也可以用于分类,反之亦然。例如逻辑回归(Logistic Regression)常用于分类,然而它也可以输出对应该类别的概率(例如得到垃圾邮件的概率是 20%)。
下面列举一些最常见的监督式学习算法:
- k-近邻
- 线性回归
- 逻辑回归
- 支持向量机
- 决策树和随机森林
- 神经网路
非监督式学习
非监督式学习,顾名思义,训练集是没有输出 label 的(如下图所示)。
下面列举一些最常见的非监督式学习算法:
- 聚类
— k-均值
— 层次聚类分析(HCA)
— 最大期望算法(EM)
- 可视化和降维
— 主成分分析(PCA)
— 核 PCA
— 局部线性嵌入(LLE)
— t分布随机邻居嵌入(t-SNE)
- 关联规则学习
— Apriori
— Eclat
例如现在你掌握了你的博客访客信息,想使用聚类算法对这些访客进行分组(如下图所示)。你不需要告诉算法每个访客应该属于哪一组,聚类算法会自己分析和判断。比如访客中有 40% 是男性,喜欢看漫画书,访问博客的时间经常是晚上;20% 是年轻的科幻迷,访问博客的时间经常是周末,等等。如果你使用层次聚类分析,会对每个组划分得更细。
可视化算法(visualizationv algorithm)也是非监督式学习的好例子。它能够把高纬的复杂且没有标签的样本数据在 2D 或 3D 空间中展示出来,便于作图(如下图所示)。这些算法尽可能保存原始数据足够多的结构信息(例如可视化的时候避免原始输入空间不同类别的样本发生重叠)。这有助于我们理解不同类别之间的近似程度。
一个相关的算法叫做降维(dimensionality reduction)。目的是简化数据但是不能损失太多信息。常用的思路就是讲相似特征合并成一个。例如汽车的里程数和它的车龄关系较为密切,降维算法就是把这两个特征合并成一个特征来表征汽车的磨损程度。这种做法就是特征提取。降维的好处是减少特征个数,让算法运行更快,减小了计算成本和存储空间成本。更重要的是,降维可以消除一些噪声影响,提升算法性能。
还有一个非监督式学习的例子就是异常检测(anomaly detection)。例如检测不正常的信用卡交易来避免诈骗,捕获人工制造缺陷,或者剔除数据集中的异常点以便传替给后面的机器学习算法。异常检测算法是使用正常数据集进行训练的,得到的模型对新的样本进行测试,判断它是不是异常点(如见图所示)。
最后一个非监督式学习的例子就是关联规则学习(association rule learning),目标就是挖掘大量数据中不同属性之前的联系。例如,通过对超市用户购买商品历史记录的分析,发现购买烧烤酱和土豆薯片的用户也倾向于购买牛排。因此,基于此分析,超市老板就可以把这些用户可能会一起买的商品放在相近的货架上,方便用户购买。
半监督式学习
半监督式学习,简单地说就是训练集大多数样本没有 label,只有少量样本有 label。例如一些照片托管服务,例如谷歌照片,就是一个例子。一旦你把所有家庭照片上传到服务器之后,云端算法会自动识别出每个家庭成员出现在哪些照片中,例如 A 出现在照片 1、3、5、6 中, B 出现在照片 2、3、5、8 中,这是一个非监督式学习的过程(聚类)。现在你要做的就只是告诉机器每个人的名字(相当于 label),即标注 A、B 等分别叫什么名字(监督式)。然后,机器就可以根据你给的 label,把每张照片中的每个人都标注上名字,即 label。也就是说,通过标注少量的样本,然后根据聚类算法,把所有样本的 label 都自动加上了。
大多数半监督式学习都是监督式学习和非监督式学习相结合的。例如深度置信网络(DBNs),其基础构建模型为受限玻尔兹曼机(RBMs)。RBMs 的训练过程是非监督式学习,但是整个 DBNs 系统是使用监督式学习调优的。
强化学习
强化学习相对来说比较难一些。如下图所示,假设一个学习系统 Agent 可以观察环境,并做出相应的行为,然后得到相应的反馈。正确的行为得到奖励,错误的行为得到惩罚。Agent 必须自我学习最佳策略是什么,来即时获得最多的正反馈,从而实现学习的目的。策略定义为 Agent 在一个给定情境中需要做出的即时行为。
上面这张图生动形象地解释了强化学习。机器人 Agent 面临的状况是发生火灾,这时候,它必须进行决策,是选择接近火源,还是接水救火?这由 Agent 自己决定。如果 Agent 直接接近火源,那么就会得到惩罚(负反馈)。通过这样的训练,让 Agent 自己知道避免惩罚就必须远离火源,而做出取水救火的策略。强化学习可以类比成训练宠物的过程,比如我们要训练狗狗坐下,但是狗狗无法直接听懂我们的指令的。在训练过程中,我们根据狗狗的行为做出相应的反馈,如果它表现得好,我们就给他奖励,如果它做跟指令完全无关的动作,我们就给它小小的惩罚。这样不断修正狗狗的动作,最终能让它按照我们的指令来行动。
强化学习非常强大,我们熟悉的 AlphaGo 就是强化学习的典型代表。2016 年,AlphaGo 战败了世界围棋冠军李世石。它就是通过分析数百万场围棋比赛,从中学习到赢的策略,然后跟自己进行很多长比赛。经过这样的训练和学习,最终 AlphaGo 成为了一名围棋顶尖高手。
3.2 批量学习和在线学习
机器学习类型另一种分类方式是根据是否可以对新添加的数据进行在线的即时学习。如果可以的话就叫在线学习(online learning),如果是离线的话就叫批量学习(batch learning)。
批量学习
批量学习不是即时学习,它是将所有的训练数据一起训练,这会花费很多时间和计算资源,所以一般只能用离线的方式训练。模型一旦训练完成,就上线发布,使用固定的模型工作,所以也常称为离线学习(offline learning)。
这时候如果有新的数据产生,想要得到新的模型必须把新的数据和之前的数据结合起来,再次重新离线训练机器学习模型,最后上线发布。
批量学习比较简单也是最常见的机器学习类型。但是,一次完整的训练过程可能会花费很多时间,甚至几天、一个星期都有可能。如果机器学习系统更新数据很频繁,那么使用这种离线方式训练就比较麻烦,成本很大,需要不停地整合数据、离线训练、发布模型。这种情况下,批量学习并不是一个好方法。
而且,训练整个数据集需要很多的计算资源(例如 CPU/GPU、内存、存储、IO、网络等)。特别是再数据量很大的情况下,其消耗的资源和成本是巨大的。这时候,一般不建议使用批量学习模型。
最后,如果你的系统需要能够自主学习,且资源有限(例如手机 App),那么携带大量数据、消耗大量资源,每天花费数小时来进行模型训练是不太现实的。
在线学习
在线学习可以即时地重新训练模型,当新的数据点或者小批量(mini-batches)数据传给模型的时候,模型立即根据新数据重新训练。整个过程快速而且成本低。这是一种在线学习的方式,如下图所示。
在线学习对于那些持续接受数据(例如,股票价格)并且需要快速更新或自主学习的系统非常有用。如果你的计算资源有限,在线学习也是一个不错的选择:一旦在线学习系统了学习了新的数据实例,它就不再需要它们了,因此可以丢弃(除非您想要回滚到以前的状态并“重播”数据)。这样可以节省大量的空间。
在线学习算法也可以应用于当数据集很大,机器内存不够的时候进行模型训练(称为核外学习,out of core learning)。做法是把整个数据集切分成许多小批量(mini-batches)样本,依次对每个小批量样本进行训练,重复进行,直到整个数据集完成训练(如下图所示)。值得注意的是,整个过程一般是离线进行的,所以在线学习这个名字可能有点让人疑惑,你也可以称之它为增量学习(incremental learning)。
在线学习一个很重要的参数就是基于数据改变更新算法的频率,是只要有新的数据进来就更新算法还是积累一定的数据改变之后再更新算法。这个参数被成为:学习率(learning rate)。如果设置很大的学习率,算法对会快速适应新数据,而忘记旧的数据(然而,垃圾邮件检测系统中你可能并不想让模型只对新的垃圾邮件标记,而很快舍弃之前的标记方法)。相反,如果设置较小的学习率,系统就会具有较大的惯性。也就是说,学习的速度比较慢,但是对数据中可能存在的噪声就不会特别敏感。系统的健壮性就会更强一些。
在线学习一个大的挑战是只要有坏数据输入给系统,那么系统的性能就会立刻逐步下降。如果这是一个即时系统,用户就会立刻注意到。例如,坏数据可能来自于机器人上的故障传感器,可能是某人在搜索引擎输入垃圾信息以提高搜索排名。为了减少这种风险,您需要密切监视系统,如果检测到系统性能下降,就立即关闭学习(或回滚到上一个版本的系统)。您可能还需要监视输入数据并对异常数据作出反应(例如,使用异常检测算法)。
3.3 基于实例学习 vs 基于模型学习
机器学习还可以就根据它的归纳方法来划分。大多数机器学习的任务是做出预测,这意味着给到大量的训练样本训练模型,系统需要泛化到从未见过的样本。模型在训练集上有好的表现是个好事,但这还不够;目标是模型在新的实例上的表现。
有两种主要的归纳方法:基于实例学习和基于模型学习。
基于实例学习
或许最简单的学习方式就是记忆学习。如果你用这种方式创建一个垃圾邮件过滤器,它只会标记所有与已经由用户标记的邮件相同的邮件。这不是最坏的解决方案,但肯定不是最好的。
不能仅仅是标记与已知邮件相同的邮件,你的垃圾邮件检测程序还应该可以标记与已知邮件相似的邮件。这就需要计算两封邮件的相似程度。一种最基本的相似度测量方式就是计算两封邮件包含相同单词的个数。如果新邮件与已知的一封垃圾邮件相同单词个数较多,那么系统就会判断其也是垃圾邮件。
这种学习方法就叫做基于实例学习:系统把所有训练集样本都存储下来,然后计算新的样本与存储的这些实例的相似度(如下图所示)。
似乎能看到一点趋势。尽管数据有些噪声,但是生活满意度多少与 GDP 呈现线性增长关系。因此,你可以令生活满意度是人均 GDP 的线性函数。这一步称为模型选择:选择一个生活满意度的线性模型,该模型只有一个属性(attribute),即人均 GDP。
这个模型有两个参数:θ0 和 θ1 ,通过调整这些参数,你可以让你的模型表示任何线性函数,如下图所示。
在使用你的模型之前,你需要确定参数值 θ0 和 θ1 让模型表现最好。怎么做呢?你需要制定一个性能评估方法。可以定义一个拟合函数测量模型有多好,也可以定义一个代价函数(loss function)来测量模型有多不好。对于线性回归问题,人们一般使用代价函数来测量线性模型预测值与实际样本值之间的距离,目标就是让这个距离越小越好,最小化。 这就是线性回归算法,根据训练集,线性回归算法能够找到最合适的参数,使得线性模型能最好程度地拟合这些数据。这个过程称为模型训练。在上面这个例子中,使用线性回归算法,得到的最佳参数为:θ0 =4.85,θ1 =4.91×10^−5 。 现在得到的线性模型拟合数据的效果就很好了,如下图所示。
最后,你就可以使用这个模型进行预测了。例如,你想知道 Cyprus 人的生活满意度,但是 OECD 并没有提供这个数据。幸运的是,你可以使用刚刚训练好的模型进行预测:你查到 Cyprus 人均 GDP 是 $22,587,然后根据模型的线性表达式,计算生活满意度为:4.85 + 22,587 × 4.91 × 10^-5 = 5.964.85+22,587×4.91×10^−5 = 5.96。 下面这段代码就是使用 Python 来导入数据集,预处理,创建可视化散点图,然后训练线性模型并作出预测。
# Code example import matplotlib import matplotlib.pyplot as plt import numpy as np import pandas as pd import sklearn.linear_model import os datapath = os.path.join("datasets", "lifesat", "") def prepare_country_stats(oecd_bli, gdp_per_capita): oecd_bli = oecd_bli[oecd_bli["INEQUALITY"]=="TOT"] oecd_bli = oecd_bli.pivot(index="Country", columns="Indicator", values="Value") gdp_per_capita.rename(columns={"2015": "GDP per capita"}, inplace=True) gdp_per_capita.set_index("Country", inplace=True) full_country_stats = pd.merge(left=oecd_bli, right=gdp_per_capita, left_index=True, right_index=True) full_country_stats.sort_values(by="GDP per capita", inplace=True) remove_indices = [0, 1, 6, 8, 33, 34, 35] keep_indices = list(set(range(36)) - set(remove_indices)) return full_country_stats[["GDP per capita", 'Life satisfaction']].iloc[keep_indices] # Load the data oecd_bli = pd.read_csv(datapath + "oecd_bli_2015.csv", thousands=',') gdp_per_capita = pd.read_csv(datapath + "gdp_per_capita.csv",thousands=',',delimiter='\t', encoding='latin1', na_values="n/a") # Prepare the data country_stats = prepare_country_stats(oecd_bli, gdp_per_capita) X = np.c_[country_stats["GDP per capita"]] y = np.c_[country_stats["Life satisfaction"]] # Visualize the data country_stats.plot(kind='scatter', x="GDP per capita", y='Life satisfaction') plt.show() # Select a linear model model = sklearn.linear_model.LinearRegression() # Train the model model.fit(X, y) # Make a prediction for Cyprus X_new = [[22587]] # Cyprus' GDP per capita print(model.predict(X_new)) # outputs [[ 5.96242338]]
[[ 5.96242338]]
值得注意的是,如果我们使用的是基于实例的方法学习,我们发现与 Cyprus 人均 GDP 最接近的国家是 Slovenia(人均 GDP 为 $20,732)。因为 Slovenia 人的生活满意度是 5.7,则可以说 Cyprus 的生活满意度也是 5.7。如果我们选择与 Cyprus GDP 最接近的 3 个国家,分别是 Portugal、Slovenia、Spain,GDP 分别是 5.1、5.7、6.5,则平均计算,得到 Cyprus 的人均 GDP 为 5.77。这两个结果与我们使用基于模型学习的结果很相近。这种实例学习方法就叫做 k-近邻算法。
对应到代码中,如果使用 k-近邻替代线下回归,则只要把下面这条语句:
clf = sklearn.linear_model.LinearRegression()
替换成:
clf = sklearn.neighbors.KNeighborsRegressor(n_neighbors=3)
就好了。
如果一切进展顺利的话,这个模型就可以较好地预测了。如果效果不好,那你还需要使用更多的特征(例如就业率、健康程度、空气质量等),获得更好的训练集,或者使用更强大的模型(例如多项式回归)。
下面对基于模型学习作个总结:
- 选取数据集
- 选择模型
- 在训练集上训练模型(即使用学习算法找到最佳参数,让代价函数最小化)
- 最后,将模型应用到新的样本中,进行预测,希望的得到较好的泛化能力
这就是构建一个典型的机器学习项目的过程。
到目前为止,我们已经介绍了很多内容:你知道了什么是机器学习,为什么机器学习是有用的,机器学习有哪些类型,以及构建机器学习系统的一般流程是什么样的。