机器学习算法一览,应用建议与解决思路

  1. 机器学习算法简述

    1. 1 从机器学习问题角度分类

    2. 2 从算法的功能角度分类

      1. 21 回归算法Regression Algorithms

      2. 22 基于实例的算法Instance-based Algorithms

      3. 23 决策树类算法Decision Tree Algorithms

      4. 24 贝叶斯类算法Bayesian Algorithms

      5. 25 聚类算法Clustering Algorithms

      6. 26 关联规则算法Association Rule Learning Algorithms

      7. 27 人工神经网络类算法Artificial Neural Network Algorithms

      8. 28 深度学习Deep Learning Algorithms

      9. 29 降维算法Dimensionality Reduction Algorithms

      10. 210 模型融合算法Ensemble Algorithms

    3. 3 机器学习算法使用图谱

  2. 机器学习问题解决思路

    1. 1 数据与可视化

    2. 2 机器学习算法选择

      1. 21 过拟合的定位与解决

      2. 22 欠拟合定位与解决

    3. 3 关于大数据样本集和高维特征空间

      1. 31 大数据情形下的模型选择与学习曲线

      2. 32 大数据量下的可视化

    4. 4 损失函数的选择

  3. 总结

1.引言

提起笔来写这篇博客,突然有点愧疚和尴尬。愧疚的是,工作杂事多,加之懒癌严重,导致这个系列一直没有更新,向关注该系列的同学们道个歉。尴尬的是,按理说,机器学习介绍与算法一览应该放在最前面写,详细的应用建议应该在讲完机器学习常用算法之后写,突然莫名奇妙在中间插播这么一篇,好像有点打乱主线。

老话说『亡羊补牢,为时未晚』,前面开头忘讲的东西,咱在这块儿补上。我们先带着大家过一遍传统机器学习算法,基本思想和用途。把问题解决思路和方法应用建议提前到这里的想法也很简单,希望能提前给大家一些小建议,对于某些容易出错的地方也先给大家打个预防针,这样在理解后续相应机器学习算法之后,使用起来也有一定的章法。

2.机器学习算法简述

按照不同的分类标准,可以把机器学习的算法做不同的分类。

2.1 从机器学习问题角度分类

我们先从机器学习问题本身分类的角度来看,我们可以分成下列类型的算法:

  • 监督学习算法

机器学习中有一大部分的问题属于『监督学习』的范畴,简单口语化地说明,这类问题中,给定的训练样本中,每个样本的输入x都对应一个确定的结果y,我们需要训练出一个模型(数学上看是一个xy的映射关系f),在未知的样本x给定后,我们能对结果y做出预测。

这里的预测结果如果是离散值(很多时候是类别类型,比如邮件分类问题中的垃圾邮件/普通邮件,比如用户会/不会购买某商品),那么我们把它叫做分类问题(classification problem);如果预测结果是连续值(比如房价,股票价格等等),那么我们把它叫做回归问题(regression problem)。

有一系列的机器学习算法是用以解决监督学习问题的,比如最经典的用于分类问题的朴素贝叶斯、逻辑回归、支持向量机等等;比如说用于回归问题的线性回归等等。

  • 无监督学习

有另外一类问题,给我们的样本并没有给出『标签/标准答案』,就是一系列的样本。而我们需要做的事情是,在一些样本中抽取出通用的规则。这叫做『无监督学习』。包括关联规则和聚类算法在内的一系列机器学习算法都属于这个范畴。

  • 半监督学习

这类问题给出的训练数据,有一部分有标签,有一部分没有标签。我们想学习出数据组织结构的同时,也能做相应的预测。此类问题相对应的机器学习算法有自训练(Self-Training)、直推学习(Transductive Learning)、生成式模型(Generative Model)等。

总体说来,最常见是前两类问题,而对应前两类问题的一些机器学习算法如下:

2.2 从算法的功能角度分类

我们也可以从算法的共性(比如功能,运作方式)角度对机器学习算法分类。下面我们根据算法的共性去对它们归个类。不过需要注意的是,我们下面的归类方法可能对分类和回归有比较强的倾向性,而这两类问题也是最常遇到的。

2.2.1 回归算法(Regression Algorithms)



回归算法是一种通过最小化预测值与实际结果值之间的差距,而得到输入特征之间的最佳组合方式的一类算法。对于连续值预测有线性回归等,而对于离散值/类别预测,我们也可以把逻辑回归等也视作回归算法的一种,常见的回归算法如下:

  • Ordinary Least Squares Regression (OLSR)

  • Linear Regression

  • Logistic Regression

  • Stepwise Regression

  • Locally Estimated Scatterplot Smoothing (LOESS)

  • Multivariate Adaptive Regression Splines (MARS)

2.2.2 基于实例的算法(Instance-based Algorithms)



这里所谓的基于实例的算法,我指的是我们最后建成的模型,对原始数据样本实例依旧有很强的依赖性。这类算法在做预测决策时,一般都是使用某类相似度准则,去比对待预测的样本和原始样本的相近度,再给出相应的预测结果。常见的基于实例的算法有:

  • k-Nearest Neighbour (kNN)

  • Learning Vector Quantization (LVQ)

  • Self-Organizing Map (SOM)

  • Locally Weighted Learning (LWL)

2.2.3 决策树类算法(Decision Tree Algorithms)



决策树类算法,会基于原始数据特征,构建一颗包含很多决策路径的树。预测阶段选择路径进行决策。常见的决策树算法包括:

  • Classification and Regression Tree (CART)

  • Iterative Dichotomiser 3 (ID3)

  • C4.5 and C5.0 (different versions of a powerful approach)

  • Chi-squared Automatic Interaction Detection (CHAID)

  • M5

  • Conditional Decision Trees

2.2.4 贝叶斯类算法(Bayesian Algorithms)



这里说的贝叶斯类算法,指的是在分类和回归问题中,隐含使用了贝叶斯原理的算法。包括:

  • Naive Bayes

  • Gaussian Naive Bayes

  • Multinomial Naive Bayes

  • Averaged One-Dependence Estimators (AODE)

  • Bayesian Belief Network (BBN)

  • Bayesian Network (BN)

2.2.5 聚类算法(Clustering Algorithms)



聚类算法做的事情是,把输入样本聚成围绕一些中心的『数据团』,以发现数据分布结构的一些规律。常用的聚类算法包括:

  • k-Means

  • Hierarchical Clustering

  • Expectation Maximisation (EM)

2.2.6 关联规则算法(Association Rule Learning Algorithms)



关联规则算法是这样一类算法:它试图抽取出,最能解释观察到的训练样本之间关联关系的规则,也就是获取一个事件和其他事件之间依赖或关联的知识,常见的关联规则算法有:

  • Apriori algorithm

  • Eclat algorithm

2.2.7 人工神经网络类算法(Artificial Neural Network Algorithms)



这是受人脑神经元工作方式启发而构造的一类算法。需要提到的一点是,我把『深度学习』单拎出来了,这里说的人工神经网络偏向于更传统的感知算法,主要包括:

  • Perceptron

  • Back-Propagation

  • Radial Basis Function Network (RBFN)

2.2.8 深度学习(Deep Learning Algorithms)



深度学习是近年来非常火的机器学习领域,相对于上面列的人工神经网络算法,它通常情况下,有着更深的层次和更复杂的结构。有兴趣的同学可以看看我们另一个系列机器学习与计算机视觉,最常见的深度学习算法包括:

  • Deep Boltzmann Machine (DBM)

  • Deep Belief Networks (DBN)

  • Convolutional Neural Network (CNN)

  • Stacked Auto-Encoders

2.2.9 降维算法(Dimensionality Reduction Algorithms)



从某种程度上说,降维算法和聚类其实有点类似,因为它也在试图发现原始训练数据的固有结构,但是降维算法在试图,用更少的信息(更低维的信息)总结和描述出原始信息的大部分内容。
有意思的是,降维算法一般在数据的可视化,或者是降低数据计算空间有很大的作用。它作为一种机器学习的算法,很多时候用它先处理数据,再灌入别的机器学习算法学习。主要的降维算法包括:

  • Principal Component Analysis (PCA)

  • Principal Component Regression (PCR)

  • Partial Least Squares Regression (PLSR)

  • Sammon Mapping

  • Multidimensional Scaling (MDS)

  • Linear Discriminant Analysis (LDA)

  • Mixture Discriminant Analysis (MDA)

  • Quadratic Discriminant Analysis (QDA)

  • Flexible Discriminant Analysis (FDA)

2.2.10 模型融合算法(Ensemble Algorithms)



严格意义上来说,这不算是一种机器学习算法,而更像是一种优化手段/策略,它通常是结合多个简单的弱机器学习算法,去做更可靠的决策。拿分类问题举个例,直观的理解,就是单个分类器的分类是可能出错,不可靠的,但是如果多个分类器投票,那可靠度就会高很多。常用的模型融合增强方法包括:

  • Random Forest

  • Boosting

  • Bootstrapped Aggregation (Bagging)

  • AdaBoost

  • Stacked Generalization (blending)

  • Gradient Boosting Machines (GBM)

  • Gradient Boosted Regression Trees (GBRT)

2.3 机器学习算法使用图谱

scikit-learn作为一个丰富的python机器学习库,实现了绝大多数机器学习的算法,有相当多的人在使用,于是我这里很无耻地把machine learning cheat sheet for sklearn搬过来了,原文可以看这里。哈哈,既然讲机器学习,我们就用机器学习的语言来解释一下,这是针对实际应用场景的各种条件限制,对scikit-learn里完成的算法构建的一颗决策树,每一组条件都是对应一条路径,能找到相对较为合适的一些解决方法,具体如下:

首先样本量如果非常少的话,其实所有的机器学习算法都没有办法从里面『学到』通用的规则和模式,so多弄点数据是王道。然后根据问题是有/无监督学习和连续值/离散值预测,分成了分类聚类回归维度约减四个方法类,每个类里根据具体情况的不同,又有不同的处理方法。

3. 机器学习问题解决思路

上面带着代价走马观花过了一遍机器学习的若干算法,下面我们试着总结总结在拿到一个实际问题的时候,如果着手使用机器学习算法去解决问题,其中的一些注意点以及核心思路。主要包括以下内容:

  • 拿到数据后怎么了解数据(可视化)

  • 选择最贴切的机器学习算法

  • 定位模型状态(过/欠拟合)以及解决方法

  • 大量极的数据的特征分析与可视化

  • 各种损失函数(loss function)的优缺点及如何选择

多说一句,这里写的这个小教程,主要是作为一个通用的建议和指导方案,你不一定要严格按照这个流程解决机器学习问题。

3.1 数据与可视化

我们先使用scikit-learn的make_classification函数来生产一份分类数据,然后模拟一下拿到实际数据后我们需要做的事情。

#numpy科学计算工具箱import numpy as np#使用make_classification构造1000个样本,每个样本有20个featurefrom sklearn.datasets import make_classification
X, y = make_classification(1000, n_features=20, n_informative=2, 
                           n_redundant=2, n_classes=2, random_state=0)#存为dataframe格式from pandas import DataFrame
df = DataFrame(np.hstack((X, y[:, None])),columns = range(20) + ["class"])
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

我们生成了一份包含1000个分类数据样本的数据集,每个样本有20个数值特征。同时我们把数据存储至pandas中的DataFrame数据结构中。我们取前几行的数据看一眼:

df[:6]
  • 1


不幸的是,肉眼看数据,尤其是维度稍微高点的时候,很有可能看花了也看不出看不出任何线索。幸运的是,我们对于图像的理解力,比数字好太多,而又有相当多的工具可以帮助我们『可视化』数据分布。

我们在处理任何数据相关的问题时,了解数据都是很有必要的,而可视化可以帮助我们更好地直观理解数据的分布和特性

数据的可视化有很多工具包可以用,比如下面我们用来做数据可视化的工具包Seaborn。最简单的可视化就是数据散列分布图和柱状图,这个可以用Seanborn的pairplot来完成。以下图中2种颜色表示2种不同的类,因为20维的可视化没有办法在平面表示,我们取出了一部分维度,两两组成pair看数据在这2个维度平面上的分布状况,代码和结果如下:

import matplotlib.pyplot as pltimport seaborn as sns#使用pairplot去看不同特征维度pair下数据的空间分布状况_ = sns.pairplot(df[:50], vars=[8, 11, 12, 14, 19], hue="class", size=1.5)
plt.show()
  • 1

  • 2

  • 3

  • 4

  • 5


我们从散列图和柱状图上可以看出,确实有些维度的特征相对其他维度,有更好的区分度,比如第11维和14维看起来很有区分度。这两个维度上看,数据点是近似线性可分的。而12维和19维似乎呈现出了很高的负相关性。接下来我们用Seanborn中的corrplot来计算计算各维度特征之间(以及最后的类别)的相关性。代码和结果图如下:

import matplotlib.pyplot as plt
plt.figure(figsize=(12, 10))
_ = sns.corrplot(df, annot=False)
plt.show()
  • 1

  • 2

  • 3

  • 4


相关性图很好地印证了我们之前的想法,可以看到第11维特征和第14维特征和类别有极强的相关性,同时它们俩之间也有极高的相关性。而第12维特征和第19维特征却呈现出极强的负相关性。强相关的特征其实包含了一些冗余的特征,而除掉上图中颜色较深的特征,其余特征包含的信息量就没有这么大了,它们和最后的类别相关度不高,甚至各自之间也没什么先惯性。

插一句,这里的维度只有20,所以这个相关度计算并不费太大力气,然而实际情形中,你完全有可能有远高于这个数字的特征维度,同时样本量也可能多很多,那种情形下我们可能要先做一些处理,再来实现可视化了。别着急,一会儿我们会讲到。

3.2 机器学习算法选择

数据的情况我们大致看了一眼,确定一些特征维度之后,我们可以考虑先选用机器学习算法做一个baseline的系统出来了。这里我们继续参照上面提到过的机器学习算法使用图谱。
我们只有1000个数据样本,是分类问题,同时是一个有监督学习,因此我们根据图谱里教的方法,使用LinearSVC(support vector classification with linear kernel)试试。注意,LinearSVC需要选择正则化方法以缓解过拟合问题;我们这里选择使用最多的L2正则化,并把惩罚系数C设为10。我们改写一下sklearn中的学习曲线绘制函数,画出训练集和交叉验证集上的得分:

from sklearn.svm import LinearSVCfrom sklearn.learning_curve import learning_curve#绘制学习曲线,以确定模型的状况def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
                        train_sizes=np.linspace(.1, 1.0, 5)):
    """
    画出data在某模型上的learning curve.
    参数解释
    ----------
    estimator : 你用的分类器。
    title : 表格的标题。
    X : 输入的feature,numpy类型
    y : 输入的target vector
    ylim : tuple格式的(ymin, ymax), 设定图像中纵坐标的最低点和最高点
    cv : 做cross-validation的时候,数据分成的份数,其中一份作为cv集,其余n-1份作为training(默认为3份)
    """

    plt.figure()
    train_sizes, train_scores, test_scores = learning_curve(
        estimator, X, y, cv=5, n_jobs=1, train_sizes=train_sizes)
    train_scores_mean = np.mean(train_scores, axis=1)
    train_scores_std = np.std(train_scores, axis=1)
    test_scores_mean = np.mean(test_scores, axis=1)
    test_scores_std = np.std(test_scores, axis=1)

    plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
                     train_scores_mean + train_scores_std, alpha=0.1,
                     color="r")
    plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
                     test_scores_mean + test_scores_std, alpha=0.1, color="g")
    plt.plot(train_sizes, train_scores_mean, 'o-', color="r",
             label="Training score")
    plt.plot(train_sizes, test_scores_mean, 'o-', color="g",
             label="Cross-validation score")

    plt.xlabel("Training examples")
    plt.ylabel("Score")
    plt.legend(loc="best")
    plt.grid("on") 
    if ylim:
        plt.ylim(ylim)
    plt.title(title)
    plt.show()#少样本的情况情况下绘出学习曲线plot_learning_curve(LinearSVC(C=10.0), "LinearSVC(C=10.0)",
                    X, y, ylim=(0.8, 1.01),
                    train_sizes=np.linspace(.05, 0.2, 5))
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48


这幅图上,我们发现随着样本量的增加,训练集上的得分有一定程度的下降,交叉验证集上的得分有一定程度的上升,但总体说来,两者之间有很大的差距,训练集上的准确度远高于交叉验证集。这其实意味着我们的模型处于过拟合的状态,也即模型太努力地刻画训练集,一不小心把很多噪声的分布也拟合上了,导致在新数据上的泛化能力变差了。

3.2.1 过拟合的定位与解决

问题来了,过拟合咋办?
针对过拟合,有几种办法可以处理:

  • 增大样本量

这个比较好理解吧,过拟合的主要原因是模型太努力地去记住训练样本的分布状况,而加大样本量,可以使得训练集的分布更加具备普适性,噪声对整体的影响下降。恩,我们提高点样本量试试:

#增大一些样本量plot_learning_curve(LinearSVC(C=10.0), "LinearSVC(C=10.0)",
                    X, y, ylim=(0.8, 1.1),
                    train_sizes=np.linspace(.1, 1.0, 5))
  • 1

  • 2

  • 3

  • 4


是不是发现问题好了很多?随着我们增大训练样本量,我们发现训练集和交叉验证集上的得分差距在减少,最后它们已经非常接近了。增大样本量,最直接的方法当然是想办法去采集相同场景下的新数据,如果实在做不到,也可以试试在已有数据的基础上做一些人工的处理生成新数据(比如图像识别中,我们可能可以对图片做镜像变换、旋转等等),当然,这样做一定要谨慎,强烈建议想办法采集真实数据。

  • 减少特征的量(只用我们觉得有效的特征)

比如在这个例子中,我们之前的数据可视化和分析的结果表明,第11和14维特征包含的信息对识别类别非常有用,我们可以只用它们。

plot_learning_curve(LinearSVC(C=10.0), "LinearSVC(C=10.0) Features: 11&14", X[:, [11, 14]], y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))
  • 1


从上图上可以看出,过拟合问题也得到一定程度的缓解。不过我们这是自己观察后,手动选出11和14维特征。那能不能自动进行特征组合和选择呢,其实我们当然可以遍历特征的组合样式,然后再进行特征选择(前提依旧是这里特征的维度不高,如果高的话,遍历所有的组合是一个非常非常非常耗时的过程!!):

from sklearn.pipeline import Pipelinefrom sklearn.feature_selection import SelectKBest, f_classif# SelectKBest(f_classif, k=2) 会根据Anova F-value选出 最好的k=2个特征plot_learning_curve(Pipeline([("fs", SelectKBest(f_classif, k=2)), # select two features
                               ("svc", LinearSVC(C=10.0))]), "SelectKBest(f_classif, k=2) + LinearSVC(C=10.0)", X, y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6


如果你自己跑一下程序,会发现在我们自己手造的这份数据集上,这个特征筛选的过程超级顺利,但依旧像我们之前提过的一样,这是因为特征的维度不太高。
从另外一个角度看,我们之所以做特征选择,是想降低模型的复杂度,而更不容易刻画到噪声数据的分布。从这个角度出发,我们还可以有(1)多项式你和模型中降低多项式次数 (2)神经网络中减少神经网络的层数和每层的结点数 (c)SVM中增加RBF-kernel的bandwidth等方式来降低模型的复杂度。
话说回来,即使以上提到的办法降低模型复杂度后,好像能在一定程度上缓解过拟合,但是我们一般还是不建议一遇到过拟合,就用这些方法处理,优先用下面的方法:

  • 增强正则化作用(比如说这里是减小LinearSVC中的C参数)
    正则化是我认为在不损失信息的情况下,最有效的缓解过拟合现象的方法。

plot_learning_curve(LinearSVC(C=0.1), "LinearSVC(C=0.1)", X, y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))
  • 1


调整正则化系数后,发现确实过拟合现象有一定程度的缓解,但依旧是那个问题,我们现在的系数是自己敲定的,有没有办法可以自动选择最佳的这个参数呢?可以。我们可以在交叉验证集上做grid-search查找最好的正则化系数(对于大数据样本,我们依旧需要考虑时间问题,这个过程可能会比较慢):

from sklearn.grid_search import GridSearchCV
estm = GridSearchCV(LinearSVC(), 
                   param_grid={"C": [0.001, 0.01, 0.1, 1.0, 10.0]})
plot_learning_curve(estm, "LinearSVC(C=AUTO)", 
                    X, y, ylim=(0.8, 1.0),
                    train_sizes=np.linspace(.05, 0.2, 5))print "Chosen parameter on 100 datapoints: %s" % estm.fit(X[:500], y[:500]).best_params_
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

在500个点得到的结果是:{‘C’: 0.01}
使用新的C参数,我们再看看学习曲线:


对于特征选择的部分,我打算多说几句,我们刚才看过了用sklearn.feature_selection中的SelectKBest来选择特征的过程,也提到了在高维特征的情况下,这个过程可能会非常非常慢。那我们有别的办法可以进行特征选择吗?比如说,我们的分类器自己能否甄别那些特征是对最后的结果有益的?这里有个实际工作中用到的小技巧。

猜你喜欢

转载自weitao1026.iteye.com/blog/2319962