1. sklearn 实现决策树
1.1 语法与参数
class sklearn.tree.DecisionTreeClassifier(criterion='gini', splitter='best', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort='deprecated', ccp_alpha=0.0)
参数说明:
- criterion: 不纯度计算指标 {“gini”, “entropy”},
- splitter: {“best”, “random”}, default=”best” 节点切分方法,默认是选择最优特征进行切分.
- max_depth: 树能达到的最大深度,防止模型太深,控制模型复杂度
- min_samples_split: int or float, default=2 抑制模型分裂
一个节点想要向下进行切分,当前样本个数必须要大于这个参数值.
例如设置为10,当这个节点中如果有9个样本,则这个节点只能作为叶节点.
- min_samples_leaf: int or float, default=1 抑制模型分裂
最小叶节点样本个数,如果上层节点分裂之后,分裂出来后的叶节点当中样本个数不足这儿参数值,则本次分裂不能进行.
- max_features : int, float or {“auto”, “sqrt”, “log2”}, default=None
最大特征,在寻找最优分裂点的时候,要考虑的特征个数
If int, 考虑特征个数,举例有100个特征, 填写20,就是每次分裂的时候,只计算20个特征.
If float, 百分比 0-1的浮点数. 100个特征, 填写0.3,那就是计算30个特征.
If “auto”, then max_features=sqrt(n_features). 根号个特征.100个特征就是计算10个特征
If “sqrt”, then max_features=sqrt(n_features). 同上
If “log2”, then max_features=log2(n_features). 计算log2个特征,64个特征,6个.
If None, then max_features=n_features. 所有特征
- random_state: 随机数种子
- max_leaf_nodes: 抑制模型分裂,最大叶节点个数,最多能够分裂出多少个叶节点.
- min_impurity_decrease: 分裂中的最小不纯度下降的值.
如果一次分裂过程中,Gain的下降不能超过这个值,那么就不能进行本次分裂.
- class_weight: 类别权重设置,(类别不均衡数据集的问题)
属性:
- classes_ : 分类标签,没用
- feature_importances_: 特征重要性.
方法:
- get_depth(self) : 树的深度
- get_n_leaves(self): 叶节点的个数
2. 泰坦尼克数据集应用决策树
导包 --> 导入数据 --> 数据探索 --> 数据预处理 --> 切分X,y --> 构建模型 --> 评价
2.1 读入数据
import pandas as pd
import numpy as np
import matplotlib.pylab as plt
train = pd.read_csv('titanic/train.csv')
test = pd.read_csv('titanic/test.csv')
answer = pd.read_csv('titanic/gender_submission.csv')
- PassengerId: 乘客ID,没用
- Pclass: 票等级
- Name: 姓名,没用
- Sex: 性别
- Age: 年龄
- SibSp: 乘客同在船上的兄弟姐妹/配偶的个数(整数值)
- Parch:乘客同在船上的乘客父母/孩子的个数(整数值)
- Ticket:票号(字符串) 没用
- Fare:乘客所持票的价格(浮点数,0-500不等)
- Cabin:乘客所在船舱(有缺失)
-
Embark:乘客登船港口:S、C、Q(有缺失)
2.2 数据探索
# 各个客舱人数
train['Pclass'].value_counts()
# 各类票价均值
train.groupby(by = 'Pclass').mean()['Fare']
# 性别比例
train['Sex'].value_counts()
plt.style.use('seaborn') # 改变绘图风格
# Series里面有封装自带的画图功能
train.Sex.value_counts().plot(kind='barh')
# 男女获救比例
train.groupby('Sex').mean()
# 年龄分布
train['Age'].plot(kind = 'hist')
# 将年龄离散化然后看获救比例(10岁一级)
age_cut = pd.cut(train['Age'],bins = [0,10,20,30,40,50,60,70,80])
train['age_cut'] = age_cut
# 存活下来的Survived标签都是1,所以直接分类累加聚合就是比例,0累加还是0
print("平均生存率: {:.3f}\n".format(train['Survived'].mean()))
print("各仓位等级存活率:\n{}\n".format(train.groupby(by = 'Pclass')['Survived'].mean()))
print("各年龄段生存率如下: \n{}".format(train.groupby(by = 'age_cut').mean()['Survived']))
# 构造函数,查看所有特征与生存率
def survive(feature):
#return train.groupby(by=feature).mean()['Survived']
Survived = train.groupby(by = feature)['Survived'].mean()
print(feature + "存活率:\n{}".format(Survived))
Survived.plot(kind = 'pie')
plt.show()
print('-'*40)
for i in ['Pclass','Sex','SibSp','Parch','Embarked']:
survive(i)
2.3 数据预处理
删除无效特征
填充空值
特征编码
2.3.1 删除无效特征
# inplace = True 直接在原表改,省去赋值
train.drop(columns=['PassengerId','Name','Cabin','Ticket'],inplace=True)
train.pop('age_cut') # 直接删
test.drop(columns=['PassengerId','Name','Cabin','Ticket'],inplace=True)
2.3.2 填充空值
train.info()
# 将年龄空值填充中位数
train.Age.fillna(train.Age.median(),inplace=True)
# 登录港口空值填充众数
train.Embarked.fillna('S',inplace=True)
# 测试集填充,测试集也用训练集数据填充,参考KNN归一化
test.Age.fillna(28,inplace=True)
test.Embarked.fillna('S',inplace=True)
2.3.3 特征编码
# 对性别进行编码 0:男 1:女
train['Sex'] = (train.Sex == 'female').astype('int')
# 对仓位进行编码
train['Embarked'] = train.Embarked.map({'S':0,'C':1,'Q':2})
test['Sex'] = (test.Sex == 'female').astype('int')
# 对仓位进行编码
test['Embarked'] = test.Embarked.map({'S':0,'C':1,'Q':2})
test.Fare.fillna(train.Fare.mean(),inplace=True)
2.4 拆分X,y
train_y = train.pop('Survived')
train_X = train.copy() #拷贝
test_X = test.copy()# 提取X
test_y = submission.Survived #提取y
2.5 决策树建模
导包 --> 实例化 --> fit --> 评估
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier(random_state = 666)
dtc.fit(train_X,train_y)
dtc.score(train_X,train_y) # 训练精度 0.9797
dtc.score(test_X,test_y) # 测试精度 0.8086 过拟合了
3. 过拟合
上边的结果明显过拟合:训练集的准确率,远远高于测试集的准确率
拟合: 模型对数据的学习过程,模型去适配数据
噪声: 代表了我们数据中存在的一些和普适性的规律不一样的信息.
数据信息两类(规律, 噪声):举例: 大部分年龄大的男性都死亡了,但是有几个活下来了,这几个就不符合常规的规律,就是数据集中的异常数据,或者说是噪声.
如果模型的学习能力太强,就不但会学习到数据中的规律,还会学习到数据中的噪声.
这个时候模型表现: 对训练集预测非常准确,但是对测试集表现就会很差,这种情况下,我们就称为模型过拟合了.
欠拟合:模型的学习能力太差了,连训练集中的规律都没学会.
表现: 无论是训练集还是测试集,它的表现都很差.
解决方案:网格搜索 调参(降低训练精度,提高测试精度)
3.1 语法
GridSearchCV: 交叉验证网格搜索
class sklearn.model_selection.GridSearchCV(estimator, param_grid, scoring=None, n_jobs=None, iid='deprecated', refit=True, cv=None, verbose=0, pre_dispatch='2*n_jobs', error_score=nan, return_train_score=False)
参数说明:
- estimator: 模型
- param_grid : 参数字典
- scoring : 模型评估指标,默认准确率
- n_jobs: CPU个数
- cv: 交叉验证折数
- verbose: 日志
属性:
- best_estimator_ : estimator 在网格搜索过程中,找到的最好的分类器.
- best_score_: 最好的分类器,对应的交叉验证分数
- best_params_: 最好的分类器对应的参数
3.2 对泰坦尼克模型使用
from sklearn.model_selection import GridSearchCV
# 实例化
# 参数分成两部分: 1.你要调参的,就不用设置 2.你不打算调的,就可以设置
dtc = DecisionTreeClassifier( random_state=666)
# max_depth=None, 非常重要
# min_samples_split=2, min_samples_leaf=1, 最小叶节点分裂个数,可选
# max_leaf_nodes=None, 有一定作用
# min_impurity_decrease=0.0 有一定作用,范围不好确定
决策树参数说明:
- criterion: 不纯度计算指标 {“gini”, “entropy”},
- splitter: {“best”, “random”}, default=”best” 节点切分方法,默认是选择最优特征进行切分.
- max_depth: 树能达到的最大深度,防止模型太深,控制模型复杂度
- min_samples_split: int or float, default=2 抑制模型分裂
一个节点想要向下进行切分,当前样本个数必须要大于这个参数值.
例如设置为10,当这个节点中如果有9个样本,则这个节点只能作为叶节点.
- min_samples_leaf: int or float, default=1 抑制模型分裂
最小叶节点样本个数,如果上层节点分裂之后,分裂出来后的叶节点当中样本个数不足这儿参数值,则本次分裂不能进行.
- max_features : int, float or {“auto”, “sqrt”, “log2”}, default=None
最大特征,在寻找最优分裂点的时候,要考虑的特征个数
If int, 考虑特征个数,举例有100个特征, 填写20,就是每次分裂的时候,只计算20个特征.
If float, 百分比 0-1的浮点数. 100个特征, 填写0.3,那就是计算30个特征.
If “auto”, then max_features=sqrt(n_features). 根号个特征.100个特征就是计算10个特征
If “sqrt”, then max_features=sqrt(n_features). 同上
If “log2”, then max_features=log2(n_features). 计算log2个特征,64个特征,6个.
If None, then max_features=n_features. 所有特征
- random_state: 随机数种子
- max_leaf_nodes: 抑制模型分裂,最大叶节点个数,最多能够分裂出多少个叶节点.
- min_impurity_decrease: 分裂中的最小不纯度下降的值.
如果一次分裂过程中,Gain的下降不能超过这个值,那么就不能进行本次分裂.
- class_weight: 类别权重设置,(类别不均衡数据集的问题)
参数调优:
# 设置调参字典
# 设置一个调参字典
d = {"ccp_alpha":np.arange(0.0,0.8,0.1),
'criterion':["gini",'entropy'],
"max_depth":[2,3,4,5,6,7],
"min_samples_split":range(2,10), #最小叶节点样本个数
"max_leaf_nodes":range(4,20), #最大叶节点个数
#"min_impurity_decrease":np.arange(0.01,0.11,0.01) #分裂中的最小不纯度下降的值
}
# 实例化网格搜索
grid = GridSearchCV(dtc,param_grid =d ,n_jobs=-1,cv=5, verbose=2 )
# 训练
grid.fit(X,y)
# 搜索到的最优交叉验证分数 : 0.8238026489234824
grid.best_score_
# 搜索到的参数
grid.best_params_
# 结果
{'criterion': 'gini',
'max_depth': 6,
'max_leaf_nodes': 15,
'min_samples_split': 2}
# 分类器
best_model = grid.best_estimator_
# 看一下搜索到的这个结果,在测试集上面表现如何?
best_model.score(X_test,y_test)
# feature_importances_: 特征重要性.
best_model.feature_importances_
#结果
array([0.1935308 , 0.56999101, 0.09826444, 0.04071957, 0.00751746,
0.08082954, 0.00914718])
#相对应的特征
X.columns
#输出:
Index(['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'], dtype='object')