集成学习
1、机器学习的主要任务
我理解的机器学习就是,如何通过设计算法使得机器掌握学习的能力,发现数据中的规律。今天主要来熟悉机器学习的主要任务。具体地说,从有无因变量的角度出发,可以将机器学习任务分为有监督学习和无监督学习。其次,在有监督学习的大框架下,我们又可以根据因变量是否连续将问题分为回归问题和分类问题。下面我们可以来看一些例子(例子代码由Datawhale团队提供)
首先导入相应的包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use("ggplot")
import seaborn as sns
1.1监督学习
1.1.1回归
回归问题我们需要的数据集是sklearn内置数据集Boston房价数据集。这是一个很著名的数据集。首先将数据集分为因变量和自变量,并将其转化为dataframe格式。
from sklearn import datasets
boston = datasets.load_boston()
X = boston.data
y = boston.target
features = boston.features_name
boston_data = pd.DataFrame(X,columns=features)
boston["price"] = y
下面来做一些可以展示因变量和自变量关系的可视化,来使得我们更好地理解回归问题数据的分布形式。因为自变量数目较多,不可能同时展示,我们选取了NOX(一氧化氮浓度)来展示其与因变量(price)之间的关系。
sns.scatterplot(boston_data["NOX"], boston_data["price"],color='"r", alpha=0.6)
plt.title("price~NOX")
plt.show()
可以看出,因变量price是一个连续型的变量。因此为一个回归问题。
1.1.2 分类
回归问题我们需要的数据集是sklearn内置数据集iris数据集。这是一个很著名的数据集。首先将数据集分为因变量和自变量,并将其转化为dataframe格式。
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
features = iris.feature_names
iris_data = pd.DataFrame(X, colomuns= features)
iris_data["target"] = y
iris数据集中的因变量为三种不同的花的种类,自变量为sepal的长度宽度以及petal的长度宽度。我们现在以sepal的长度宽度为坐标轴的两轴分别展示三种不同iris的数据值。
marker = ['s','x','o']
for index,c in enumerate(np.unique(y)):
plt.scatter(x=iris_data.loc[y==c,'sepal length(cm)'],y=iris_data.loc[y==c,'sepal width(cm)'],alpha=0.8,label=c,marker=marker[c])
plt.xlabel("sepal length(cm)")
plt.ylabel("sepal width(cm)")
plt.legend()
plt.show
做出的图如下所示
可以看出这是一个分类问题
1.2无监督学习
无监督学习顾名思义就是没有相应的因变量,常见的无监督学习就是聚类问题。本次Datawhale团队在接下来提供的学习内容中并不会再涉及无监督学习的算法,所以只在此处列出来几个聚类问题的例子。我们以符合正态分布的聚类数据为例
from sklearn import datasets
x,y = datasets.make_blobs(n_samples=5000,n_features=2,centers=3)
for index,c in enumerate(np.unique(y))
plt.scatter(x[y==c,0],x[y==c,1],s=7)
plt.show()
生成的图形如下所示,可以看出这是一个聚类问题。
Datawhale团队也在此处总结了常见的样本生成函数
2.掌握基本的回归模型
今天我主要学习了如何使用sklearn构建完整的回归模型。下面首先介绍本节学习到的几种回归模型。
2.1 理论简介
(a)简单线性回归模型:
简单线性回归模型可以表示为:
Y
=
X
β
+
ϵ
Y=X\beta+\epsilon
Y=Xβ+ϵ
其中 X 为一个
n
×
p
n \times p
n×p 维的矩阵,包含n个样本,每个样本有p个自变量。
β
\beta
β 是 p个自变量的回归系数。
ϵ
\epsilon
ϵ 是随机误差,服从正态分布。
(b)广义线性回归模型:
广义线性回归模型通常有如下的pdf或者pmf。
Y
i
h
a
s
p
d
f
o
r
p
m
f
e
x
p
{
η
i
y
i
−
ζ
(
η
i
)
ϕ
}
h
(
y
i
,
ϕ
)
,
i
=
1
,
2...
n
Y_{i} \, has \, pdf \, or \, pmf \quad exp\{\frac{\eta_{i}y_{i}-\zeta(\eta_{i})}{\phi}\}h(y_{i},\phi),\quad i=1,2...n
Yihaspdforpmfexp{ϕηiyi−ζ(ηi)}h(yi,ϕ),i=1,2...n
通过定义
μ
(
η
)
=
ζ
′
η
\mu(\eta)=\zeta^{'}{\eta}
μ(η)=ζ′η和一个一直的链接函数g, 我们可以定义
η
i
\eta_{i}
ηi 和因变量
x
i
x_i
xi之间的关系。
g
(
μ
(
η
i
)
)
=
β
′
x
i
,
,
i
=
1
,
2...
,
n
g(\mu(\eta_{i}))=\beta^{'}x_{i},\quad,i=1,2...,n
g(μ(ηi))=β′xi,,i=1,2...,n
我们最常见也是最著名的广义回归模型便是逻辑回归了。本文就不再赘述了,通过简单的化简,可以得出逻辑回归模型符合上述关于广义回归模型的定义。
(c)广义可加模型
广义可加模型主要用来克服之前模型只能处理线性关系的局限性,广义可加模型的定义如下:
y
i
=
w
0
+
Σ
j
=
1
p
f
i
(
x
i
j
)
+
ϵ
i
y_{i}=w_{0}+\Sigma_{j=1}^{p}f_{i}(x_{ij})+\epsilon_{i}
yi=w0+Σj=1pfi(xij)+ϵi
其中对于f的形式我们不做特别多的限制。
GAM模型的优点与不足:
优点:简单容易操作,能够很自然地推广线性回归模型至非线性模型,使得模型的预测精度有所上升;由于模型本身是可加的,因此GAM还是能像线性回归模型一样把其他因素控制不变的情况下单独对某个变量进行推断,极大地保留了线性回归的易于推断的性质。
缺点:GAM模型会经常忽略一些有意义的交互作用,比如某两个特征共同影响因变量,不过GAM还是能像线性回归一样加入交互项
x
i
×
x
j
x_i \times x_j
xi×xj 的形式进行建模;但是GAM模型本质上还是一个可加模型,如果我们能摆脱可加性模型形式,可能还会提升模型预测精度,详情请看后面的算法。
(d) 回归树
这是我第一次接触回归树模型,在这里我从 (https://www.cnblogs.com/wuliytTaotao/p/10724118.html)摘抄了回归树算法的相应算法步骤,展示如下:
树模型的优缺点:
- 树模型的解释性强,在解释性方面可能比线性回归还要方便。
- 树模型更接近人的决策方式。
- 树模型可以用图来表示,非专业人士也可以轻松解读。
- 树模型可以直接做定性的特征而不需要像线性回归一样哑元化。
- 树模型能很好处理缺失值和异常值,对异常值不敏感,但是这个对线性模型来说却是致命的。
- 树模型的预测准确性一般无法达到其他回归模型的水平,但是改进的方法很多。
(e)SVR模型
SVR模型就是SVM模型在回归领域的应用。SVR允许一个宽度为
ϵ
\epsilon
ϵ。对于落在这个宽度以内的点,我们不对其进行损失计算,即我们不认为其是错误点。SVR的理论知识运用到了凸优化的知识,我还需要再多些时间去学习,在这里暂时不做总结。
2.2 代码实现
本文代码来自于Datawhale团队提供的相应文档。本人只是在这里跟随Datawhale团队的代码运行了一遍。
导入第一节已经介绍过的Boston房价数据集
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()
线性模型的相应代码如下:
from sklearn import linear_model # 引入线性回归方法
lin_reg = linear_model.LinearRegression() # 创建线性回归的类
lin_reg.fit(X,y) # 输入特征X和因变量y进行训练
print("模型系数:",lin_reg.coef_) # 输出模型的系数
print("模型得分:",lin_reg.score(X,y)) # 输出模型的决定系数R^2
回归树的相应代码如下:
from sklearn.tree import DecisionTreeRegressor
reg_tree = DecisionTreeRegressor(criterion = "mse",min_samples_leaf = 5)
reg_tree.fit(X,y)
reg_tree.score(X,y)
SVR模型的代码如下:
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler # 标准化数据
from sklearn.pipeline import make_pipeline # 使用管道,把预处理和模型形成一个流程
reg_svr = make_pipeline(StandardScaler(), SVR(C=1.0, epsilon=0.2))
reg_svr.fit(X, y)
reg_svr.score(X,y)
3. 模型优化
3.1 偏差与方差理论(Variance-BiasTrade off )
本节我跟着Datawhale团队学习了如何初步优化模型。主要介绍几种模型优化的指标和选择方法。首先什么样的模型才是最好的呢?训练误差最小的?这当然是不恰当的。使用训练误差来评价模型,难免有一种“王婆卖瓜,自卖自夸”的嫌疑。我们可以从下面这个例子来看(来自华东师范大学统计学院方方老师的课件),对于一个回归的问题,如果我们不对回归方程的项数进行限制,那后果显然就是,我们可以将模型的训练误差降到很低,甚至是0(如图中例子,直接使用插值函数)。但是该模型显然在新来的数据中不会表现的很出色,这就是“过拟合”现象。
那我们应该从什么角度来评价模型,答案是泛化误差。从理论上来讲,一个好的
f
^
(
x
)
\hat{f}(x)
f^(x) 应该使得
E
(
Y
−
f
^
(
x
)
)
2
E(Y-\hat{f}(x))^{2}
E(Y−f^(x))2 最小:
E
(
Y
−
f
^
(
x
)
)
2
=
E
f
^
E
(
X
,
Y
)
(
Y
−
f
^
(
X
)
)
2
=
1
R
Σ
i
=
1
R
E
(
X
,
Y
)
(
Y
−
f
^
(
X
)
)
2
=
1
R
Σ
i
=
1
R
1
m
Σ
j
=
1
m
(
y
j
−
f
^
(
x
j
)
)
2
\begin{aligned} E(Y-\hat{f}(x))^{2}&=E_{\hat{f}}E_{(X,Y)}(Y-\hat{f}(X))^{2}\\ &=\frac{1}{R}\Sigma_{i=1}^{R}E_{(X,Y)}(Y-\hat{f}(X))^{2}\\ &=\frac{1}{R}\Sigma_{i=1}^{R}\frac{1}{m}\Sigma_{j=1}^{m}(y_j-\hat{f}(x_j))^{2} \end{aligned}
E(Y−f^(x))2=Ef^E(X,Y)(Y−f^(X))2=R1Σi=1RE(X,Y)(Y−f^(X))2=R1Σi=1Rm1Σj=1m(yj−f^(xj))2
我们把这个指标分解一下就可以得出方差与偏差的理论。对于给定的
x
0
x_0
x0,我们假设
y
0
=
f
(
x
0
)
+
ϵ
y_0=f(x_0)+\epsilon
y0=f(x0)+ϵ。我们有
E
r
r
(
x
0
)
=
E
f
^
E
y
0
∣
x
0
(
y
0
−
f
^
(
x
0
)
)
2
=
E
f
^
,
ϵ
(
f
(
x
0
)
−
f
^
(
x
0
)
+
ϵ
)
2
=
E
f
^
,
ϵ
[
E
f
^
(
f
^
(
x
0
)
−
f
^
(
x
0
)
)
+
f
(
x
0
)
−
E
f
^
(
x
0
)
+
ϵ
]
2
=
V
a
r
f
^
(
f
^
(
x
0
)
)
+
[
B
i
a
s
(
f
^
(
x
0
)
)
]
2
+
V
a
r
(
ϵ
)
\begin{aligned} Err(x_0)&=E_{\hat{f}}E_{y_0|x_0}(y_0-\hat{f}(x_0))^{2}\\ &=E_{\hat{f},\epsilon}(f(x_0)-\hat{f}(x_0)+\epsilon)^{2}\\ &=E_{\hat{f},\epsilon}[{{E_{\hat{f}}(\hat{f}(x_0)-\hat{f}(x_0))}}+f(x_0)-E_{\hat{f}(x_0)}+\epsilon]^{2}\\ &=Var_{\hat{f}}(\hat{f}(x_0))+[Bias(\hat{f}(x_0))]^{2}+Var(\epsilon) \end{aligned}
Err(x0)=Ef^Ey0∣x0(y0−f^(x0))2=Ef^,ϵ(f(x0)−f^(x0)+ϵ)2=Ef^,ϵ[Ef^(f^(x0)−f^(x0))+f(x0)−Ef^(x0)+ϵ]2=Varf^(f^(x0))+[Bias(f^(x0))]2+Var(ϵ)
由上式我们可以看出,最终的误差可以分解为方差和偏差的和。一般来说,模型的复杂度越高,f的方差就会越大。另一方面,模型的偏差是指:为了选择一个简单的模型去估计真实函数所带入的误差。假如真实的数据X与Y的关系是二次关系,但是我们选择了线性模型进行建模,那由于模型的复杂度引起的这种误差我们称为偏差,它的构成时复杂的。偏差度量了学习算法的期望预测与真实结果的偏离程度,即刻画了学习算法本身的拟合能力。偏差度量的是单个模型的学习能力,而方差度量的是同一个模型在不同数据集上的稳定性。“偏差-方差分解”说明:泛化性能是由学习算法的能力、数据的充分性以及学习任务本身的难度所共同决定的。给定学习任务,为了取得好的泛化性能,则需使偏差较小,即能够充分拟合数据,并且使方差较小,即使得数据扰动产生的影响小。一般而言,增加模型的复杂度,会增加模型的方差,但是会减少模型的偏差,我们要找到一个方差–偏差的权衡,使得误差最小。
3.2 特征筛选
3.2.1 根据指标去进行特征筛选
根据Variance-BiasTrade off 的理论,我们在选择指标的时候也应该把模型的复杂度代入公式中去。以下是几种比较合理的指标。
Mallow’s Cp
Mallow’s Cp 的推导较为麻烦。本文在这里采用了华东师范大学统计学院方方老师的课件来说明:
由此,我们可以推出Mallow‘ Cp的公式:
C
p
=
S
S
E
+
2
p
σ
^
2
C_p=SSE+2p\hat{\sigma}^{2}
Cp=SSE+2pσ^2’
仔细琢磨Mallow’s Cp 的表达式,我们可以看出,过于简单的模型SSE相对较大,p相对较少。过于复杂的模型,SSE相对较小,而p相对较大。这其实也是一种 Variance-Bias的 trade off。
AIC、BIC
AIC、BIC的推导较为麻烦,我也没有看明白。只在这里列出来最后的表达式:
A
I
C
=
n
l
o
g
(
S
S
E
/
n
)
+
2
p
AIC=nlog(SSE/n)+2p
AIC=nlog(SSE/n)+2p
B
I
C
=
n
l
o
g
(
S
S
E
/
n
)
+
p
l
o
g
n
BIC=nlog(SSE/n)+p logn
BIC=nlog(SSE/n)+plogn
可以看出相当于AIC,BIC对于模型复杂度的惩罚更厉害。二者本质上也是一种Variance-Bias的 trade off。
根据以上几种指标,我们可以通过如下的方式去进行特征选择
最优子集选择:
(i) 记不含任何特征的模型为 𝑀0 ,计算这个 𝑀0 的评价指标。
(ii) 在 𝑀0 基础上增加一个变量,计算p个模型的评价指标,选择评价指标最小的模型记作 𝑀1 ,并计算该模型 𝑀1 的测试误差。
(iii) 再增加变量,计算p-1个模型的评价指标,并选择评价指标最小的模型记作 𝑀2 ,并计算该模型 𝑀2 的评价指标。
(iv) 重复以上过程知道拟合的模型有p个特征为止,并选择p+1个模型 {𝑀0,𝑀1,…,𝑀𝑝} 中评价指标最小的模型作为最优模型。
向前逐步选择:
最优子集选择虽然在原理上很直观,但是随着数据特征维度p的增加,子集的数量为 2𝑝 ,计算效率非常低下且需要的计算内存也很高,在大数据的背景下显然不适用。因此,我们需要把最优子集选择的运算效率提高,因此向前逐步选择算法的过程如下:
(i) 记不含任何特征的模型为 𝑀0 ,计算这个 𝑀0 的评价指标。
(ii) 在 𝑀0 基础上增加一个变量,计算p个模型的评价指标,选择评价指标最小的模型记作 𝑀1 ,并计算该模型 𝑀1 的测试误差。
(iii) 在最小的评价指标模型下继续增加一个变量,选择RSS最小的模型记作 𝑀2 ,并计算该模型 𝑀2 的评价指标。
(iv) 以此类推,重复以上过程知道拟合的模型有p个特征为止,并选择p+1个模型 {𝑀0,𝑀1,…,𝑀𝑝} 中评价指标最小的模型作为最优模型。
以下是来自(https://blog.csdn.net/weixin_44835596/article/details/89763300)的特征筛选的例子
#定义向前逐步回归函数
def forward_select(data,target):
variate=set(data.columns) #将字段名转换成字典类型
variate.remove(target) #去掉因变量的字段名
selected=[]
current_score,best_new_score=float('inf'),float('inf') #目前的分数和最好分数初始值都为无穷大(因为AIC越小越好)
#循环筛选变量
while variate:
aic_with_variate=[]
for candidate in variate: #逐个遍历自变量
formula="{}~{}".format(target,"+".join(selected+[candidate])) #将自变量名连接起来
aic=ols(formula=formula,data=data).fit().aic #利用ols训练模型得出aic值
aic_with_variate.append((aic,candidate)) #将第每一次的aic值放进空列表
aic_with_variate.sort(reverse=True) #降序排序aic值
best_new_score,best_candidate=aic_with_variate.pop() #最好的aic值等于删除列表的最后一个值,以及最好的自变量等于列表最后一个自变量
if current_score>best_new_score: #如果目前的aic值大于最好的aic值
variate.remove(best_candidate) #移除加进来的变量名,即第二次循环时,不考虑此自变量了
selected.append(best_candidate) #将此自变量作为加进模型中的自变量
current_score=best_new_score #最新的分数等于最好的分数
print("aic is {},continuing!".format(current_score)) #输出最小的aic值
else:
print("for selection over!")
break
formula="{}~{}".format(target,"+".join(selected)) #最终的模型式子
print("final formula is {}".format(formula))
model=ols(formula=formula,data=data).fit()
return(model)
import statsmodels.api as sm #最小二乘
from statsmodels.formula.api import ols #加载ols模型
forward_select(data=boston_data,target="Price")
3.2.2 根据LASSO进行特征筛选
主要是LASSO。LASSO主要通过最小化
LASSO特征提取的原理是:椭圆形曲线为RSS等高线,菱形和圆形区域分别代表了L1和L2约束,Lsaao回归和岭回归都是在约束下的回归,因此最优的参数为椭圆形曲线与菱形和圆形区域相切的点。但是Lasso回归的约束在每个坐标轴上都有拐角,因此当RSS曲线与坐标轴相交时恰好回归系数中的某一个为0,这样就实现了特征提取。反观岭回归的约束是一个圆域,没有尖点,因此与RSS曲线相交的地方一般不会出现在坐标轴上,因此无法让某个特征的系数为0,因此无法做到特征提取。
3.3 利用交叉验证进行模型评价
(摘自Datawhale课件)前面讨论的对训练误差修正得到测试误差的估计是间接方法,这种方法的桥梁是训练误差,而交叉验证则是对测试误差的直接估计。交叉验证比训练误差修正的优势在于:能够给出测试误差的一个直接估计。在这里只介绍K折交叉验证:我们把训练样本分成K等分,然后用K-1个样本集当做训练集,剩下的一份样本集为验证集去估计由K-1个样本集得到的模型的精度,这个过程重复K次取平均值得到测试误差的一个估计 C V ( k ) = 1 k Σ i = 1 k M S E i CV_{(k)}=\frac{1}{k}\Sigma_{i=1}^{k}MSE_{i} CV(k)=k1Σi=1kMSEi。5交叉验证如下图:(蓝色的是训练集,黄色的是验证集)
3.4 参数调优
机器学习算法的一大要义便是选择最好的参数组合去降低测试误差。本节主要介绍了两种参数调优的方法:网格搜索法以及随机搜索法。
网格搜索法:
网格搜索法是一种暴力搜索方法,它将所有可能的参数组合形成网络中的一个个格子,并逐一代入参数模型中测量其测试误差(通常采取交叉验证的方法),最终得出最优的参数组合。
随机搜索法:
考察其源代码,其搜索策略如下:(参考:https://blog.csdn.net/qq_36810398/article/details/86699842)
(a)对于搜索范围是distribution的超参数,根据给定的distribution随机采样;
(b)对于搜索范围是list的超参数,在给定的list中等概率采样;
(c)对a、b两步中得到的n_iter组采样结果,进行遍历。
(补充)如果给定的搜索范围均为list,则不放回抽样n_iter次。
可以看出,这种搜索方式避免了维度灾难问题,可以高效快速地进行搜索。
下面是Datawhale团队提供的代码实例,我们分别采用上述两种方法进行参数的最优化选择。
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
# 下面我们使用网格搜索来对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_)
# 下面我们使用随机搜索来对SVR调参:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform # 引入均匀分布设置参数
pipe_svr = Pipeline([("StandardScaler",StandardScaler()),
("svr",SVR())])
distributions = dict(svr__C=uniform(loc=1.0, scale=4), # 构建连续参数的分布
svr__kernel=["linear","rbf"], # 离散参数的集合
svr__gamma=uniform(loc=0, scale=4))
rs = RandomizedSearchCV(estimator=pipe_svr,
param_distributions = distributions,
scoring = 'r2',
cv = 10) # 10折交叉验证
rs = rs.fit(X,y)
print("随机搜索最优得分:",rs.best_score_)
print("随机搜索最优参数组合:\n",rs.best_params_)
从最终选择出来的参数组合我们可以看出这两种参数选择方式选择出来的最优参数组合是不同的。
4. 掌握基本的分类问题
4.1 分类问题的评价指标
分类问题的评价指标主要是从混淆矩阵中推导出来的。下面介绍混淆矩阵。混淆矩阵主要有四个指标:
真阳性(Test Positive):预测值和真实值都是阳性
真阴性(Test Negative):预测值和真实都是阴性
假阳性(False Positive):预测值是阳性而真实值为阴性
假阴性(False Negative):预测值为阴性而真实值为阳性
混淆矩阵可以表达为:
(
T
P
F
N
F
P
T
N
)
\left( \begin{array}{cc} TP&FN\\ FP&TN \end{array} \right)
(TPFPFNTN)
从混淆矩阵中,我们可以得到众多评价指标:
- 准确率: A C C = T P + T N F P + F N + T P + T N ACC=\frac{TP+TN}{FP+FN+TP+TN} ACC=FP+FN+TP+TNTP+TN
- 精度: P R E = T P T P + F P PRE=\frac{TP}{TP+FP} PRE=TP+FPTP
- 召回率: R E C = T P T P + F N REC=\frac{TP}{TP+FN} REC=TP+FNTP
- F1值: F 1 = 2 P R E × R E C P R E + R E C F1=2\frac{PRE\times REC}{PRE+REC} F1=2PRE+RECPRE×REC
- ROC曲线:以假阳率为横轴,真阳率为纵轴画出来的曲线,曲线下方的面积越大越好。
4.2 常见的分类模型理论
4.2.1 逻辑回归
当我们处理分类问题的时候,一个比较常规的想法是:我们通过已有的解释变量去预测待测物品属于某个类别的概率,如果其大于0.5,就把它归结为那个类别。但是实际操作时,一般的线性回归却做不到这一点,因为我们无法保证输出值一定位于0和1之间。逻辑回归就是用来处理这种问题,它运用了一个连接函数,将位于实数R上的输出转移到0和1之间。具体表达式为
p
(
x
)
=
e
β
0
+
β
1
x
1
+
e
β
0
+
β
1
x
p(x)=\frac{e^{\beta_0+\beta_1x}}{1+e^{\beta_0+\beta_1x}}
p(x)=1+eβ0+β1xeβ0+β1x。
逻辑回归参数的估计通常采取的是最大似然估计,具体的估计流程在这里就不叙述了。
4.2.2 线性判别分析
Datawhale团队在介绍线性判别分析的时候分别从贝叶斯角度和降维的角度介绍了算法的原理。因为之前学习过降维的角度去介绍该算法,所以这里我就来介绍一下我之前没有学习过的另一个角度。
我们先来看一下贝叶斯公式:
p
(
Y
=
k
∣
X
=
x
)
=
π
k
f
k
(
x
)
Σ
l
=
1
k
π
l
f
l
(
x
)
p(Y=k|X=x)=\frac{\pi_{k}f_{k}(x)}{\Sigma_{l=1}^{k}\pi_{l}f_{l}(x)}
p(Y=k∣X=x)=Σl=1kπlfl(x)πkfk(x)
这个公式的含义便是给定样本X后,Y属于第k类的概率。可以看出,在给定样本后,分母其实是不变的。那么我们在分类的时候,其实只需要比较分子就可以了。当我们假设假定
f
k
∼
N
(
μ
k
,
σ
2
)
f_k\sim N(\mu_k, \sigma^{2})
fk∼N(μk,σ2)。
f
k
π
k
f_k \pi_k
fkπk 可以表示为
π
k
1
2
π
σ
e
x
p
{
−
(
x
−
μ
k
)
2
2
σ
2
}
\pi_{k}\frac{1}{\sqrt{2\pi}\sigma}exp\{-\frac{(x-\mu_{k})^{2}}{2\sigma^{2}}\}
πk2πσ1exp{−2σ2(x−μk)2}。其中,
μ
k
^
=
1
n
k
Σ
i
:
y
i
=
k
x
i
\hat{\mu_k}=\frac{1}{n_k}\Sigma_{i:y_i=k}x_i
μk^=nk1Σi:yi=kxi;
σ
^
2
=
1
n
−
k
Σ
k
=
1
K
Σ
i
:
y
i
=
k
(
x
i
−
μ
^
k
)
2
\hat{\sigma}^{2}=\frac{1}{n-k}\Sigma_{k=1}^{K}\Sigma_{i:y_i=k}(x_i-\hat{\mu}_k)^{2}
σ^2=n−k1Σk=1KΣi:yi=k(xi−μ^k)2 。
4.2.3 决策树
决策树作为一种运用于分类的学习器,自然不能再采取均方误差作为分类的评价指标了,下面介绍两种决策树常用的分类指标。
- 基尼系数:
G = Σ k = 1 K p ^ m k ( 1 − p ^ m k ) G=\Sigma_{k=1}^{K}\hat{p}_{mk}(1-\hat{p}_{mk}) G=Σk=1Kp^mk(1−p^mk)
其中 p ^ m k \hat{p}_{mk} p^mk表示m区域内第k类所占的比例。从基尼系数的定义我们可以看出,基尼系数其实是一钟衡量纯度的指标。由基尼系数得到的决策树又叫CART。 - 交叉熵:
D = − Σ k = 1 K p ^ m k l o g ( p ^ m k ) D=-\Sigma_{k=1}^{K}\hat{p}_{mk}log(\hat{p}_{mk}) D=−Σk=1Kp^mklog(p^mk)
交叉熵最早源自物理的概念。可以看出,交叉熵也是用来衡量分类的纯度的。
决策树的算法流程
决策树分类算法的完整步骤:
a. 选择最优切分特征j以及该特征上的最优点s:
遍历特征j以及固定j后遍历切分点s,选择使得基尼系数或者交叉熵最小的(j,s)
b. 按照(j,s)分裂特征空间,每个区域内的类别为该区域内样本比例最多的类别。
c. 继续调用步骤1,2直到满足停止条件,就是每个区域的样本数小于等于5。
d. 将特征空间划分为J个不同的区域,生成分类树。
4.2.4 SVM
关于SVM公式的推导,我跟着B站UP住大海老师做了相应的学习,目前为止记下来如下比较。关于SVM的内容,目前我还只学了一小部分,我还会跟着大海老师继续把SVM的相应公式学习完毕,并在网上在学习其他相关的内容。
4.3 常见的分类模型代码
需要指出,此处的代码均为Datawhale团队提供,我只是跟着老师打了一遍。
先导入数据集
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
feature = iris.feature_names
data = pd.DataFrame(X,columns=feature)
data['target'] = y
data.head()
逻辑回归
from sklearn.linear_model import LogisticRegression
log_iris = LogisticRegression()
log_iris.fit(X,y)
log_iris.score(X,y)
线性判别分析
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda_iris = LinearDiscriminantAnalysis()
lda_iris.fit(X,y)
lda_iris.score(X,y)
决策树
from sklearn.tree import DecisionTreeClassifier
tree_iris = DecisionTreeClassifier(min_samples_leaf=5)
tree_iris.fit(X,y)
tree_iris.score(X,y)
SVM
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
svc_iris = make_pipeline(StandardScaler(), SVC(gamma='auto'))
svc_iris.fit(X, y)
svc_iris.score(X,y)
4.4 分类模型的参数调优
(代码来自Datawhale团队,我只是跟着老师的代码打了一遍)
首先针对SVM模型进行参数调优。采用的方法是之前介绍过的网格搜索法和随机搜索法。分别对svc__C,'svc__gamma,'svc__kernel这三个参数进行了选择。
网格搜索法
# 使用网格搜索进行超参数调优:
# 方式1:网格搜索GridSearchCV()
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
import time
start_time = time.time()
pipe_svc = make_pipeline(StandardScaler(),SVC(random_state=1))
param_range = [0.0001,0.001,0.01,0.1,1.0,10.0,100.0,1000.0]
param_grid = [{'svc__C':param_range,'svc__kernel':['linear']},{'svc__C':param_range,'svc__gamma':param_range,'svc__kernel':['rbf']}]
gs = GridSearchCV(estimator=pipe_svc,param_grid=param_grid,scoring='accuracy',cv=10,n_jobs=-1)
gs = gs.fit(X,y)
end_time = time.time()
print("网格搜索经历时间:%.3f S" % float(end_time-start_time))
print(gs.best_score_)
print(gs.best_params_)
随机搜索法
# 方式2:随机网格搜索RandomizedSearchCV()
from sklearn.model_selection import RandomizedSearchCV
from sklearn.svm import SVC
import time
start_time = time.time()
pipe_svc = make_pipeline(StandardScaler(),SVC(random_state=1))
param_range = [0.0001,0.001,0.01,0.1,1.0,10.0,100.0,1000.0]
param_grid = [{'svc__C':param_range,'svc__kernel':['linear']},{'svc__C':param_range,'svc__gamma':param_range,'svc__kernel':['rbf']}]
# param_grid = [{'svc__C':param_range,'svc__kernel':['linear','rbf'],'svc__gamma':param_range}]
gs = RandomizedSearchCV(estimator=pipe_svc, param_distributions=param_grid,scoring='accuracy',cv=10,n_jobs=-1)
gs = gs.fit(X,y)
end_time = time.time()
print("随机网格搜索经历时间:%.3f S" % float(end_time-start_time))
print(gs.best_score_)
print(gs.best_params_)
下面介绍一下如何使用代码来计算二分类问题的混淆矩阵以及ROC曲线。接受者操作特性曲线(receiver operating characteristic curve,简称ROC曲线),又称为感受性曲线(sensitivity curve)。得此名的原因在于曲线上各点反映着相同的感受性,它们都是对同一信号刺激的反应,只不过是在几种不同的判定标准下所得的结果而已。接受者操作特性曲线就是以虚惊概率为横轴,击中概率为纵轴所组成的坐标图,和被试在特定刺激条件下由于采用不同的判断标准得出的不同结果画出的曲线
混淆矩阵
# 混淆矩阵:
# 加载数据
df = pd.read_csv("http://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data",header=None)
'''
乳腺癌数据集:569个恶性和良性肿瘤细胞的样本,M为恶性,B为良性
'''
# 做基本的数据预处理
from sklearn.preprocessing import LabelEncoder
X = df.iloc[:,2:].values
y = df.iloc[:,1].values
le = LabelEncoder() #将M-B等字符串编码成计算机能识别的0-1
y = le.fit_transform(y)
le.transform(['M','B'])
# 数据切分8:2
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,stratify=y,random_state=1)
from sklearn.svm import SVC
pipe_svc = make_pipeline(StandardScaler(),SVC(random_state=1))
from sklearn.metrics import confusion_matrix
pipe_svc.fit(X_train,y_train)
y_pred = pipe_svc.predict(X_test)
confmat = confusion_matrix(y_true=y_test,y_pred=y_pred)
fig,ax = plt.subplots(figsize=(2.5,2.5))
ax.matshow(confmat, cmap=plt.cm.Blues,alpha=0.3)
for i in range(confmat.shape[0]):
for j in range(confmat.shape[1]):
ax.text(x=j,y=i,s=confmat[i,j],va='center',ha='center')
plt.xlabel('predicted label')
plt.ylabel('true label')
plt.show()
ROC曲线
# 绘制ROC曲线:
from sklearn.metrics import roc_curve,auc
from sklearn.metrics import make_scorer,f1_score
scorer = make_scorer(f1_score,pos_label=0)
gs = GridSearchCV(estimator=pipe_svc,param_grid=param_grid,scoring=scorer,cv=10)
y_pred = gs.fit(X_train,y_train).decision_function(X_test)
#y_pred = gs.predict(X_test)
fpr,tpr,threshold = roc_curve(y_test, y_pred) ###计算真阳率和假阳率
roc_auc = auc(fpr,tpr) ###计算auc的值
plt.figure()
lw = 2
plt.figure(figsize=(7,5))
plt.plot(fpr, tpr, color='darkorange',
lw=lw, label='ROC curve (area = %0.2f)' % roc_auc) ###假阳率为横坐标,真阳率为纵坐标做曲线
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([-0.05, 1.0])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic ')
plt.legend(loc="lower right")
plt.show()
5. 最后的话
感谢Datawhale团队提供的学习材料和学习机会,期待下一阶段的学习~