把sklearn几乎所有分类方法都用一遍
数据来源于1994年美国人口普查数据库。(下载地址:https://archive.ics.uci.edu/ml/datasets/Adult )
预测任务是确定一个人的年收入是否超过5万。
数据集包含14个属性,分别是:年龄、工作类别、final weight、教育、教育数量、婚姻状况、职业、关系、种族、性别、资本收益、资本损失、每周小时数、国籍。其中,年龄、final weight、教育数量、资本收益、资本损失和每周小时数是数值标签,其余是标称标签。
序号 | 字段名 | 含义 | 类型 |
---|---|---|---|
0 | age | 年龄 | double |
1 | workclass | 工作类型 | string |
2 | fnlwgt | 序号 | string |
3 | education | 教育程度 | string |
4 | education_num | 受教育时间 | double |
5 | marital_status | 婚姻状态 | string |
6 | occupation | 职业 | string |
7 | relationship | 关系 | string |
8 | race | 种族 | string |
9 | sex | 性别 | string |
10 | capital_gain | 资本收益 | string |
11 | capital_loss | 资本损失 | string |
12 | hours_per_week | 每周工作小时数 | double |
13 | native_country | 原籍 | string |
14 | (label)income | 收入 | string |
import numpy as np
import pandas as pd
from sklearn.metrics import classification_report
from sklearn.metrics import roc_auc_score,roc_curve, auc
train = pd.read_csv('data.csv',header=None)
test = pd.read_csv('test.csv',header=None)
首先为各列特征变量设置列标签,预览数据集信息。
#设置特征变量的标签
cols = ['age', 'workclass', 'fnlwgt', 'education', 'education_num', 'marital_status',
'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
'hours_per_week', 'native_country', 'wage_class']
train.columns = cols
test.columns = cols
train.head()
age | workclass | fnlwgt | education | education_num | marital_status | occupation | relationship | race | sex | capital_gain | capital_loss | hours_per_week | native_country | wage_class | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
2 | 38 | Private | 215646 | HS-grad | 9 | Di vorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
观察到数据的输出列存在空格,为了统一输出格式,需要对带有空格的标签替换处理。
#统一输出列格式,删去空格
train['wage_class'] = train['wage_class'].map(lambda x:x.replace(' ',''))
test['wage_class'] = test['wage_class'].replace({' <=50K.':'<=50K',' >50K.':'>50K'})
另外,部份数据缺失,在源数据中被填为“?”,需要将“?”标为NAN值。
#将“?”填补为NAN
train = train.replace(' ?',np.nan)
test = test.replace(' ?',np.nan)
对于特征,我们可以分析单特征,也可以分析不同特征之间的关系。
本数据集中的特征分为两种:标称型和数值型:
将训练集和测试集合并,并用sign标记。
#将训练集和测试集合并到新的Dataframe: adult,并运用sign对训练集和测试集标记
tr = train.copy()
tr['sign'] = 'train'
te = test.copy()
te['sign'] = 'test'
adult = tr.append(te).reset_index().drop(columns={'index'})
adult.head()
age | workclass | fnlwgt | education | education_num | marital_status | occupation | relationship | race | sex | capital_gain | capital_loss | hours_per_week | native_country | wage_class | sign | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K | train |
1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K | train |
2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K | train |
3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K | train |
4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K | train |
统计各个数值型变量的数据分布情况如下:
#展示变量的数据描述(不包括带有缺失值的变量)
adult.describe()
age | fnlwgt | education_num | capital_gain | capital_loss | hours_per_week | |
---|---|---|---|---|---|---|
count | 48842.000000 | 4.884200e+04 | 48842.000000 | 48842.000000 | 48842.000000 | 48842.000000 |
mean | 38.643585 | 1.896641e+05 | 10.078089 | 1079.067626 | 87.502314 | 40.422382 |
std | 13.710510 | 1.056040e+05 | 2.570973 | 7452.019058 | 403.004552 | 12.391444 |
min | 17.000000 | 1.228500e+04 | 1.000000 | 0.000000 | 0.000000 | 1.000000 |
25% | 28.000000 | 1.175505e+05 | 9.000000 | 0.000000 | 0.000000 | 40.000000 |
50% | 37.000000 | 1.781445e+05 | 10.000000 | 0.000000 | 0.000000 | 40.000000 |
75% | 48.000000 | 2.376420e+05 | 12.000000 | 0.000000 | 0.000000 | 45.000000 |
max | 90.000000 | 1.490400e+06 | 16.000000 | 99999.000000 | 4356.000000 | 99.000000 |
统计各个变量的标签数量和最多的一类标签。
#展示变量的数据描述(包括带有缺失值的变量)
adult.describe(include=['O'])
workclass | education | marital_status | occupation | relationship | race | sex | native_country | wage_class | sign | |
---|---|---|---|---|---|---|---|---|---|---|
count | 46043 | 48842 | 48842 | 46033 | 48842 | 48842 | 48842 | 47985 | 48842 | 48842 |
unique | 8 | 16 | 7 | 14 | 6 | 5 | 2 | 41 | 2 | 2 |
top | Private | HS-grad | Married-civ-spouse | Prof-specialty | Husband | White | Male | United-States | <=50K | train |
freq | 33906 | 15784 | 22379 | 6172 | 19716 | 41762 | 32650 | 43832 | 37155 | 32561 |
#导入画图工具包
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno #missingno是一个可视化缺失值的库
分别绘制训练集和测试集的缺失值图,一共有三列有缺失:work_class、occupation和native_country。
#绘制训练集和测试集的缺失值柱状图
plt.subplot(1,2,1)
msno.bar(train)
plt.title('train')
plt.subplot(1,2,2)
plt.title('test')
msno.bar(test)
plt.show()
为了进一步对缺失值之间的相似度进行分析,绘制缺失值热力图。利用热力图可以观察多个特征两两的相似度,相似度由皮尔逊相关系数度量。
occupation和workclass为1表明这两个变量在测试集和训练集都是同步缺失的。
#绘制训练集和测试集的缺失值热力图
msno.heatmap(train,figsize=(3, 2)),msno.heatmap(test,figsize=(3, 2))
(,
)
分别对训练集和测试集绘制缺失值矩阵图。矩阵图中白线越多,代表缺失值越多。
结果表明workclass和occupation相比于native_country有更多的缺失值
#绘制缺失值矩阵图
msno.matrix(train,figsize=(6,3))
msno.matrix(test,figsize=(6,3))
将训练集和测试集合并,进一步统计缺失数据在总体数据中所占的比例,并绘制柱状图:
测试集中,工作类型workclass和职业occupation分别有5.91%和5.93%的缺失,原籍native_country有1.68%缺失。
训练集中,工作类型workclass和职业occupation分别有5.63%和5.66%的缺失,原籍native_country有1.79%缺失。
#统计缺失值所占的比例
temp = adult.groupby('sign').apply(lambda x :x.isna().sum()/len(x))
temp = temp.loc[:,(temp!=0).any()]
temp
workclass | occupation | native_country | |
---|---|---|---|
sign | |||
test | 0.059149 | 0.059333 | 0.016829 |
train | 0.056386 | 0.056601 | 0.017905 |
#绘制柱状图
temp.plot(kind='bar',figsize=(8,4))
发现数据存在occupation缺失而workclass为"Never-worked"的情况,反之则不存在。
这是由于无工作者没有职业,此部分可直接将这些occupation填补为一个新的类即可。
#存在以下情况,但不存在occupation不缺失但workclass缺失的情况
#这是由于无工作者没有职业,这部分直接为occupation填补一个新类即可
adult[['occupation','workclass']][(adult['occupation'].isna()==True)&(adult['workclass'].isna()==False)]
occupation | workclass | |
---|---|---|
5361 | NaN | Never-worked |
10845 | NaN | Never-worked |
14772 | NaN | Never-worked |
20337 | NaN | Never-worked |
23232 | NaN | Never-worked |
32304 | NaN | Never-worked |
32314 | NaN | Never-worked |
41346 | NaN | Never-worked |
44168 | NaN | Never-worked |
46459 | NaN | Never-worked |
#把上述这种情况填补一下
adult.loc[adult['workclass']==' Never-worked','occupation'] = ' Never-worked'
两种常见的编码方式:
表示分类变量最常用的方法就是使用 one-hot 编码 ( onehot-encoding) 或 N 取一编码 ( one-out-of-N encoding) , 也叫虚拟变量( dummy variable) 。 虚拟变量背后的思想是将一个分类变量替换为一个或多个新特征, 新特征取值为 0 和 1。 One-Hot编码是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值。然后,每个整数值被表示为二进制向量,除了整数的索引之外,它都是零值,它被标记为1。
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
#将分类属性编码
for col in col_dis:
adult[col] = pd.Categorical(adult[col]).codes
#onehot编码
adult_oh = adult.copy()
for col in col_dis:
integer_encoded = np.array(adult[col]+1)
# label_encoder= LabelEncoder()
# integer_encoded = label_encoder.fit_transform(adult_oh[col])
onehot_encoder = OneHotEncoder(sparse=False,n_values='auto')
one_hot_df = pd.DataFrame(onehot_encoder.fit_transform(integer_encoded.reshape(-1,1)))
one_hot_df.columns = [col+'_'+str(x) for x in one_hot_df.columns]
adult_oh = adult_oh.merge(one_hot_df,left_index=True,right_index=True)
adult_oh = adult_oh.drop(columns={col})
编码完成后,需要将训练集和测试集划分开。
#分割训练集
from sklearn.model_selection import train_test_split
train_oh = adult_oh[adult_oh['sign']=='train'].drop(columns={'sign'})
test_oh = adult_oh[adult_oh['sign']=='test'].drop(columns={'sign'})
y = train_oh.pop('wage_class')
y = y.map(lambda x:0 if x=='<=50K' else 1)
X = train_oh
X_train, X_valid, Y_train, Y_valid = train_test_split(X, y, test_size=0.2, random_state=42)
#分割测试集
y_t = test_oh.pop('wage_class')
y_t = y_t.map(lambda x:0 if x=='<=50K' else 1)
X_t = test_oh
随机森林是一个包含多个决策树的分类器。
根据下列算法而建造每棵树:
输入为样本集,弱分类器迭代次数T。
输出为最终的强分类器f(x)
(1)对于t = 1,2,3,…,T:
对训练集进行第t次采样,共采集m次,得到包含m个样本的采样集Dt
用采样集Dt训练第t个决策树模型Gt(x),在训练决策树模型的节点的时候,在节点上所有的样本特征中选择一部分样本特征,在这些随机选择的部分样本特征中选择一个最优的特征来做决策树的左右子树划分。
(2)如果是分类算法预测,则T个弱学习器投出最多票数的类别或者类别之一为最终类别。如果是回归算法,T个弱学习器得到的回归结果进行算术平均得到的值为最终的模型输出。
随机森林的优点
随机森林的缺点
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
首先对随机森林调参,选取最优拟合参数。
主要有3类特征可以被调整,以改善该模型的预测能力:
A. max_features:
随机森林允许单个决策树使用特征的最大数量:
Auto/None :简单地选取所有特征,每棵树都没有任何的限制。
sqrt :此选项是每颗子树可以利用总特征数的平方根个。“log2”是另一种相似类型的选项。
0.2:此选项允许每个随机森林的子树可以利用变量(特征)数的20%。如果想考察的特征x%的作用, 我们可以使用“0.X”的格式。
B. n_estimators:
想要建立子树的数量。 较多的子树可以让模型有更好的性能,使预测更好更稳定,但同时让你的代码变慢。
C. min_sample_leaf:
较小的叶子使模型更容易捕捉训练数据中的噪声。
D. max_depth:
决策树最大深度。默认情况下决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。
rf = RandomForestClassifier()
param_grid = {'n_estimators': [50],'max_features': ['auto', 'sqrt', 'log2'],'max_depth':np.arange(6,11),'min_samples_leaf':[2,3,5],'min_samples_split':[0.1,0.3,0.5]}
grid = GridSearchCV(RandomForestClassifier(), param_grid=param_grid, cv=5, scoring='roc_auc')
grid.fit(X,y)
GridSearchCV(cv=5, error_score='raise-deprecating',
estimator=RandomForestClassifier(bootstrap=True, class_weight=None,
criterion='gini', max_depth=None,
max_features='auto',
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
min_samples_leaf=1,
min_samples_split=2,
min_weight_fraction_leaf=0.0,
n_estimators='warn', n_jobs=None,
oob_score=False,
random_state=None, verbose=0,
warm_start=False),
iid='warn', n_jobs=None,
param_grid={'max_depth': array([ 6, 7, 8, 9, 10]),
'max_features': ['auto', 'sqrt', 'log2'],
'min_samples_leaf': [2, 3, 5],
'min_samples_split': [0.1, 0.3, 0.5],
'n_estimators': [50]},
pre_dispatch='2*n_jobs', refit=True, return_train_score=False,
scoring='roc_auc', verbose=0)
grid.best_params_
{'max_depth': 10,
'max_features': 'sqrt',
'min_samples_leaf': 5,
'min_samples_split': 0.1,
'n_estimators': 50}
根据网格搜索的结果,选取好最佳参数后,拟合模型:
#随机森林
rf = RandomForestClassifier(max_depth = 10,max_features='sqrt',min_samples_leaf = 5,min_samples_split = 0.1,n_estimators=150,class_weight='balanced')
rf.fit(X,y)
RandomForestClassifier(bootstrap=True, class_weight='balanced',
criterion='gini', max_depth=10, max_features='sqrt',
max_leaf_nodes=None, min_impurity_decrease=0.0,
min_impurity_split=None, min_samples_leaf=5,
min_samples_split=0.1, min_weight_fraction_leaf=0.0,
n_estimators=150, n_jobs=None, oob_score=False,
random_state=None, verbose=0, warm_start=False)
根据输出结果,该随机森林模型的精确率为85%,召回率为78%,F1值(精确度和召回率的调和平均值)为79%,样本数为16281.
#测试集预测
print(classification_report(y_t, rf.predict(X_t)))
precision recall f1-score support
0 0.95 0.75 0.84 12435
1 0.52 0.87 0.65 3846
accuracy 0.78 16281
macro avg 0.73 0.81 0.74 16281
weighted avg 0.85 0.78 0.79 16281
决策树(Decision Tree)是一种非参数的有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。
决策树的构造
优点:
简单易懂,容易解释,可视化,适用性广
缺点:
容易过拟合,
数据中的小变化会影响结果,不稳定,
每一个节点的选择都是贪婪算法,不能保证全局最优解。
参数选择:
criterion:不纯度的计算方法,默认为“gini”,基尼系数;“entropy”,使用信息熵
max_depth :树的最大深度
min_samples_split : 拆分节点所需的最小样本数
min_samples_leaf : 拆分节点后的叶子节点的最小样本数,满足时才进行拆分。
#自动找到最优参数
from sklearn.tree import DecisionTreeClassifier
param = {'criterion':['gini','entropy'],'max_depth':np.arange(9,15),'min_samples_leaf':[2,3,5],'min_samples_split':[0.1,0.3,0.5]}
grid = GridSearchCV(DecisionTreeClassifier(),param_grid=param,cv=5,scoring='roc_auc')
grid.fit(X,y)
GridSearchCV(cv=5, error_score='raise-deprecating',
estimator=DecisionTreeClassifier(class_weight=None,
criterion='gini', max_depth=None,
max_features=None,
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
min_samples_leaf=1,
min_samples_split=2,
min_weight_fraction_leaf=0.0,
presort=False, random_state=None,
splitter='best'),
iid='warn', n_jobs=None,
param_grid={'criterion': ['gini', 'entropy'],
'max_depth': array([ 9, 10, 11, 12, 13, 14]),
'min_samples_leaf': [2, 3, 5],
'min_samples_split': [0.1, 0.3, 0.5]},
pre_dispatch='2*n_jobs', refit=True, return_train_score=False,
scoring='roc_auc', verbose=0)
grid.best_params_
{'criterion': 'entropy',
'max_depth': 10,
'min_samples_leaf': 3,
'min_samples_split': 0.1}
根据网格搜索的结果,选取好最佳参数后,拟合模型:
#拟合决策树模型
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(criterion="entropy" ,random_state=0,splitter='random',max_depth=10,min_samples_leaf=3,min_samples_split=0.1,class_weight='balanced')
dt.fit(X,y)
DecisionTreeClassifier(class_weight='balanced', criterion='entropy',
max_depth=10, max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=3, min_samples_split=0.1,
min_weight_fraction_leaf=0.0, presort=False,
random_state=0, splitter='random')
根据输出结果,该决策树模型的精确率为84%,召回率为77%,F1值(精确度和召回率的调和平均值)为78%.
#结果-评价指标
print(classification_report(y_t, dt.predict(X_t)))
precision recall f1-score support
0 0.95 0.74 0.83 12435
1 0.50 0.87 0.64 3846
accuracy 0.77 16281
macro avg 0.72 0.80 0.73 16281
weighted avg 0.84 0.77 0.78 16281
logistic回归又称logistic回归分析,是一种广义的线性回归分析模型。
Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法完成。
优点:
计算代价不高,易于理解和实现。
缺点:
容易欠拟合,分类精度可能不高。
适用数据类型:
数值型和标称型数据。
参数说明:
penalty:惩罚项,str类型,可选参数为l1和l2,默认为l2。用于指定惩罚项中使用的范数。
C: C为正则化系数λ的倒数,通常默认为1
intercept_scaling:仅在正则化项为”liblinear”,且fit_intercept设置为True时有用。float类型,默认为1。
random_state:随机数种子,int类型,可选参数,默认为无,仅在正则化优化算法为sag,liblinear时有用。
solver:优化算法选择参数,只有五个可选参数,即newton-cg,lbfgs,liblinear,sag,saga。默认为liblinear。solver参数决定了我们对逻辑回归损失函数的优化方法。
max_iter:算法收敛最大迭代次数,int类型,默认为10。仅在正则化优化算法为newton-cg, sag和lbfgs才有用,算法收敛的最大迭代次数。
multi_class:分类方式选择参数,str类型,可选参数为ovr和multinomial,默认为ovr。ovr即前面提到的one-vs-rest(OvR),而multinomial即前面提到的many-vs-many(MvM)。如果是二元逻辑回归,ovr和multinomial并没有任何区别,区别主要在多元逻辑回归上。
class_weight:用于标示分类模型中各种类型的权重,可以是一个字典或者’balanced’字符串,默认为不输入,也就是不考虑权重,即为None。如果选择输入的话,可以选择balanced让类库自己计算类型权重,或者自己输入各个类型的权重。
LogisticRegressionCV使用了交叉验证来选择正则化系数C。
from sklearn.linear_model import LogisticRegression,LogisticRegressionCV
lr_cv2 = LogisticRegressionCV(Cs=[0.001,0.01,0.1,1,10,100,1000,10000,100000],random_state=0, solver='liblinear',multi_class='ovr',penalty='l2',max_iter=100,cv=3,scoring='roc_auc',class_weight='balanced')
lr_cv2.fit(X,y)
LogisticRegressionCV(Cs=[0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000, 100000],
class_weight='balanced', cv=3, dual=False, fit_intercept=True,
intercept_scaling=1.0, max_iter=100, multi_class='ovr',
n_jobs=1, penalty='l2', random_state=0, refit=True,
scoring='roc_auc', solver='liblinear', tol=0.0001, verbose=0)
lr_cv2.C_
array([100000.])
print(classification_report(y_t,lr_cv2.predict(X_t)))
precision recall f1-score support
0 0.94 0.79 0.86 12435
1 0.56 0.85 0.67 3846
avg / total 0.85 0.81 0.82 16281
lr_cv1 = LogisticRegressionCV(Cs=[0.01,0.1,1,5,10,50,100],random_state=0, solver='liblinear',multi_class='ovr',penalty='l1',max_iter=100,cv=3,scoring='roc_auc',class_weight='balanced')
lr_cv1.fit(X,y)
# lr_cv2 = LogisticRegressionCV(Cs=[1,2,3,4,5,10,50,100,1000],random_state=0, solver='liblinear',multi_class='ovr',penalty='l2',max_iter=100,cv=3,scoring='roc_auc')
# lr_cv2.fit(X,y)
LogisticRegressionCV(Cs=[1], class_weight='balanced', cv=3, dual=False,
fit_intercept=True, intercept_scaling=1.0, l1_ratios=None,
max_iter=100, multi_class='ovr', n_jobs=None, penalty='l1',
random_state=0, refit=True, scoring='roc_auc',
solver='liblinear', tol=0.0001, verbose=0)
lr_cv1.C_
array([1])
print(classification_report(y_t,lr_cv1.predict(X_t)))
precision recall f1-score support
0 0.94 0.80 0.86 12435
1 0.56 0.84 0.67 3846
accuracy 0.81 16281
macro avg 0.75 0.82 0.77 16281
weighted avg 0.85 0.81 0.82 16281
对比后发现正则化系数为默认值1时,logistics regression回归效果最好。
#LogisticRegression模型
from sklearn.linear_model import LogisticRegression,LogisticRegressionCV
#LR默认是L2惩罚
lr= LogisticRegression(random_state=0, solver='liblinear',multi_class='ovr',max_iter=100,class_weight='balanced')
lr.fit(X,y)
LogisticRegression(C=1.0, class_weight='balanced', dual=False,
fit_intercept=True, intercept_scaling=1, l1_ratio=None,
max_iter=100, multi_class='ovr', n_jobs=None, penalty='l2',
random_state=0, solver='liblinear', tol=0.0001, verbose=0,
warm_start=False)
根据输出结果,该LR模型的精确率为85%,召回率为81%,F1值(精确度和召回率的调和平均值)为82%,样本数为16281.
#输出LR模型拟合结果
print(classification_report(y_t,lr.predict(X_t)))
precision recall f1-score support
0 0.94 0.79 0.86 12435
1 0.56 0.85 0.67 3846
accuracy 0.81 16281
macro avg 0.75 0.82 0.77 16281
weighted avg 0.85 0.81 0.82 16281
Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器)。
算法过程
优点
泛化错误率低,无需参数调整
缺点
对离群点敏感
参数说明
base_estimator:基分类器,默认是决策树,在该分类器基础上进行boosting,理论上可以是任意一个分类器,但是如果是其他分类器时需要指明样本权重。
n _estimators:基分类器提升(循环)次数,默认是50次,这个值过大,模型容易过拟合;值过小,模型容易欠拟合。
learning_rate:学习率,表示梯度收敛速度,默认为1,如果过大,容易错过最优值,如果过小,则收敛速度会很慢;该值需要和n_estimators进行一个权衡,当分类器迭代次数较少时,学习率可以小一些,当迭代次数较多时,学习率可以适当放大。
algorithm:boosting算法,也就是模型提升准则,有两种方式SAMME, 和SAMME.R两种,默认是SAMME.R,两者的区别主要是弱学习器权重的度量,前者是对样本集预测错误的概率进行划分的,后者是对样本集的预测错误的比例,即错分率进行划分的,默认是用的SAMME.R。
random_state:随机种子设置。
#Adaboost模型
from sklearn.ensemble import AdaBoostClassifier
ada=AdaBoostClassifier(n_estimators=100)
ada.fit(X,y)
AdaBoostClassifier(algorithm='SAMME.R', base_estimator=None, learning_rate=1.0,
n_estimators=100, random_state=None)
根据输出结果,该adaboost模型的精确率为85%,召回率为86%,F1值(精确度和召回率的调和平均值)为86%,样本数为16281.
#输出Adaboost模型拟合结果
print(classification_report(y_t,ada.predict(X_t)))
precision recall f1-score support
0 0.89 0.94 0.91 12435
1 0.76 0.62 0.68 3846
accuracy 0.86 16281
macro avg 0.82 0.78 0.80 16281
weighted avg 0.86 0.86 0.86 16281
K最近邻(k-Nearest Neighbor,KNN)分类算法,是最简单的机器学习算法之一。
该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
算法过程:
优点:
缺点:
参数介绍:
n_neighbors:KNN中的k值,默认为5;
weights:用于标识每个样本的近邻样本的权重,可选择"uniform",“distance” 或自定义权重。默认"uniform",所有最近邻样本权重都一样。如果是"distance",则权重和距离成反比例;如果样本的分布是比较成簇的,即各类样本都在相对分开的簇中时,我们用默认的"uniform"就可以了,如果样本的分布比较乱,规律不好寻找,选择"distance"是一个比较好的选择;
metric,p:距离度量,默认闵可夫斯基距离 “minkowski”(p=1为曼哈顿距离, p=2为欧式距离);
from sklearn.neighbors import KNeighborsClassifier
首先运用网格搜索方法,搜索该数据集下KNN模型的最佳参数。
#自动找到最优参数
param = {'metric':['euclidean', 'manhattan','chebyshev'],'n_neighbors':np.arange(3,11),'weights':['uniform','distance']}
grid = GridSearchCV(KNeighborsClassifier(),param_grid=param,cv=5,scoring='roc_auc')
grid.fit(X,y)
GridSearchCV(cv=5, error_score='raise',
estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=5, p=2,
weights='uniform'),
fit_params=None, iid=True, n_jobs=1,
param_grid={'metric': ['euclidean', 'manhattan', 'chebyshev'], 'n_neighbors': array([ 3, 4, 5, 6, 7, 8, 9, 10]), 'weights': ['uniform', 'distance']},
pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
scoring='roc_auc', verbose=0)
grid.best_params_
{'metric': 'manhattan', 'n_neighbors': 7, 'weights': 'distance'}
搜寻得到最优参数,即用曼哈顿距离作为距离度量,每次搜寻最近的7个邻近点,且权重与距离呈反比。
#拟合KNN模型
knn = KNeighborsClassifier(n_neighbors = 7,metric='manhattan',weights='distance')
knn.fit(X,y)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='manhattan',
metric_params=None, n_jobs=None, n_neighbors=7, p=2,
weights='distance')
根据输出结果,该KNN模型的精确率为77%,召回率为79%,F1值(精确度和召回率的调和平均值)为77%.
#输出KNN拟合结果
print(classification_report(y_t, knn.predict(X_t)))
precision recall f1-score support
0 0.83 0.92 0.87 12435
1 0.58 0.37 0.45 3846
accuracy 0.79 16281
macro avg 0.70 0.64 0.66 16281
weighted avg 0.77 0.79 0.77 16281
GBDT模型是一个集成模型,基分类器采用CART,使用的是CART树中的回归树,集成方式为Gradient Boosting。
算法过程概述
优点
缺点:
由于弱学习器之间存在依赖关系,难以并行训练数据。
Boosting框架参数介绍
弱学习器(决策树)框架参数介绍
from sklearn.ensemble import GradientBoostingClassifier
#GBDT模型
gbdt = GradientBoostingClassifier()
gbdt.fit(X,y)
GradientBoostingClassifier(criterion='friedman_mse', init=None,
learning_rate=0.1, loss='deviance', max_depth=3,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=100,
n_iter_no_change=None, presort='auto',
random_state=None, subsample=1.0, tol=0.0001,
validation_fraction=0.1, verbose=0,
warm_start=False)
根据输出结果,该GBDT模型的精确率为87%,召回率为89%,F1值(精确度和召回率的调和平均值)为87%.
#输出GBDT分类结果
print(classification_report(y_t, gbdt.predict(X_t)))
precision recall f1-score support
0 0.89 0.95 0.92 12435
1 0.80 0.61 0.69 3846
accuracy 0.87 16281
macro avg 0.84 0.78 0.81 16281
weighted avg 0.87 0.87 0.87 16281
朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。先通过已给定的训练集,以特征词之间独立作为前提假设,学习从输入到输出的联合概率分布,再基于学习到的模型,输入X求出使得后验概率最大的输出Y。
优点
朴素贝叶斯算法假设了数据集属性之间是相互独立的,因此算法的逻辑性十分简单,并且算法较为稳定,当数据呈现不同的特点时,朴素贝叶斯的分类性能不会有太大的差异。换句话说就是朴素贝叶斯算法的健壮性比较好,对于不同类型的数据集不会呈现出太大的差异性。当数据集属性之间的关系相对比较独立时,朴素贝叶斯分类算法会有较好的效果。
缺点
属性独立性的条件同时也是朴素贝叶斯分类器的不足之处。数据集属性的独立性在很多情况下是很难满足的,因为数据集的属性之间往往都存在着相互关联,如果在分类过程中出现这种问题,会导致分类的效果大大降低。
参数说明
在scikit-learn中,一共有3个朴素贝叶斯的分类算法类。分别是GaussianNB,MultinomialNB和BernoulliNB。其中GaussianNB是先验为高斯分布的朴素贝叶斯,MultinomialNB是先验为多项式分布的朴素贝叶斯,而BernoulliNB是先验为伯努利分布的朴素贝叶斯。
这三个类适用的分类场景各不相同,一般来说,如果样本特征的分布大部分是连续值,使用GaussianNB会比较好。如果如果样本特征的分大部分是多元离散值,使用MultinomialNB比较合适。而如果样本特征是二元离散值或者很稀疏的多元离散值,应该使用BernoulliNB。
#建立朴素贝叶斯模型
from sklearn.naive_bayes import GaussianNB
nb=GaussianNB()
nb.fit(X,y)
GaussianNB(priors=None, var_smoothing=1e-09)
根据输出结果,该NB模型的精确率为77%,召回率为80%,F1值(精确度和召回率的调和平均值)为77%.
#输出NB运行结果
print(classification_report(y_t, nb.predict(X_t)))
precision recall f1-score support
0 0.82 0.95 0.88 12435
1 0.64 0.30 0.41 3846
accuracy 0.80 16281
macro avg 0.73 0.63 0.65 16281
weighted avg 0.77 0.80 0.77 16281
支持向量机属于一种二类分类模型,它的模型是定义在特征空间上的间隔最大的分类器。支持向量机的本质在于在特征空间寻求一个间隔最大的超平面。
算法的核心思想
1.在线性可分的情况下,利用间隔最大化的学习策略寻求一个间隔最大的超平面。
2在线性不可分的情况下,通过核函数将低维的特征向量空间映射到高维来寻求线性可分。
优点
1对于线性不可分的情况可以通过核函数,映射到高维特征空间实现线性可分。
2.SVM学习问题可以表示为凸优化问题,因此可以利用已知的有效算法发现目标函数的全局最小值。而其他分类方法(如基于规则的分类器和人工神经网络)都采用一种基于贪心学习的策略来搜索假设空间,这种方法一般只能获得局部最优解。
3.小集群分类效果好。
缺点
1 SVM仅仅只限于一个二类分类问题,对于多分类问题解决效果并不好。
2.仅局限于小集群样本,对于观测样本太多时,效率较低。
3.寻求合适的核函数相对困难。
参数说明
(1)C: 目标函数的惩罚系数C,用来平衡分类间隔margin和错分样本的,default C = 1.0;
(2)kernel:参数选择有RBF, Linear, Poly, Sigmoid,precomputed或者自定义一个核函数, 默认的是"RBF",即径向基核,也就是高斯核函数;而Linear指的是线性核函数,Poly指的是多项式核,Sigmoid指的是双曲正切函数tanh核。
(3)degree:degree决定了多项式的最高次幂;
(4)class_weight:指每个类所占据的权重,默认为1,即默认正类样本数量和反类一样多,可以用一个字典dict指定每个类的权值,或者选择默认的参数balanced,指按照每个类中样本数量的比例自动分配权值。
#支持向量机的训练
from sklearn import svm
import time
t1=time.time()
svc_bal = svm.SVC(class_weight='balanced') #自动调整不平衡样本
svc_bal.fit(X, y)
print(time.time()-t1)
979.9100477695465
#输出SVM训练结果
print(classification_report(y_t, svc_bal.predict(X_t)))
precision recall f1-score support
0 0.78 0.93 0.85 12435
1 0.38 0.15 0.21 3846
avg / total 0.68 0.74 0.70 16281
根据输出结果,该NB模型的精确率为69%,召回率为76%,F1值(精确度和召回率的调和平均值)为68%.
t1=time.time()
svc = svm.SVC()
svc.fit(X, y)
print(classification_report(y_t, svc.predict(X_t)))
print(time.time()-t1)
precision recall f1-score support
0 0.77 0.98 0.86 12435
1 0.45 0.06 0.11 3846
avg / total 0.69 0.76 0.68 16281
874.8090364933014
MLPClassifier是一个监督学习算法。
MLP又名多层感知机,也叫人工神经网络(ANN,Artificial Neural Network),除了输入输出层,它中间可以有多个隐藏层,如果没有隐藏层即可解决线性可划分的数据问题。最简单的MLP模型只包含一个隐藏层,即三层的结构。多层感知机的层与层之间是全连接的(全连接的意思就是:上一层的任何一个神经元与下一层的所有神经元都有连接)。多层感知机最底层是输入层,中间是隐藏层,最后是输出层。
优点
(1)自学习功能对于预测有特别重要的意义。
(2)具有联想存储功能。用人工神经网络的反馈网络就可以实现这种联想。
(3)具有高速寻找优化解的能力。寻找一个复杂问题的优化解,往往需要很大的计算量,利用一个针对某问题而设计的反馈型人工神经网络,发挥计算机的高速运算能力,可能很快找到优化解。
缺点
(1)没能力解释推理过程和推理依据。
(2)当数据不充分的时候,神经网络就无法进行工作。
(3)把一切问题的特征都变为数字,把一切推理都变为数值计算,丢失信息。
参数说明
hidden_layer_sizes :隐藏层的神经元个数。
alpha :float,可选的,默认0.0001,正则化项参数
learning_rate_int:double,可选,默认0.001,初始学习率,控制更新权重的补偿,只有当solver=’sgd’ 或’adam’时使用。
#搜寻该数据集下神经网络的最佳参数
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import GridSearchCV
param = {'hidden_layer_sizes':[10,20,30,40,50,60,70,80,90,100],'learning_rate_init':[0.01,0.001],'alpha':[0.0001,0.001,0.01,0.1,1,10]}
grid = GridSearchCV(MLPClassifier(),param_grid=param,cv=5,scoring='roc_auc')
grid.fit(X,y)
print(grid.best_params_)
{'alpha': 0.001, 'hidden_layer_sizes': 90, 'learning_rate_init': 0.01}
#训练神经网络模型
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import GridSearchCV
mlp = MLPClassifier(hidden_layer_sizes=90,learning_rate_init=0.01,alpha=0.001)
mlp.fit(X,y)
print(classification_report(y_t, mlp.predict(X_t)))
precision recall f1-score support
0 0.83 0.97 0.90 12435
1 0.81 0.36 0.49 3846
accuracy 0.83 16281
macro avg 0.82 0.67 0.70 16281
weighted avg 0.82 0.83 0.80 16281
pytorch写的网络结构,ROC曲线用的是上面的结果。
import torchvision as tv
import torchvision.transforms as transforms
import torch
import torch.nn as nn
import torch.utils.data as Data
train = np.array(X)
train_lab = np.array(y)
test = np.array(X_t)
test_lab = np.array(y_t)
train = torch.FloatTensor(train)
test = torch.FloatTensor(test)
train_lab = torch.LongTensor(train_lab)
test_lab = torch.LongTensor(test_lab)
print(train.shape,train_lab.shape)
print(test.shape,test_lab.shape)
train_dataset = Data.TensorDataset(train,train_lab)
#将dataset放入DataLoader中
train_loader = Data.DataLoader(
dataset=train_dataset,
batch_size = 4,#设置batch size
shuffle=True,#打乱数据
num_workers=2#多线程读取数据
)
torch.Size([32561, 109]) torch.Size([32561])
torch.Size([16281, 109]) torch.Size([16281])
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self,in_channel,h_channel,out_channel):
super(Net, self).__init__()
self.fc1 = nn.Linear(in_channel, h_channel)
self.fc2 = nn.Linear(h_channel, out_channel)
def forward(self, x):
#x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
#x = F.max_pool2d(F.relu(self.conv2(x)), 2)
#x = x.view(x.size()[0], -1)
x = F.relu(self.fc1(x))
x = self.fc2(x)
#x = F.softmax(self.fc3(x))
#x = F.log_softmax(self.fc3(x),1)
return x
net = Net(109,90,2)
print(net)
Net(
(fc1): Linear(in_features=109, out_features=90, bias=True)
(fc2): Linear(in_features=90, out_features=2, bias=True)
)
from torch import optim
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = optim.Adam(net.parameters(), lr=0.01, betas=(0.9,0.999))
for epoch in range(10):
for ii,(data,label) in enumerate(train_loader):
y_pred = net(data)
loss = criterion(y_pred,label)
optimizer.zero_grad()#清除上次迭代的更新梯度
loss.backward()#反向传播
optimizer.step()#更新权重
with torch.no_grad():
test_p = net(test)
test_p1 = torch.max(F.softmax(test_p,dim=1), 1)[1]
test_p2 = test_p1.data.numpy().squeeze()
print(classification_report(test_lab.detach().numpy(), test_p2))
precision recall f1-score support
0 0.77 1.00 0.87 12435
1 1.00 0.01 0.02 3846
avg / total 0.82 0.77 0.67 16281
ROC的全称是“受试者工作特征”(Receiver Operating Characteristic)曲线。在机器学习领域,常被用来评判分类、检测结果的好坏。
“混淆矩阵”:
对于二分类问题,可将样本根据其真实类别与学习器预测类别的组合划分为TP(true positive)、FP(false positive)、TN(true negative)、FN(false negative)四种情况,TP+FP+TN+FN=样本总数。
“ROC 曲线”思路:
根据学习器的预测结果对样例进行排序,按此顺序逐个把样本作为正例进行预测,每次计算出两个重要量的值(TPR、FPR),分别以它们为横、纵坐标作图。
AUC (Area under Curve):
ROC曲线下的面积,介于0.1和1之间,作为数值可以直观的评价分类器的好坏,值越大越好。
plt.figure(figsize=(10,8))
fpr_rf,tpr_rf,thresholds=roc_curve(y_t, [x[1] for x in rf.predict_proba(X_t)])
fpr_lr,tpr_lr,thresholds=roc_curve(y_t, [x[1] for x in lr.predict_proba(X_t)])
fpr_lr_cv1,tpr_lr_cv1,thresholds=roc_curve(y_t, [x[1] for x in lr_cv1.predict_proba(X_t)])
fpr_dt,tpr_dt,thresholds=roc_curve(y_t, [x[1] for x in dt.predict_proba(X_t)])
fpr_nb,tpr_nb,thresholds=roc_curve(y_t, [x[1] for x in nb.predict_proba(X_t)])
fpr_gbdt,tpr_gbdt,thresholds=roc_curve(y_t, [x[1] for x in gbdt.predict_proba(X_t)])
fpr_knn,tpr_knn,thresholds=roc_curve(y_t, [x[1] for x in knn.predict_proba(X_t)])
fpr_ada,tpr_ada,thresholds=roc_curve(y_t, [x[1] for x in ada.predict_proba(X_t)])
fpr_mlp,tpr_mlp,thresholds=roc_curve(y_t, [x[1] for x in mlp.predict_proba(X_t)])
# fpr_svm,tpr_svm,thresholds=roc_curve(y_t, y_t_pred)
# roc_auc_rf=auc(fpr_rf,tpr_rf)
plt.title('ROC curve')
plt.plot(fpr_lr,tpr_lr)
plt.plot(fpr_lr_cv1,tpr_lr_cv1)
plt.plot(fpr_dt,tpr_dt)
plt.plot(fpr_rf,tpr_rf)
plt.plot(fpr_nb,tpr_nb)
plt.plot(fpr_gbdt,tpr_gbdt)
plt.plot(fpr_knn,tpr_knn)
plt.plot(fpr_ada,tpr_ada)
plt.plot(fpr_mlp,tpr_mlp)
# plt.plot(fpr_svm,tpr_svm)
plt.legend(labels=['LogisticRegression','LogisticRegression_CV_L1','DecisionTree','RandomForest','NaiveBayes','GBDT','KNN','AdaBoost','MLP'])
由图可知,GBDT和Adaboost表现最好,其次是logisticsRegression,决策树、随机森林和神经网络相近,KNN模型表现不佳。