一文入门sklearn二分类实战

在小白我的第一篇文里就提出过一个问题,就是现在的教程都太“分散”太“板块”,每一个知识点都单独用一个例子,机器学习算法里也是这样的,可能逻辑回归用葡萄酒的案例讲,决策树又用鸢尾花的数据集了。今天,我们只用乳腺癌数据集,专门来看一看二分类问题的实战应用。

导入包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 解决坐标轴刻度负号乱码
plt.rcParams['axes.unicode_minus'] = False
# 解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['Simhei']

%matplotlib inline

导入数据

# 读入癌症数据集
from sklearn import datasets
from sklearn.model_selection import train_test_split

cancer=datasets.load_breast_cancer()
X=cancer.data
y=cancer.target

X_train,X_test,y_train,y_test=train_test_split(X,y)
print('训练集维度:{}\n测试集维度:{}'.format(X_train.shape,X_test.shape))

训练集维度:(426, 30)
测试集维度:(143, 30)

一、逻辑回归

我们先来看看分类里最简单却又是最经典的逻辑回归:

1.1 sklearn实战

废话不多说,看看用sklearn怎么做逻辑回归。

from sklearn.linear_model import LogisticRegression
from sklearn import metrics

lr = LogisticRegression()                                        # 实例化一个LR模型
lr.fit(X_train,y_train)                                          # 训练模型
y_prob = lr.predict_proba(X_test)[:,1]                           # 预测1类的概率
y_pred = lr.predict(X_test)                                      # 模型对测试集的预测结果
fpr_lr,tpr_lr,threshold_lr = metrics.roc_curve(y_test,y_prob)    # 获取真阳率、伪阳率、阈值
auc_lr = metrics.auc(fpr_lr,tpr_lr)                              # AUC得分
score_lr = metrics.accuracy_score(y_test,y_pred)                 # 模型准确率
print([score_lr,auc_lr])

得到准确率为0.9720, AUC值为0.9954。这个结果可以说是相当不错了。
可以查看一下LR模型的默认参数。

lr
LR默认参数

1.2 对逻辑回归的理解

这里的乳腺癌数据集有30个特征,我们需要根据这些特征去判断一个是否会患乳腺癌。不妨换一个思路,如果按照我们熟悉的线性回归,这个问题该怎么思考呢?那么我们会根据每个人自身的特征给他打个分,我们规定分数越高就越不健康。进一步,我们希望把每个人的得分进行归一化,让分数限制在[0,1]这个区间,这样,打分的问题就变成了概率问题。
那怎么把得分变成概率呢?有很多方法,最经典的就要说一说sigmoid函数了。
https://www.cnblogs.com/xitingxie/p/9924523.html
我随意贴了一个网上找的比较全的sigmoid函数解析,偷个懒。

1.3 损失函数

在这里,不得不说一下,小白我刚开始接触逻辑回归的时候,就掉入了这个坑,LR带有回归二字,我想当然地就认为损失函数肯定和回归一样嘛,真实值和预测值之间差值的平方和嘛!but,它本质其实还是分类问题啊,分类模型的损失函数,是交叉熵。
熵这个问题可能又会让很多初学者很困扰了,我刚开始学的时候也是一知半解,在这里分享一篇觉得写的很好的交叉熵的文:
https://www.jianshu.com/p/8a0ad237b0ed
另外贴一篇损失函数推导:
https://www.cnblogs.com/shayue/p/10520414.html

1.4 求解损失函数(max_iter)

使用梯度下降。这个经典方法我在这儿也不过多说了,同样,贴个链接大家可以自己去看:
https://www.jianshu.com/p/93d9fea7f4c2
我们这里只来看一看max_iter这个参数,它表示梯度下降法中最大迭代次数,max_iter越大,代表步长越小,模型迭代时间越长。我们来看一看效果。

trainList=[]  # 用来记录训练集得分
testList=[]   # 用来记录测试集得分

for i in range(1,101,10):
    lr=LogisticRegression(max_iter=i).fit(X_train,y_train)
    trainList.append(metrics.accuracy_score(y_train,lr.predict(X_train)))
    testList.append(metrics.accuracy_score(y_test,lr.predict(X_test)))
    
plt.figure(figsize=(20, 5))
plt.style.use('seaborn-colorblind')
plt.plot(range(1,101,10),trainList,color='orange',label='train')
plt.plot(range(1,101,10),testList,color='red',label='train')
max_iter

我们可以看到,基本迭代到30次的时候测试集和训练集的得分就不再提高了,继续增大迭代次数也只是浪费了资源。

1.5 如何防止过拟合(penalty & C)

逻辑回归和线性回归一样,为了防止过拟合,可以在损失函数的后面添加正则项。同样的,我也贴个详解的连接在后面,但大家只要知道我们有l1和l2两种正则项,sklearn里大多数算法默认的都是l2范式,它会让特征前的系数尽量小防止过拟合,但不会像l1范式那样直接将某些系数减小到0:
https://blog.csdn.net/red_stone1/article/details/80755144
在sklearn中,通过penalty选择l1还是l2范式,而通过C控制对系数的惩罚力度。要注意的是,C是加在损失函数前面的,而不是加在正则项前面,因此C越大,表示对系数的惩罚力度越小,模型越容易过拟合。

lr1=LogisticRegression(penalty='l1').fit(X_train,y_train)
lr2=LogisticRegression(penalty='l2').fit(X_train,y_train)

print('L1正则得分:{}\nL2正则得分:{}'.format(metrics.accuracy_score(y_test,lr1.predict(X_test)),metrics.accuracy_score(y_test,lr2.predict(X_test))))

当然,这个数据集过于理想,因此使用l1和l2基本没有不同。

list1=[]  # 用来记录l1正则得分
list2=[]   # 用来记录l2正则得分

for i in np.linspace(0.05,1,20):
    lr1=LogisticRegression(penalty='l1',C=i).fit(X_train,y_train)
    lr2=LogisticRegression(penalty='l2',C=i).fit(X_train,y_train)
    list1.append(metrics.accuracy_score(y_test,lr1.predict(X_test)))
    list2.append(metrics.accuracy_score(y_test,lr2.predict(X_test)))
    
plt.figure(figsize=(20, 5))
plt.style.use('seaborn-colorblind')
plt.plot(np.linspace(0.05,1,20),list1,color='orange',label='l1-test')
plt.plot(np.linspace(0.05,1,20),list2,color='red',label='l2-test')
plt.legend(loc='lower right')
plt.show()
正则系数C

我们一般是要防止过拟合,所以对C的调参一般是在[0,1]之间调整。可以看到,加入C之后,l1和l2的区别就出来了。C取默认值1.0的时候其实就是得分最高的了,再调整C意义不大。

1.6 如何处理样本不均衡(class_weight)

pd.Series(y).value_counts()

在乳腺癌的数据集中,正例:负例是357:212,并不存在样本不均衡问题。但在实际现实问题建模时候,样本不均衡却是常态,比如在处理信用违约时,不违约的人总是多数,而违约的人是非常少的,除了我们常用的上采用的方法,还可以通过class_weight参数调节,在这里就不举例了。

二、支持向量机

支持向量机在深度学习没有兴起的时候那可是不可撼动的王者地位,当然,它这么好用,也可想而知,这家伙肯定是很难很复杂的。

2.1 sklearn实战

同样,废话不多说,先来看支持向量机算法在乳腺癌数据集上的表现。

from sklearn.svm import SVC
from sklearn import metrics

svc = SVC().fit(X_train,y_train)

y_prob = svc.decision_function(X_test)                              # 决策边界距离
y_pred = svc.predict(X_test)                                        # 模型对测试集的预测结果
fpr_svc,tpr_svc,threshold_svc = metrics.roc_curve(y_test,y_prob)     # 获取真阳率、伪阳率、阈值
auc_svc = metrics.auc(fpr_svc,tpr_svc)                              # 模型准确率
score_svc = metrics.accuracy_score(y_test,y_pred)
print([score_svc,auc_svc])

可以得到,在不调任何参数的情况下,准确率为0.6294, AUC值为0.9126。我们会发现AUC值很高,但是准确率却不高。其实,通过metrics.precision_score(y_test,y_pred)和metrics.recall_score(y_test,y_pred),我们会发现精确率很低只有0.6294,但召回率为1.0。(什么是精确率和召回率大家也可以自行百度下)
同样,我们可以看一下svc的默认参数。

svc
svc默认参数

2.2 对支持向量机的理解

我们之前说逻辑回归的时候,通过最好理解的线性回归举例,通过sigmoid函数将一个本该是线性回归的问题转化为了一个线性分类的问题。那么,这个时候问题来了,如果数据不是线性可分的呢?这时候支持向量机就展现出它的核技术的魅力了,通过将低维度的数据映射到高维度,从而将非线性的问题转换成一个线性问题。
是不是听着就觉得很绕?这是不可避免的,因为逻辑回归还可以用例子来理解,但是支持向量机基本上就是从数学推导的角度来选取一个最优的分类方法。但大家只要知道,算法是存在“进化”的,逻辑回归解决了线性回归到线性分类的难题,而支持向量机解决了线性分类到非线性分类的难题。
支持向量机其实说白了,就是想通过数学的方法,看看怎么样能够将数据“最大程度”地分离开,就要找这个“最大程度”,我们叫“边际”(margin)最大。而支持向量机的名字也是有原因的,“边际”其实只由两类中离分割线最近的点所决定,这些点叫“支持向量”。
另外,需要注意的是,在逻辑回归的损失函数求解过程中,我们是将不同类的标签记作1和0的,但是在支持向量机的损失函数求解中,我们是将不同类标签记作1和-1的,这么做是对我们求解有用的。

2.3 损失函数

我们之前说过,支持向量机的求解目标就是max margin,让“边际”最大。那么怎么去求这个“边际”呢?


支持向量机分析

原理鄙人的画图水平有限,在上图中,我们假设有一条线w·x+b=0可以将两类点分开,这条线往上平移到红色类的边缘处,这条线就是w·x+b=1,;往下平移到橙色类边缘处,这条线就是w·x+b=-1。那有人就会问,向上移应该是截距项增加啊,那也应该是w·x+b=-1啊?而且,为什么上下移动就是等号右边加一个单位呢?同志们,我们这里只是一个记号而已,要知道,w和b都是一个符号,两边可以同时除以-1,也可以同时除以其他数,等号左边也同时除以,这时候我们会得到新的w和b,但是我们同样用w和b表示而已,不必纠结。
我们如果假设Xc和Xd是决策边界上的两个点,那么就有W·Xc+b=0,W·Xd+b=0,相减得到W·(Xc-Xd)=0。两个向量点积为0,则说明垂直,w和我们要求的边际d是相同方向。
这时候,假设Xa和Xb分别在上边缘和下边缘上,那么就有W·Xa+b=1,W·Xb+b=-1,相减得到W·(Xa-Xb)=2。
我们对两边同时除以W的模,则有W/||W|| · (Xa-Xb) = 2/||W||,线性代数过关的小伙伴们能很快反应过来,等号左边其实就是Xa和Xb两点连线这个向量在w方向的投影,其实也就是我们要求的两条边界线之间的距离d。那么,我们的求解目标也很明确了,就是要max d,也就是max 2/||W||。我们将这个问题转化一下,其实也就是min 1/2 * W2,当然,外加一个限制条件,y * (w*x+b)>1 (w·x+b>1表示的是红色的点,这时候y=1;w·x+b<-1表示的是橙色的点,这时候y=-1。这下明白将两类标签定义为1和-1而不是1和0的用处了吧)。
当然,这只是我们弄出损失函数的第一步,这个带约束条件的最小化问题仍然是不好求解的。之后,我们还需要用拉格朗日乘数法,将上面这个约束条件去掉,转化为一个拉格朗日函数,我叫它第二步;再之后,我们要用拉格朗日对偶的性质,去求解我们第二步得到的拉格朗日函数,这是第三步。这就是我归纳的三步走。当然,这些数学推导还是有点复杂和繁琐的,我在这里也不推导了,想了解的自行百度。

2.4 损失函数求解

支持向量机损失函数的求解就不是梯度下降法这么简单的了,涉及太多数学推导,在这里我同样也不写了,大家一定要自行去了解序列最小优化算法(SMO),贴一篇吧:
https://blog.csdn.net/qq_39521554/article/details/80723770

2.5 核函数的选择(kernel & gamma)

接下来是SVM中第一个重要参数,kernel。我们之前也说过了,支持向量机之所以这么牛,就是因为核技术。核函数是什么呢?我们之前提到了,很多数据在本身的维度是线性不可分的,那怎么办呢?那就将数据映射到更高维度的空间去看一看,可能就可以线性分割了。核函数可以做什么呢?1、不需要去知道将低维映射到高维所需要用的函数;2、可以先在低维度进行点积运算,再直接映射到高纬度,大大减少了运算量;3、避免了维度诅咒。
参数kernel常用的共有四个:“linear”、“poly”、“rbf”、“sigmoid”。默认的并且是最常使用的是高斯核rbf。效果最差的、也是最不常用的是“sigmoid”。当然,它们各自有各自出场的场合。
我们可以来看下三种核函数(只看‘linear’、‘rbf’、‘sigmoid’,'poly'跑出结果是非常久的,这里就不实验了)在乳腺癌数据集上的表现。

from sklearn.svm import SVC
from sklearn import metrics

kernelList = ['linear','rbf','sigmoid']         

for kernel in kernelList:
    svc = SVC(kernel=kernel).fit(X_train,y_train)
    y_pred = svc.predict(X_test)
    score_svc = metrics.accuracy_score(y_test,y_pred)
    print(score_svc)

可以看到,‘linear’、‘rbf’、‘sigmoid’核表现分别为0.9790、0.6503、0.6503,可以看到,线性核表现最优,高斯核作为默认参数准确率却比较低。因为乳腺癌数据集是一个线性可分数据集。
如果我们对数据标准化一下呢?

# 读入癌症数据集
from sklearn import datasets
from sklearn.model_selection import train_test_split

cancer=datasets.load_breast_cancer()
X=cancer.data
y=cancer.target

# 数据标准化
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
scaler.fit(X)
X= scaler.transform(X)

X_train,X_test,y_train,y_test=train_test_split(X,y)

再执行一下上面的三个核函数,‘linear’、‘rbf’、‘sigmoid’的得分分别为0.9790、
0.9860、0.9790。我们会发现,高斯核的表现一下子变成了number one,“sigmoid”居然也都跟线性核打平手了。
因为涉及到算“距离”,所以可以看到,量纲问题就非常重要了。
因为高斯核(rbf)在处理线性和非线性问题时都很好用,所以我们重点看高斯核。

高斯径向基核函数表达式:

我们可以调的就是公式里的gamma值。我们试着来调一下gamma。
score_gamma=[]
gammaList=np.logspace(-10,1,50)

for gamma in gammaList:
    svc = SVC(gamma=gamma).fit(X_train,y_train)
    score_gamma.append(svc.score(X_test,y_test))

print('gamma={}时,得分最高={}'.format(gammaList[score_gamma.index(max(score_gamma))],max(score_gamma)))

gamma=0.004291934260128779时,得分最高=0.986013986013986。
当然,也可以将调参曲线画出来。

plt.plot(gammaList,score_gamma)
gamma调参

如果选用的是'poly'多项式核函数,还可以去调一下degree,大家可自行去试。

2.6 如何避免过拟合(重要参数C)

现实很残酷,基本是不可能找到一条线能够将两类点完全分开的。或者举一个更明显一点的例子,还是上面那个图,如果这时候新来了两个点1和点2,很明显原来的那条分界线就不能将两类点完全分开了,这时候蓝色的新边界才是能将点完全分开的最好分割线。但也有个问题,使用新的分割线,两类点之间的距离,也就是“边际”(margin)就变窄了,很有可能这两个点本身就是异常点,这时候就需要我们去权衡,是尽可能地将两类点分开呢,还是让大多数点分离得更远。前者呢,就叫做硬间隔(hard margin),这时候有过拟合的风险;后者呢,叫做软间隔(soft margin),会有欠拟合的风险。



为了防止过拟合呢,我们会给损失函数加上一个“松弛度”,但作为权衡,会在这个“松弛度”前面加上一个惩罚系数C,防止对分错的点“太过宽松”。
需要注意的是,逻辑回归中也有这么一个惩罚系数C,但它不是在正则项前面,而SVM中的C是在“松弛度”的前面,两个C效果却是一样的,C太大,模型容易过拟合;C太小,模型容易欠拟合。

score_C=[]
CList=np.linspace(0.01,30,50)

for i in CList:
    svc = SVC(C=i).fit(X_train,y_train)
    score_C.append(svc.score(X_test,y_test))

print('C={}时,得分最高={}'.format(CList[score_C.index(max(score_C))],max(score_C)))

C=0.6220408163265306时,得分最高=0.986013986013986。
同样可以画出来看一看。

plt.plot(CList,score_C)

2.7 如何处理不均衡问题

同样,调节class_weight。

三、K近邻(knn算法)

knn可以说是分类中原理最简单最容易理解的了。

3.1 sklearn实战

from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics

knn = KNeighborsClassifier().fit(X_train,y_train)

y_prob = knn.predict_proba(X_test)[:,1]                              
y_pred = knn.predict(X_test)                                       
fpr_knn,tpr_knn,threshold_knn = metrics.roc_curve(y_test,y_prob)   
auc_knn = metrics.auc(fpr_knn,tpr_knn)                              
score_knn = metrics.accuracy_score(y_test,y_pred)
print([score_knn,auc_knn])

knn算法的准确率为0.9441, AUC值为0.9766。
这个结果其实已经很不错的,如果再将数据标准化呢?
我们将数据标准化后,得出准确率为0.9930, AUC值为0.9977。这么一个最简单的分类算法,在乳腺癌数据集上的表现居然如此之好。

knn

3.2 knn原理

knn就是字面的意思,选定k个最近的点,看这k个点中占比最多的类作为标签。毫无疑问,k的取值至关重要。

3.3 K值选择(n_neighbors)

score_K=[]
KList=range(2,13)

for k in KList:
    knn = KNeighborsClassifier(n_neighbors=k).fit(X_train,y_train)
    score_K.append(knn.score(X_test,y_test))

print('K={}时,得分最高={}'.format(KList[score_K.index(max(score_K))],max(score_K)))

K=5时,得分最高=0.9930。

plt.plot(KList,score_K)

3.4 k个点的权重(weights)

我们要去想这么一个问题,假设一个点周围最近的5个点中,有2个点离得非常近,有3个点离得很远。这时候,虽然3个点的类占大头,但直觉告诉我们,它很可能是和离得比较近的那2个点是一类的。所以,可能我们需要给离得近的那2个点更大一点的权重。
weights的默认值是'uniform',表示所有最近邻样本权重都一样。如果是"distance",则权重和距离成反比例,即距离预测目标更近的近邻具有更高的权重,这样在预测类别或者做回归时,更近的近邻所占的影响因子会更加大。当然,我们也可以自定义权重,即自定义一个函数,输入是距离值,输出是权重值。这样我们可以自己控制不同的距离所对应的权重。

from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics

knn = KNeighborsClassifier(weights='distance').fit(X_train,y_train)

y_prob = knn.predict_proba(X_test)[:,1]                              
y_pred = knn.predict(X_test)                                       
fpr_knn,tpr_knn,threshold_knn = metrics.roc_curve(y_test,y_prob)   
auc_knn = metrics.auc(fpr_knn,tpr_knn)                              
score_knn = metrics.accuracy_score(y_test,y_pred)
print([score_knn,auc_knn])

将weights改为'distance'之后,会发现,精确度为0.9930,而AUC的值竟然上升到了0.9991。

3.5 距离计算方式(metric)

knn可以使用的距离度量较多,一般来说默认的欧式距离,但knn里默认的是‘minkowski’。可以来看下不同的距离计算方式下模型的表现。

list=['euclidean','manhattan','chebyshev','minkowski']

for i in list:
    knn = KNeighborsClassifier(metric=i).fit(X_train,y_train)
    print(knn.score(X_test,y_test))

可以看到,euclidean欧氏距离和minkowski闵可夫斯基距离得分最高,同为0.9930,manhattan曼哈顿距离次之为0.9790,chebyshev切比雪夫距离的效果最差为0.9510。

四、决策树

接下来我们进入到树这个大类里了,前面三种算法基本都涉及到一个距离的计算,但是树却不用,因此也并不需要标准化(乳腺癌的数据集标准化之后反而降低了得分)。

4.1sklearn实战

from sklearn import tree
from sklearn import metrics

dtc = tree.DecisionTreeClassifier()                              # 建立决策树模型
dtc.fit(X_train,y_train)                                         # 训练模型
y_prob = dtc.predict_proba(X_test)[:,1]                          # 预测1类的概率
y_pred = dtc.predict(X_test)                                     # 模型对测试集的预测结果 
fpr_dtc,tpr_dtc,threshod_dtc= metrics.roc_curve(y_test,y_prob)   # 获取真阳率、伪阳率、阈值
score_dtc = metrics.accuracy_score(y_test,y_pred)                
auc_dtc = metrics.auc(fpr_dtc,tpr_dtc) 
print([score_dtc,auc_dtc])

决策树得分0.9441,AUC值为0.9443。

dtc

4.2 决策树原理

关于决策树的原理也是可以查到很多了,只需要知道这些概念:
①信息量:-lnP
②熵:信息量的期望值(-∑PlnP)
③gini:和信息熵类似(∑P(1-P))
④信息增益
⑤信息增益率
⑥预剪枝方法:预剪枝方法:1限制深度;2限制叶子结点的样本个数;3对每次分裂的信息增益设定阈值。
⑦搞清楚ID3、C4.5、Cart树:
ID3的局限性:1信息增益存在过度拟合的缺点。2不能直接处理连续型变量。3对缺失值敏感。
C4.5& cart改进:1使用信息增益率。2对连续型的自动分箱。
关于熵:
https://www.jianshu.com/p/8a0ad237b0ed

4.3 如何衡量信息纯度(criterion)

决策树需要找出最佳的分枝方法,衡量的指标就是信息“不纯度”,“不纯度”越低,说明类别分的越纯,也就是分类越好。
criterion这个参数就是用来选择用何种方式来衡量这个“不纯度”,提供了信息熵(entropy)和基尼系数(gini)两种方法,上面有提到过。但是这个指标基本我们也不用去调,因为sklearn中用的是cart二叉树的方法,cart树的衡量指标就是gini。

4.4 如何防止过拟合(max_depth & min_samples_leaf & max_features & min_impurity_decrease)

大家一定要知道,树是天生过拟合的算法,包括之后的随机森林,甚至于以树为基础分类器的集成学习算法,都存在这样一个过拟合的属性。
要防止树的过拟合,第一个要提的就是max_depth,这也是防止过拟合最好的神器,在高维度地样本量时非常好用。

depthList=np.arange(3,10)
score=[]

for i in depthList:
    dtc = tree.DecisionTreeClassifier(max_depth=i).fit(X_train,y_train)
    score.append(dtc.score(X_test,y_test))

print('最优深度为{},最佳得分是{}'.format(depthList[score.index(max(score))],max(score)))

最优深度为6,最佳得分是0.9441。

plt.plot(depthList,score)

min_samples_leaf则限定每个叶子节点上的最小样本个数,防止最后分的太细。
max_features限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃,也是用来限制高维度数据的过拟合的。
min_impurity_decrease则限制信息增益的大小,信息增益小于设定数值的分枝不会发生。
其实可以通过网格搜索来确定以上这些参数的最佳组合。

from sklearn import tree
from sklearn import metrics
from sklearn.model_selection import GridSearchCV

params={'max_depth':[*np.arange(3,10)]
        ,'min_samples_leaf':[*np.arange(1,50,5)]
        ,'min_impurity_decrease':[*np.linspace(0,0.5,20)]}
dtc = tree.DecisionTreeClassifier()

GS = GridSearchCV(dtc, params, cv=10).fit(X_train,y_train)
GS.best_params_
GS.best_score_

得到最佳参数{'max_depth': 4, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 1},交叉验证最佳得分0.9343。可以看到,加了别的参数的时候,最优深度就不再是6而是4了。

4.5 树的可视化

# 树的可视化
import graphviz
from sklearn.tree import export_graphviz

dtc = tree.DecisionTreeClassifier().fit(X_train,y_train)

export_graphviz(dtc,out_file='DecisionTree.dot',class_names=['0','1'],impurity=False,filled=True)

with open('DecisionTree.dot') as f:
    dot_graph=f.read()

graphviz.Source(dot_graph)

五、随机森林

随机森林,顾名思义,就是很多棵树组合在一起。

5.1 sklearn实战

from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics

rfc = RandomForestClassifier()                                     # 建立随机森林分类器
rfc.fit(X_train,y_train)                                           # 训练随机森林模型
y_prob = rfc.predict_proba(X_test)[:,1]                            # 预测1类的概率
y_pred=rfc.predict(X_test)                                         # 模型对测试集的预测结果
fpr_rfc,tpr_rfc,threshold_rfc = metrics.roc_curve(y_test,y_prob)   # 获取真阳率、伪阳率、阈值  
auc_rfc = metrics.auc(fpr_rfc,tpr_rfc)                             # AUC得分
score_rfc = metrics.accuracy_score(y_test,y_pred)                  # 模型准确率
print([score_rfc,auc_rfc])

模型准确率为0.9580,AUC值为0.9942。

rfc

5.2 到底种多少棵树(n_estimators)

我们可以在5.1中看到随机森林默认的n_estimators是10,显然,对于集成学习来说,10棵树的数量是偏少了的。好像更新的版本之后n_estimators默认值会变为100。我们来看下随着建的树越多,训练集和测试集数据的得分会有什么样的变化。

from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics

train_score=[]
test_score=[]

for i in range(10,101):
    rfc = RandomForestClassifier(n_estimators=i,random_state=13).fit(X_train,y_train)
    train_score.append(rfc.score(X_train,y_train))
    test_score.append(rfc.score(X_test,y_test))
    
plt.figure(figsize=(20, 5))
plt.plot(range(10,101),train_score,color='orange',label='train')
plt.plot(range(10,101),test_score,color='red',label='test')
plt.legend(loc='lower right')
plt.show()

可以看到,随着树的数量增多,训练集的效果明显变好,测试集的得分有所上升,但变化不大。初步目测,n_estimators在60左右效果是最好的。

5.3 如何防止过拟合

这个跟树是几乎一样,因为咱本身也是树组成的。调节几个重要参数就好了:max_depth、min_samples_leaf、max_features、min_impurity_decrease。

六、二分类模型评价指标

二分类模型评价指标围绕混淆矩阵展开,这个也太经典了,不在这儿详说了:
https://blog.csdn.net/qq_27575895/article/details/81476871
以随机森林为例,看一看precision和recall值。

metrics.precision_score(y_test,y_pred) # 精确率
metrics.recall_score(y_test,y_pred) # 召回率

随机森林模型下精确率和召回率均为0.9759。

plt.style.use('bmh')
plt.figure(figsize=(13,10))

plt.plot(fpr_lr,tpr_lr,label='lr')                             # 逻辑回归
plt.plot(fpr_svc,tpr_svc,label='svc')                          # 支持向量机模型
plt.plot(fpr_knn,tpr_knn,label='knn')                             # K近邻
plt.plot(fpr_dtc,tpr_dtc,label='dtc')                          # 决策树
plt.plot(fpr_rfc,tpr_rfc,label='rfc')                          # 随机森林

plt.legend(loc='lower right',prop={'size':25})
plt.xlabel('伪阳率')
plt.ylabel('真阳率')
plt.title('ROC曲线')
plt.show()
ROC曲线

当然,小白也是机器学习初学者,有什么理解错误的地方也欢迎指正。这篇文也是针对初学者做的一个总结,希望能够有帮助。本文后续还会不断更新,加入新的分类算法,加入新的调参参数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容