【Python机器学习】浅谈监督学习及经典算法

监督学习概述

监督机器学习问题主要有两种,分别是分类和回归

  • 分类问题的目标是预测类别标签

  • 回归任务的目标是预测一个连续值(预测一个数值)

泛化、过拟合与欠拟合

一个模型能够对没见过的数据做出准确预测,我们就说它能够从训练集泛化到测试集。

过拟合:构建一个对现有信息量来说过于复杂的模型

欠拟合:选择过于简单的模型

模型复杂度与训练数据集中输入的变化密切相关

复杂度更小的模型意味着在训练集上的性能更差,但泛化性能更好。

常用的机器学习算法

fit、predict和score方法是scikit-learn监督学习模型中最常用的接口

scikit-learn总是将从训练数据中得出的值保存在以下划线结尾的属性中。

一、k-NN算法

k-NN算法可以说是最简单的机器学习算法。构建模型只需要保存训练数据集即可。想要对新数据点做出预测,算法会在训练数据集中找到最近的数据点,算它的“最近邻”。

k近邻算法很容易理解,但由于预测速度慢且不能处理具有很多特征的数据集,所以在实践中往往不会用

k近邻分类算法

根据新数据点在训练集中距离最近的邻居来进行预测。该算法在KNeighborsClassifier类中实现,里面既包含构建模型的算法,也包含利用模型进行预测的算法。处理数据时,先将类实例化,并设定参数。然后调用fit方法来构建模型,传入训练数据(X_train)和训练输出(y_trian)作为参数;最后,用score方法来评估模型(该方法计算的是模型精度)

应用scikit-learn中任何机器学习算法的核心代码:

X_train, X_test, y_train, y_test = train_test_split(
iris_dataset['data'],iris_dataset['target'],random_state=0)
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train, y_train)
print("Test set score:{:.2f}".format(knn.score(X_test, y_test)))

k近邻回归

用于回归的k近邻算法在scikit-learn的KNeighborsRegressor类中实现。其用法与KNeighborsClassifier类似:

from sklearn.neighbors import KNeighborsRegressor

X, y = mglearn.datasets.make_wave(n_samples=40)
#将wave数据集分为训练集和测试集
X_train, X_test, y_train,y_test = train_test_split(X, y, random_state=0)
#模型实例化,并将邻居个数设为3
reg = KNeighborsRegressor(n_neighbors=3)
#利用训练数据和训练目标值来拟合模型
reg.fit(X_train, y_train)

对于回归问题,可以用score方法来评估模型,这一方法返回的是R^2分数。

reg.score(X_test, y_test)

二、线性模型

线性模型是在实践中广泛使用的一类模型,几十年来被广泛研究。

线性模型利用输入特征的线性函数进行预测.

如果特征数量大于样本数量,线性模型的表现通常都很好。

回归问题,线性模型预测的一般公式如下:

ŷ=w[0] *x[0] +w[1] *x[1] +…+w[p] *x[p] +b

预测的响应值看作输入特征的加权求和,权重由w的元素给出(可以取负值)。

用于回归的线性模型可以表示为:对单一特征的预测结果是一条直线,两个特征时是一个平面,或者在更高维度(即更多特征)时是一个超平面。

“斜率”参数(w,也叫作权重或系数)被保存在coef_属性中,而偏移或截距(b)被保存在intercept_属性中

常见的线性回归模型

线性回归(OLS) Linear Regression算法

线性回归,或者普通最小二乘法(ordinary least squares,OLS),是回归问题最简单也最经典的线性方法。线性回归寻找参数w和b,使得对训练集的预测值与真实的回归目标值y之间的均方误差最小。

from sklearn.linear_model import LinearRegression
岭回归

岭回归也是一种用于回归的线性模型,它的预测公式与普通最小二乘法相同。岭回归在linear_model.Ridge中实现。

from sklearn.linear_model import Ridge

Ridge是一种约束更强的模型,所以更不容易过拟合。

Ridge模型在模型的简单性(系数都接近于0)与训练集性能之间做出权衡。简单性和训练集性能二者对于模型的重要程度可以由用户通过设置alpha参数来指定。alpha的最佳设定值取决于用到的具体数据集。增大alpha会使得系数更加趋向于0,从而降低训练集性能,但可能会提高泛化性能。

无论是岭回归还是线性回归,所有数据集大小对应的训练分数都要高于测试分数。岭回归是正则化的,因此它的训练分数要整体低于线性回归的训练分数。但岭回归的测试分数要更高,特别是对较小的子数据集。

如果有足够多的训练数据,正则化变得不那么重要,并且岭回归和线性回归将具有相同的性能。

Lasso正则化

另一种正则化的线性回归是Lasso,但用到的方法不同,叫作L1正则化。

from sklearn.linear_model import Lasso

Lasso在训练集与测试集上的表现都很差。这表示存在欠拟合。它只选择了一部分输入特征Lasso也有一个正则化参数alpha,可以控制系数趋向于0的强度。但如果把alpha设得太小,那么就会消除正则化的效果,并出现过拟合。

实践中,在Lasso和岭回归模型中一般首选岭回归。

用于二分类的线性模型

ŷ=w[0] *x[0] +w[1] *x[1] +…+w[p] *x[p] +b> 0

为预测设置阈值(0):如果函数值小于0,我们就预测类别-1;如果函数值大于0,我们就预测类别+1

对于用于分类的线性模型,决策边界是输入的线性函数。换句话说,(二元)线性分类器是利用直线、平面或超平面来分开两个类别的分类器。

用于分类的线性模型在低维空间中看起来可能非常受限,决策边界只能是直线或平面

最常见的两种线性分类算法是Logistic回归(logistic regression)和线性支持向量机(linearsupport vector machine,线性SVM),前者在linear_model.LogisticRegression中实现,后者在svm.LinearSVC(SVC代表支持向量分类器)中实现。

from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC

LinearSVC和LogisticRegression得到的决策边界都是直线,将顶部归为类别1的区域和底部归为类别0的区域分开了(换句话说,对于每个分类器而言,位于黑线上方的新数据点都会被划为类别1,而在黑线下方的点都会被划为类别0。)

LogisticRegression默认应用L2正则化

LogisticRegression和LinearSVC决定正则化强度的权衡参数叫作C(默认值C=1)

C值越大,对应的正则化越弱。换句话说,如果参数C值较大,那么LogisticRegression和LinearSVC将尽可能将训练集拟合到最好,而如果C值较小,那么模型更强调使系数向量(w)接近于0。较小的C值可以让算法尽量适应“大多数”数据点,而较大的C值更强调每个数据点都分类正确的重要性。

在图像上:C值很小,对应强正则化。大部分属于类别0的点都位于底部,大部分属于类别1的点都位于顶部。C值稍大,模型更关注两个分类错误的样本,使决策边界的斜率变大。C值非常大,使得决策边界的斜率也很大,模型对类别0中尽量使所有点的分类都正确,但很可能过拟合。

用于多分类的线性模型

对每个类别都学习一个二分类模型,将这个类别与所有其他类别尽量分开,这样就生成了与类别个数一样多的二分类模型。在测试点上运行所有二类分类器来进行预测。在对应类别上分数最高的分类器“胜出”,将这个类别标签返回作为预测结果。

每个类别都对应一个二类分类器,这样每个类别也都有一个系数(w)向量和一个截距(b)。对每个类别都有一个系数向量和一个截距,也使用了相同的预测方法。

线性模型的主要参数是正则化参数,在回归模型中叫作alpha,在LinearSVC和LogisticRegression中叫作C。

alpha值较大或C值较小,说明模型比较简单。

假定只有几个特征是真正重要的,那么你应该用L1正则化,否则应默认使用L2正则化。如果模型的可解释性很重要的话,使用L1也会有帮助。

三、决策树

决策树是广泛用于分类和回归的模型。本质上是从一层层的if/else问题中进行学习,并得出结论。

决策树预测

学习决策树,就是学习一系列if/else问题(经过一系列测试),使之能够以最快的速度得到正确答案。为了构造决策树,算法搜遍所有可能的测试,找出对目标变量来说信息量最大的那一个。

对数据反复进行递归划分,直到划分后的每个区域(决策树的每个叶结点)只包含单一目标值(单一类别或单一回归值)。想要对新数据点进行预测,首先要查看这个点位于特征空间划分的哪个区域,然后将该区域的多数目标值(如果是纯的叶结点,就是单一目标值)作为预测结果。从根结点开始对树进行遍历就可以找到这一区域,每一步向左还是向右取决于是否满足相应的测试

决策树回归

方法:基于每个结点的测试对树进行遍历,最终找到新数据点所属的叶结点。这一数据点的输出即为此叶结点中所有训练点的平均目标值。

决策树的复杂度

如果树中某个叶结点所包含数据点的目标值都相同,那么这个叶结点就是纯的。构造决策树直到所有叶结点都是纯的叶结点,这会导致模型非常复杂,并且对训练数据高度过拟合。(纯叶结点的存在说明这棵树在训练集上的精度是100%。训练集中的每个数据点都位于分类正确的叶结点中。)

防止过拟合有两种常见的策略:一种是及早停止树的生长,也叫预剪枝(pre-pruning)【预剪枝的限制条件可包括:限制树的最大深度、限制叶结点的最大数目,或者规定一个结点中数据点的最小数目来防止继续划分。】;另一种是先构造树,但随后删除或折叠信息量很少的结点,也叫后剪枝(post-pruning)或剪枝

scikit-learn的决策树在DecisionTreeRegressor类和DecisionTreeClassifier类中实现(scikit-learn只实现了预剪枝,没有实现后剪枝。)

from sklearn.tree import DecisionTreeRegressor
from sklearn.tree import DecisionTreeClassifier

DTC未剪枝的树容易过拟合,对新数据的泛化性能不佳。将预剪枝应用在决策树上,可以在完美拟合训练数据之前阻止树的展开。一种方法是限制树的深度可以减少过拟合。这会降低训练集的精度,但可以提高测试集的精度。

分析决策树

利用tree模块的export_graphviz函数来将树可视化

from sklearn.tree import export_graphviz

树的可视化有助于深入理解算法是如何进行预测的,也是易于向非专家解释的机器学习算法的优秀示例。

生成的图中,每个结点的samples给出了该结点中的样本个数,values给出的是每个类别的样本个数。

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

cancer = load_breast_cancer()	#通过数据集导入数据

#划分训练集、测试集
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=42)	

tree = DecisionTreeClassifier(random_state=0)
tree.fit(X_train, y_train)	#通过训练集构建决策树模型

#查看模型精度
print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))

#可视化树
from sklearn.tree import export_graphviz
export_graphviz(tree, out_file="tree.dot", class_names=["malignant","benign"],
feature_names=cancer.feature_names, impurity=False, filled=True)

import graphviz
with open("tree.dot") as f:
    dot_graph = f.read()
graphviz.Source(dot_graph)

树的特征重要性

总结树的工作原理最常用的是特征重要性,它为每个特征对树的决策的重要性进行排序(对于每个特征来说,它都是一个介于0和1之间的数字,其中0表示“根本没用到”,1表示“完美预测目标值”)

如果某个特征的feature_importance_很小,并不能说明这个特征没有提供任何信息。这只能说明该特征没有被树选中,可能是因为另一个特征也包含了同样的信息。

与线性模型的系数不同,特征重要性始终为正数,也不能说明该特征对应哪个类别。

所有基于树的模型都存在的缺点:一旦输入超出了模型训练数据的范围,模型就只能持续预测最后一个已知数据点。树不能在训练数据的范围之外生成“新的”响应。

控制决策树模型复杂度的参数是预剪枝参数,它在树完全展开之前停止树的构造。选择一种预剪枝策略(设置max_depth、max_leaf_nodes或min_samples_leaf)足以防止过拟合。

决策树有两个优点:一是得到的模型很容易可视化,非专家也很容易理解(至少对于较小的树而言);二是算法完全不受数据缩放的影响

决策树的主要缺点在于,即使做了预剪枝,它也经常会过拟合,泛化性能很差。

四、决策树集成

大多数应用中,往往使用下面介绍的集成方法来替代单棵决策树。有两种集成模型对大量分类和回归的数据集都是有效的,二者都以决策树为基础,分别是随机森林(random forest)梯度提升决策树(gradient boosted decision tree)

下面简要介绍随机森林:

随机森林

随机森林本质上是许多决策树的集合,其中每棵树都和其他树略有不同。背后的思想是,每棵树的预测可能都相对较好,但可能对部分数据过拟合,对这些树的结果取平均值来降低过拟合。

随机森林中树的随机化方法有两种:一种是通过选择用于构造树的数据点,另一种是通过选择每次划分测试的特征。

在没有调节任何参数的情况下,随机森林的精度为97%.随机森林的默认参数通常就已经可以给出很好的结果。

构造随机森林的步骤:
确定树的个数

构造前,需要确定用于构造的树的个数(RandomForestRegressor或RandomForestClassifier的n_estimators参数).树在构造时彼此完全独立,算法对每棵树进行不同的随机选择,以确保树和树之间是有区别的。

自助采样

构造一棵树,首先要对数据进行自助采样。从n_samples个数据点中有放回地(即同一样本可以被多次抽取)重复随机抽取一个样本,共抽取n_samples次。这样会创建一个与原数据集大小相同的数据集,但有些数据点会缺失(大约三分之一),有些会重复。

构造决策树

接下来,基于这个新创建的数据集来构造决策树(此过程中的一个关键参数是max_features)。在每个结点处,算法随机选择特征的一个子集,并对其中一个特征寻找最佳测试,而不是对每个结点都寻找最佳测试。选择的特征个数由max_features参数来控制。每个结点中特征子集的选择相互独立的,树的每个结点可以使用特征的不同子集来做出决策。

max_features决定每棵树的随机性大小,较小的max_features可以降低过拟合。一般来说,好的经验就是使用默认值:对于分类,默认值是max_features=sqrt(n_features);对于回归,默认值是max_features=n_features。

由于使用了自助采样,随机森林中构造每棵决策树的数据集都是略有不同的。由于每个结点的特征选择,每棵树中的每次划分都是基于特征的不同子集。这两种方法共同保证随机森林中所有树都不相同。

应用随机森林

利用随机森林进行预测,算法首先对森林中的每棵树进行预测

对于回归问题,对这些结果取平均值作为最终预测

from sklearn.ensemble import RandomForestRegresso

对于分类问题,用“软投票”(soft voting)策略。即每个算法做出“软”预测,给出每个可能的输出标签的概率。对所有树的预测概率取平均值,然后将概率最大的类别作为预测结果。

from sklearn.ensemble import RandomForestClassifier

随机森林可以给出特征重要性,计算方法是将森林中所有树的特征重要性求和并取平均。

#可视化特征重要性函数 (以cancer数据为例,其他数据改名即可)
def plot_feature_importances_cancer(model):
    n_features = cancer.data.shape[1]
    plt.barh(range(n_features), model.feature_importances_, align='center')
    plt.yticks(np.arange(n_features), cancer.feature_names)
    plt.xlabel("Feature importance")
    plt.ylabel("Feature")

随机森林给了“worst radius”(最大半径)特征很大的重要性,但从总体来看,实际上却选择“worst perimeter”(最大周长)作为信息量最大的特征。

回归和分类的随机森林是目前应用最广泛的机器学习方法之一。这种方法非常强大,通常不需要反复调节参数就可以给出很好的结果,也不需要对数据进行缩放。随机森林比单棵树更能从总体把握数据的特征。

随机森林本质上是随机的,设置不同的随机状态(或者不设置random_state参数)可以彻底改变构建的模型。森林中的树越多,它对随机状态选择的鲁棒性就越好。如果你希望结果可以重现,固定random_state是很重要的。

随机森林需要调节的重要参数有n_estimators和max_features,可能还包括预剪枝选项(如max_depth)。n_estimators总是越大越好。对更多的树取平均可以降低过拟合,从而得到鲁棒性更好的集成。不过收益是递减的,而且树越多需要的内存也越多,训练时间也越长。

你可能感兴趣的:(Python,python,机器学习,算法,监督学习)