接下来我们开始构建简单的回归模型,并使用sklearn中自带的数据集进行演示。我们会介绍三种常用的回归模型,分别是线性回归,回归树和SVM支持向量机模型。
对于任何一个机器学习模型,我们都可以从三个方面来进行解剖,即:
- 模型(Model)
机器学习问题通常都要都要转化为一个数学模型来求解。模型是从数据里抽象出来的。在进行数据分析时,我们通常手上只有数据,从数据中抽取的规律就是模型。一般情况,该模型表达了从特征到标签(数值)的映射关系。给定一个输入,经过模型后就可以得到一个输出。模型可以是确定性的,也可以是随机的,无所谓,总之用数学可以描述,只要数学可以描述的,就可以进行预测分析。从这点上来说,机器学习,就是让机器通过数据产生模型。
- 模型(Model)
-
- 策略(Strategy)
知道了模型的概念,那么如何通过数据来构造模型呢? 如何构造就是我们所谓的策略。
不同的策略,对应不同的模型的比较标准和选择标准。就跟选班干部一样,选帅的,好,那就让吴彦祖当班长,选逗比的,也许选出来的就是王宝强,选会唱歌的,没准是周杰伦…好,所以最终确定的模型是什么,实际上就跟两件事儿有关:
- 我们拿到的数据是什么?
- 我们选择模型的策略是什么?
- 策略(Strategy)
说道策略,一般会讲到,经验风险最小化作为常用的标准。经验风险最小是指,用这个模型,套到已有的观测数据上,基本上是靠谱的。这也是大多数时候我们在机器学习时候有意或无意就用到的准侧。经验风险最小化是一个参数优化的过程,我们需要构造一个损失函数来描述经验风险,损失函数可以理解为我们预测一个数据错了给我们带来的代价。每个人对损失函数的定义都不同,所以优化出来的结果也不同,这也导致最终我们学习到的模型会各种各样,解决一个问题的方案有多种多样…
- 3 算法(Algorithm)
我们通过策略实现模型的方法就是算法
我们有了数据,有了学习模型的策略,然后就要开始去构造模型了,如果模型的基本形式有了,就是一个优化模型参数的问题了。如果学习过确定性模型的朋友,优化并不陌生,但是优化过程往往是复杂的,面对复杂的数学优化问题我们通常难以通过简单的求导获得最终的结果,所以就要构造一系列的算法。
我们的目标是让算法尽量高效,更少的计算机内存代价,更快的运算速度,更有效的参数优化结果…
模型、策略和算法这三样东西好比打开机器学习模型的三把金钥匙,明白了这三把钥匙的含义,也就能深入理解这个模型了。下面分别阐述
1. 线性回归
模型:对于线性回归的模型,一般可以用如下的矩阵来描述:
假设:数据集,
,
假设X和Y之间存在线性关系,模型的具体形式为:
策略:
我们采用真实值与线性回归模型的预测值
之间的差距,在这里我们和使用二范数的平方和L(w)来描述这种差距,即:
我们需要找到使得L(w)最小时对应的参数w,即:
所以,我们策略就是寻找最小的w组合,使得最小。
如何寻找呢?这就是算法了。
一般我们采用梯度下降算法来解决,及对求导。
线性模型形式简单、易于建模。但是却蕴含着机器学习中一些重要的基本思想。许多功能更为强大的非线性模型可以在线性模型的基础上通过引入层级结构或高维映射而得。此外,由于w直观表达了各属性在预测中的重要性,因此线性模型有很好的可解释性。并且经常作为各个系统的基线模型。
2. 回归树
决策树(Decision Tree)是一种基本的分类与回归方法,当决策树用于分类时称为分类树,用于回归时称为回归树。
基于树的回归方法主要是依据分层和分割的方式将特征空间划分为一系列简单的区域。对某个给定的待预测的自变量,用他所属区域中训练集的平均数或者众数对其进行预测。由于划分特征空间的分裂规则可以用树的形式进行概括,因此这类方法称为决策树方法。决策树由结点(node)和有向边(diredcted edge)组成。结点有两种类型:内部结点(internal node)和叶结点(leaf node)。内部结点表示一个特征或属性,叶结点表示一个类别或者某个值。区域 𝑅1,𝑅2R1,R2 等称为叶节点,将特征空间分开的点为内部节点。
建立回归树的过程大致可以分为以下两步:
- 1. 将自变量的特征空间(即)的可能取值构成的集合分割成J个互不重叠的区域
。
- 2. 对落入区域的每个观测值作相同的预测,预测值等于
上训练集的因变量的简单算术平均。
具体来说,就是:
a. 选择最优切分特征j以及该特征上的最优点s:
遍历特征j以及固定j后遍历切分点s,选择使得下式最小的(j,s)
b. 按照(j,s)分裂特征空间:
c. 继续调用步骤1,2直到满足停止条件,就是每个区域的样本数小于等于5。
d. 将特征空间划分为J个不同的区域,生成回归树:
树模型的优缺点:
- 树模型的解释性强,在解释性方面可能比线性回归还要方便。
- 树模型更接近人的决策方式。
- 树模型可以用图来表示,非专业人士也可以轻松解读。
- 树模型可以直接做定性的特征而不需要像线性回归一样哑元化。
- 树模型能很好处理缺失值和异常值,对异常值不敏感,但是这个对线性模型来说却是致命的。
- 树模型的预测准确性一般无法达到其他回归模型的水平,但是改进的方法很多。
3. 支持向量机回归(SVR)
支持向量机模型可以之前的文章: //www.greatytc.com/p/b6b8ae283471
下面通过sklearn中的数据集进行演示,我们还是用之前房价预测数据集
from sklearn import datasets
boston = datasets.load_boston()
X = boston.data
y = boston.target
features = boston.feature_names
boston_data = pd.DataFrame(X,columns=features)
boston_data["Price"] = y
boston_data.head()
下面分别导入线性回归、决策树、和SVR模型进行训练和预测:
from sklearn.tree import DecisionTreeRegressor # 导入回归树模型
from sklearn.svm import SVR # 支持向量机模型
lin_reg = linear_model.LinearRegression() # 创建线性回归的类
lin_reg.fit(X,y) # 输入特征X和因变量y进行训练
print('使用线性回归模型:')
print("模型系数:",lin_reg.coef_) # 输出模型的系数
print("模型得分:",lin_reg.score(X,y)) # 输出模型的决定系数R^2
print('-' * 20)
reg_tree = DecisionTreeRegressor(criterion = "mse",min_samples_leaf = 5)
reg_tree.fit(X,y)
print('使用决策树回归模型:')
print('模型的得分为:', reg_tree.score(X,y))
print('-' * 20)
from sklearn.preprocessing import StandardScaler # 标准化数据
from sklearn.pipeline import make_pipeline # 使用管道,把预处理和模型形成一个流程
print('使用SVR回归模型:')
reg_svr = make_pipeline(StandardScaler(), SVR(C=1.0, epsilon=0.2))
reg_svr.fit(X, y)
print("使用SVR模型的分数 :" , reg_svr.score(X,y))
>>>
使用线性回归模型:
模型系数: [-1.08011358e-01 4.64204584e-02 2.05586264e-02 2.68673382e+00
-1.77666112e+01 3.80986521e+00 6.92224640e-04 -1.47556685e+00
3.06049479e-01 -1.23345939e-02 -9.52747232e-01 9.31168327e-03
-5.24758378e-01]
模型得分: 0.7406426641094095
--------------------
使用决策树回归模型:
模型的得分为: 0.9376307599929274
--------------------
使用SVR回归模型:
使用SVR模型的分数 : 0.7024525421955277
- 模型优化
当完成一个完整的模型构建训练并预测出结果后,相当与我们完成了一个机器学习的baseline,剩下的部分就是如何优化提高模型分数了。这一部分一般可以从两个方面着手:
- 输入数据的处理,包含归一化以及更好的特征工程
- 模型超参数的优化
下面简要说说明一下超参数优化
首先要明白参数和超参数的意义 - 参数与超参数:
我们很自然的问题就是岭回归中的参数和参数w之间有什么不一样?事实上,参数w是我们通过设定某一个具体的
后使用类似于最小二乘法、梯度下降法等方式优化出来的,我们总是设定了
是多少后才优化出来的参数w。因此,类似于参数w一样,使用最小二乘法或者梯度下降法等最优化算法优化出来的数我们称为参数,类似于
一样,我们无法使用最小二乘法或者梯度下降法等最优化算法优化出来的数我们称为超参数。
模型参数是模型内部的配置变量,其值可以根据数据进行估计。- 进行预测时需要参数。
- 它参数定义了可使用的模型。
- 参数是从数据估计或获悉的。
- 参数通常不由编程者手动设置。
- 参数通常被保存为学习模型的一部分。
- 参数是机器学习算法的关键,它们通常由过去的训练数据中总结得出 。
模型超参数是模型外部的配置,其值无法从数据中估计。 - 超参数通常用于帮助估计模型参数。
- 超参数通常由人工指定。
- 超参数通常可以使用启发式设置。
- 超参数经常被调整为给定的预测建模问题。
我们前面(4)部分的优化都是基于模型本身的具体形式的优化,那本次(5)调整的内容是超参数,也就是取不同的超参数的值对于模型的性能有不同的影响。
- 网格搜索GridSearchCV():
网格搜索:https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html?highlight=gridsearchcv#sklearn.model_selection.GridSearchCV
网格搜索结合管道:https://scikit-learn.org/stable/auto_examples/compose/plot_compare_reduction.html?highlight=gridsearchcv
网格搜索的思想非常简单,比如你有2个超参数需要去选择,那你就把所有的超参数选择列出来分别做排列组合。举个例子:和
,你可以做一个排列组合,即:{[0.01,0.01],[0.01,0.1],[0.01,1],[0.1,0.01],[0.1,0.1],[0.1,1.0],[1,0.01],[1,0.1],[1,1]} ,然后针对每组超参数分别建立一个模型,然后选择测试误差最小的那组超参数。换句话说,我们需要从超参数空间中寻找最优的超参数,很像一个网格中找到一个最优的节点,因此叫网格搜索。
- 随机搜索 RandomizedSearchCV() :
https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.RandomizedSearchCV.html?highlight=randomizedsearchcv#sklearn.model_selection.RandomizedSearchCV
网格搜索相当于暴力地从参数空间中每个都尝试一遍,然后选择最优的那组参数,这样的方法显然是不够高效的,因为随着参数类别个数的增加,需要尝试的次数呈指数级增长。有没有一种更加高效的调优方式呢?那就是使用随机搜索的方式,这种方式不仅仅高校,而且实验证明,随机搜索法结果比稀疏化网格法稍好(有时候也会极差,需要权衡)。参数的随机搜索中的每个参数都是从可能的参数值的分布中采样的。与网格搜索相比,这有两个主要优点:- 可以独立于参数数量和可能的值来选择计算成本。
- 添加不影响性能的参数不会降低效率。
# 我们先来对未调参的SVR进行评价:
from sklearn.svm import SVR # 引入SVR类
from sklearn.pipeline import make_pipeline # 引入管道简化学习流程
from sklearn.preprocessing import StandardScaler # 由于SVR基于距离计算,引入对数据进行标准化的类
from sklearn.model_selection import GridSearchCV # 引入网格搜索调优
from sklearn.model_selection import cross_val_score # 引入K折交叉验证
from sklearn import datasets
boston = datasets.load_boston() # 返回一个类似于字典的类
X = boston.data
y = boston.target
features = boston.feature_names
pipe_SVR = make_pipeline(StandardScaler(),
SVR())
score1 = cross_val_score(estimator=pipe_SVR,
X = X,
y = y,
scoring = 'r2',
cv = 10) # 10折交叉验证
print("CV accuracy: %.3f +/- %.3f" % ((np.mean(score1)),np.std(score1)))
>>>
CV accuracy: 0.187 +/- 0.649
下面我们使用网格搜索来对SVR调参:
# 下面我们使用网格搜索来对SVR调参:
from sklearn.pipeline import Pipeline
pipe_svr = Pipeline([("StandardScaler",StandardScaler()),
("svr",SVR())])
param_range = [0.0001,0.001,0.01,0.1,1.0,10.0,100.0,1000.0]
param_grid = [{"svr__C":param_range,"svr__kernel":["linear"]}, # 注意__是指两个下划线,一个下划线会报错的
{"svr__C":param_range,"svr__gamma":param_range,"svr__kernel":["rbf"]}]
gs = GridSearchCV(estimator=pipe_svr,
param_grid = param_grid,
scoring = 'r2',
cv = 10) # 10折交叉验证
gs = gs.fit(X,y)
print("网格搜索最优得分:",gs.best_score_)
print("网格搜索最优参数组合:\n",gs.best_params_)
>>>
网格搜索最优得分: 0.6081303070817233
网格搜索最优参数组合:
{'svr__C': 1000.0, 'svr__gamma': 0.001, 'svr__kernel': 'rbf'}