传统机器学习(六)集成算法(1)—随机森林算法及案例详解

这篇具有很好参考价值的文章主要介绍了传统机器学习(六)集成算法(1)—随机森林算法及案例详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

传统机器学习(六)集成算法(1)—随机森林算法及案例详解

1、概述

集成学习(Ensemble Learning)就是通过某种策略将多个模型集成起来,通过群体决策来提高决策准确率。

集成学习首要的问题是选择什么样的学习器以及如何集成多个基学习器,即集成策略。

1.1 集成学习的分类

一个有效的集成除了要让各个基学习器的学习效果好之外,还需要各个基学习器的差异尽可能大(差异性:各个基学习器的预测结果不完全相同)。集成学习往往和方差大的模型组合时效果显著。

  • Bagging类方法中基学习器之间不存在强依赖关系,可并行执行。

  • Boosting类方法中基学习器之间存在强依赖关系,必须串行执行。

  • Stacking:聚合多个分类或回归模型(可以分阶段来做)。

Boosting和Bagging通常都是使用同一种基学习器(base learner),因此我们一般称之为同质集成方法。

Stacking通常都是基于多个不同的基学习器做的集成,因此我们称之为异质集成方法。

1.1.1 Bagging

Bagging类方法是通过随机构造训练样本、随机选择特征等方法来提高每个基模型的独立性。由于训练数据的不同,获得的学习器会存在差异性,但是若采样的每个子集都完全不同,则每个基学习器都只能训练一小部分数据,无法进行有效的学习。因此考虑使用相互交叠的采样子集。代表性方法有Bagging和随机森林等。

  • Bagging(Bootstrap Aggregating)是通过不同模型的训练数据集的独立性来提高不同模型之间的独立性。我们在原始训练集上进行有放回的随机采样。可以采样T个含有m个样本的数据集并行训练得到T个模型,然后将这些基学习模型进行结合。对于基学习器的集成方法,Bagging通常对分类任务使用简单投票法,对回归任务使用平均法。若预测的结果中有含有相同票数的两个类,可以使用随机选择或者考察学习器投票的置信度来确定。

  • 随机森林(Random Forest)是在Bagging的基础上再引入了随机特征,进一步提高每个基模型之间的独立性。在随机森林中,每个基模型都是一棵决策树,与传统决策树不同的是,在RF中,对每个基决策树的每个节点,先从该节点的属性集合中随机选择一个包含k个属性的子集,然后从这个子集中选择一个最优属性由于划分,而传统的决策树是直接在当前节点的属性集合中选择一个最优属性来划分集合。

1.1.2 Boosting

Boosting类方法是按照一定的顺序来先后训练不同的基模型,每个模型都针对先前模型的错误进行专门训练。根据先前模型的结果,来调整训练样本的权重,从而增加不同基模型之间的差异性。Boosting的过程很类似于人类学习的过程,我们学习新知识的过程往往是迭代式的。第一遍学习的时候,我们会记住一部分知识,但往往也会犯一些错误,对于这些错误,我们的印象会很深。第二遍学习的时候,就会针对犯过错误的知识加强学习,以减少类似的错误发生。不断循环往复,直到犯错误的次数减少到很低的程度。

Boosting 类方法是一种非常强大的集成方法,只要基模型的准确率比随机猜测高,就可以通过集成方法来显著地提高集成模型的准确率。Boosting类方法的代表性方法有:AdaBoost,GBDT等。

1.1.3 Stacking

对于一个问题来说,我们可以采用不同类型的学习器来解决学习的问题,这些学习器通常能够学习到问题的一部分,但不能学习到问题的整个空间。Stacking的做法是首先构建多个不同类型的一级学习器,并使用他们来得到一级预测结果,然后基于这些一级预测结果,构建一个二级学习器,来得到最终的预测结果

Stacking的动机可以描述为:如果某个一级学习器错误地学习了特征空间的某个区域,那么二级学习器通过结合其他一级学习器的学习行为,可以适当纠正这种错误。

1.2 随机森林原理

随机森林(Random Fores)是将决策树进行简单bagging的一种集成算法。它经过多次随机抽取样本训练多棵决策树,用多棵决策树集成决策。由于它拥有多棵树,且每棵树是随机的,所以称为随机森林。

传统机器学习(六)集成算法(1)—随机森林算法及案例详解

1.2.1 模型表达式

y = 1 k [ t 1 ∗ p r o b ( x ) + [ t 2 ∗ p r o b ( x ) + . . . + [ t k ∗ p r o b ( x ) ] y = \frac{1}{k}[t_{1}*prob(x) + [t_{2}*prob(x) + ...+ [t_{k}*prob(x)] y=k1[t1prob(x)+[t2prob(x)+...+[tkprob(x)]

其中:                                                                                                       
 t(i)          :  决策树                                                                               
 t(i).prob(x)  :  第i棵树对x的预测,输出为各个类别的预测概率(行向量)   
 k             :  森林规模数  
 
即模型为多棵决策树组成,最后的预测概率为各棵树的概率预测均值。概率得分最大的一类,即为预测类别

1.2.2 模型训练

模型训练焦点在于如何训练出多棵不同的弱树。可以通过如下方式实现:每次随机选择部分样本,和部分变量训练一棵弱树,要使树是弱树,可将树的深度设置得较浅,总之,让树的预测不是很精确。

-- 1、训练流程如下

放回式抽取n个样本,每个样本抽到的次数,作为样本权重。
用加了权重的样本训练弱决策树(弱决策树即:最大分割特征:2),一直训练K棵树为止。
简单地说,就是生成k棵树,每棵树用的样本随机抽取。最后k棵树组合在一起就是森林。

-- 2、训练参数
 (1) 变量最大个数m,m一般远小于总变量个数M,例如 根号M  
 (2) 森林规模(树的棵数k)           

1.2.3 随机森林的泛化能力

袋外错误率

随机森林可用袋外错误率obb(out-of-bag) error评估泛化能力.

袋外错误率的思路是通过未参与训练样本(袋外样本)的准确率来检验森林的泛化能力,由于每个样本都只被部分树用于训练,可以只用森林中该样本不参与的子森林来预测样本,袋外错误率就是用该方法对所有样本进行预测的准确率。

袋外类别预测

袋外的类别预测,是指用该样本未参与训练的树对其进行概率预测,汇总所有树对该样本的预测结果(概率之和归一化后的值),最后哪个类别的概率大,就认为袋外预测类别是哪个。

传统机器学习(六)集成算法(1)—随机森林算法及案例详解

所有样本的袋外预测准确率,即为袋外评分(obb_socre),袋外错误率 则为:obb_error = 1 - obb_socre

1.2.4 特征权重

特征权重是每个特征对随机森林贡献度的占比,特征权重越高,说明特征对森林的构成越重要,对决策结果影响越大。

只要将森林的每棵树的评分求均值,并作归一化,就是森林的特征评分。
s = n o r m ( 1 k ∑ i = 1 n s i ) s = norm(\frac{1}{k}\sum\limits_{i=1}^ns_{i}) s=norm(k1i=1nsi)
其中,s(i)为向量,是第 i 棵树对各个特征的评分。

1.2.5 随机森林的优缺点

1、随机森林算法优点

  • 由于采用了集成算法,本身精度比大多数单个算法要好,所以准确性高

  • 在测试集上表现良好,由于两个随机性的引入,使得随机森林不容易陷入过拟合(样本随机,特征随机)

  • 在工业上,由于两个随机性的引入,使得随机森林具有一定的抗噪声能力,对比其他算法具有一定优势

  • 由于树的组合,使得随机森林可以处理非线性数据,本身属于非线性分类(拟合)模型

  • 它能够处理很高维度(feature很多)的数据,并且不用做特征选择,对数据集的适应能力强:既能处理离散型数据,也能处理连续型数据,数据集无需规范化

  • 训练速度快,可以运用在大规模数据集上

  • 可以处理缺省值(单独作为一类),不用额外处理

  • 由于有袋外数据(OOB),可以在模型生成过程中取得真实误差的无偏估计,且不损失训练数据量

  • 在训练过程中,能够检测到feature间的互相影响,且可以得出feature的重要性,具有一定参考意义

  • 由于每棵树可以独立、同时生成,容易做成并行化方法

  • 由于实现简单、精度高、抗过拟合能力强,当面对非线性数据时,适于作为基准模型

2、随机森林算法缺点

  • 当随机森林中的决策树个数很多时,训练时需要的空间和时间会比较大

  • 随机森林中还有许多不好解释的地方,有点算是黑盒模型

  • 在某些噪音比较大的样本集上,RF的模型容易陷入过拟合

2、手动实现随机森林

from sklearn.datasets import load_iris
import numpy as np
from sklearn.base import clone
from sklearn.tree import DecisionTreeClassifier
np.random.seed(888)

# ==================== 加载数据=====================================================
iris = load_iris()
X = iris.data
Y = iris.target
n_samples = X.shape[0]            # 样本个数
n_samples_bootstrap = X.shape[0]  # 抽样个数
c_n = np.unique(Y).shape[0]       # 类别数
tree_num = 100                    # 森林决策树个数
trees = []                        # 初始化树列表
p_oob = np.zeros((n_samples, c_n))     # oob投票结果
random_state = np.random.mtrand._rand  # 随机状态
max_features = 2                       # 每棵树分割最大特征数(至少有一个分割点,若一个都没有,则无视该条件)
# 建立树模板
base_estimator = DecisionTreeClassifier()
base_estimator.set_params(**{'criterion': 'gini',
                             'min_samples_split': 2,
                             'min_samples_leaf': 1,
                             'min_weight_fraction_leaf': 0.0,
                             'max_features': max_features,
                             'max_leaf_nodes': None,
                             'min_impurity_decrease': 0.0,
                             'random_state': None,
                             'ccp_alpha': 0.0
                             })

# 逐树训练
random_state_list = [random_state.randint(np.iinfo(np.int32).max) for i in range(tree_num)]  # 初始化树随机状态
for i in range(tree_num):
    sample_indices = np.random.RandomState(random_state_list[i]).randint(0, n_samples, n_samples_bootstrap)  # 抽样
    sample_counts = np.bincount(sample_indices, minlength=n_samples)  # 抽样分布
    curr_sample_weight = np.ones((n_samples,), dtype=np.float64) * sample_counts  # 样本权重
    cur_tree = clone(base_estimator)  # 初始化树
    cur_tree.set_params(**{'random_state': random_state_list[i]})  # 设置当前树随机状态
    cur_tree.fit(X, Y, sample_weight=curr_sample_weight, check_input=False)  # 训练树
    trees.append(cur_tree)  # 将本次训练好的树, 添加到树列表

    # 计算obb得分
    un_select = ~ np.isin(range(n_samples), sample_indices)  # 未选中的数据
    cur_p_oob = cur_tree.predict_proba(X[un_select, :])  # 将当前未选中数据的预测结果
    p_oob[un_select, :] += cur_p_oob  # 投票到汇总结果

# =============== 模型指标统计 ===================================
oob_score = np.mean(Y == np.argmax(p_oob, axis=1), axis=0)  # obb样本正确率即为obb得分

# 计算特征得分
all_importances = [getattr(tree, 'feature_importances_') for tree in trees if tree.tree_.node_count > 1]  # 获取每棵树中各特征评估
all_importances = np.mean(all_importances, axis=0, dtype=np.float64)  # 求均值
feature_importances = all_importances / np.sum(all_importances)       # 归一化

# =============== 模型预测 ===================================
sim_p = np.zeros((X.shape[0], c_n), dtype=np.float64)  # 初始化投票得分
for i in range(len(trees)):  # 逐树投票
    sim_p += trees[i].predict_proba(X) / len(trees)    # 投票
sim_c = np.argmax(sim_p, axis=1)  # 得分最高者作为投票结果

# =================打印结果==========================
print("\n----前5条预测结果:----")
print(sim_p[0:5])  # 打印结果
print("\n----袋外准确率oob_score:----")
print(oob_score)  # 打印oob得分
print("\n----特征得分:----")
print(feature_importances)
----5条预测结果:----
[[1.   0.   0.  ]
 [0.99 0.01 0.  ]
 [1.   0.   0.  ]
 [1.   0.   0.  ]
 [1.   0.   0.  ]]

----袋外准确率oob_score:----
0.9533333333333334

----特征得分:----
[0.08186032 0.02758341 0.44209899 0.44845728]

3、sklearn中的bagging和随机森林

3.1 bagging

import numpy as np
import os
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
import warnings
warnings.filterwarnings('ignore')
np.random.seed(42)


'''
Bagging策略:
首先对训练数据集进行多次采样,保证每次得到的采样数据都是不同的;
分别训练多个模型,例如树模型;
预测时需得到所有模型结果再进行集成。
'''

from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split

X, y = make_moons(n_samples=500,noise=0.25,random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

plt.plot(X[:,0][y==0],X[:,1][y==0],'yo',alpha = 0.6)
plt.plot(X[:,0][y==0],X[:,1][y==1],'bs',alpha = 0.6)

传统机器学习(六)集成算法(1)—随机森林算法及案例详解

'''
1、传统决策树
'''
from sklearn import tree
from sklearn.metrics import accuracy_score

tree_clf = tree.DecisionTreeClassifier(random_state=42)
tree_clf.fit(X_train,y_train)

y_pred = tree_clf.predict(X_test)
print('传统决策树精确率为:',accuracy_score(y_test,y_pred))
# 传统决策树精确率为: 0.936
'''
2、Bagging
'''
from sklearn.ensemble import BaggingClassifier


bag_clf = BaggingClassifier(
    tree.DecisionTreeClassifier(), # 拟合数据集的随机子集的基学习器
    n_estimators=500,              # 基学习器数目
    max_samples=100,               # 每个学习器抽样的最大样本数
    bootstrap=True,                # 样本是否放回
    n_jobs=-1,
    oob_score=True,
    random_state=42
)
bag_clf.fit(X_train,y_train)

y_pred = bag_clf.predict(X_test)
print('Bagging精确率为:',accuracy_score(y_test,y_pred))
# Bagging精确率为: 0.952
'''
3、决策边界对比
'''
from matplotlib.colors import ListedColormap

def plot_decision_boundary(clf,X,y,axes=[-1.5,2.5,-1,1.5],alpha=0.5,contour =True):
    x1s=np.linspace(axes[0],axes[1],100)
    x2s=np.linspace(axes[2],axes[3],100)
    x1,x2 = np.meshgrid(x1s,x2s)
    X_new = np.c_[x1.ravel(),x2.ravel()]
    y_pred = clf.predict(X_new).reshape(x1.shape)
    custom_cmap = ListedColormap(['#fafab0','#9898ff','#a0faa0'])
    plt.contourf(x1,x2,y_pred,cmap = custom_cmap,alpha=0.3)
    if contour:
        custom_cmap2 = ListedColormap(['#7d7d58','#4c4c7f','#507d50'])
        plt.contour(x1,x2,y_pred,cmap = custom_cmap2,alpha=0.8)
    plt.plot(X[:,0][y==0],X[:,1][y==0],'yo',alpha = 0.6)
    plt.plot(X[:,0][y==0],X[:,1][y==1],'bs',alpha = 0.6)
    plt.axis(axes)
    plt.xlabel('x1')
    plt.xlabel('x2')
    
    
plt.figure(figsize = (12,5))

plt.subplot(121)
plot_decision_boundary(tree_clf,X,y)
plt.title('Decision Tree')

plt.subplot(122)
plot_decision_boundary(bag_clf,X,y)
plt.title('Decision Tree With Bagging')
plt.show()

传统机器学习(六)集成算法(1)—随机森林算法及案例详解

# 随机森林可用袋外错误率obb(out-of-bag) error评估泛化能力.
# obb_error = 1 - obb_socre
obb_error = 1 - bag_clf.oob_score_
obb_error
# 0.06399999999999995

3.2 随机森林

"""
sklearn的随机森林Demo
"""
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import numpy as np

np.random.seed(55)
# ==================== 加载数据 =================
iris = load_iris()
X   = iris.data
y   = iris.target

# ========================= 模型训练 =============
clf = RandomForestClassifier(
    n_jobs=1,
    oob_score=True,
    max_features=2,
    n_estimators=100
)
clf.fit(X, y)

# =============================== 模型预测 ========
pred_prob = clf.predict_proba(X)
pred_c    = clf.predict(X)
preds     = iris.target_names[pred_c]

#=================打印结果==========================
print("\n----前5条预测结果:----")
print(pred_prob[0:5])
print("\n----袋外准确率oob_score:----")
print(clf.oob_score_)
print("\n----特征得分:----")
print(clf.feature_importances_)
----前5条预测结果:----
[[1. 0. 0.]
 [1. 0. 0.]
 [1. 0. 0.]
 [1. 0. 0.]
 [1. 0. 0.]]

----袋外准确率oob_score:----
0.9533333333333334

----特征得分:----
[0.09910252 0.0317905  0.48356935 0.38553764]

4、随机森林案例

4.1 随机森林预测宽带客户离网

数据集链接:https://pan.baidu.com/s/1vmjldkWZtQWlFopWlFLX9w 提取码:ad1h

集成学习与神经网络一样,都属于解释性较差的黑盒模型,所以我们无需过分探究数据集中每个变量的具体含义,只需关注最后一个变量broadband即可,争取通过如年龄,使用时长,支付情况以及流量和通话情况等变量对宽带客户是否会续费做出一个较准确的预测。

1、数据探索

import pandas as pd
import numpy as np

df = pd.read_csv('../data/broadband.csv') # 宽带客户数据

# 列名全部换成小写
df.rename(str.lower, axis='columns', inplace=True)

# 只需关注参数,broadband:0-离开,1-留存
df.head()
cust_id gender age tenure channel autopay arpb_3m call_party_cnt day_mou afternoon_mou night_mou avg_call_length broadband
0 63 1 34 27 2 0 203 0 0.0 0.0 0.0 3.04 1
1 64 0 62 58 1 0 360 0 0.0 1910.0 0.0 3.30 1
2 65 1 39 55 3 0 304 0 437.2 200.3 0.0 4.92 0
3 66 1 39 55 3 0 304 0 437.2 182.8 0.0 4.92 0
4 67 1 39 55 3 0 304 0 437.2 214.5 0.0 4.92 0
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1114 entries, 0 to 1113
Data columns (total 13 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   cust_id          1114 non-null   int64  
 1   gender           1114 non-null   int64  
 2   age              1114 non-null   int64  
 3   tenure           1114 non-null   int64  
 4   channel          1114 non-null   int64  
 5   autopay          1114 non-null   int64  
 6   arpb_3m          1114 non-null   int64  
 7   call_party_cnt   1114 non-null   int64  
 8   day_mou          1114 non-null   float64
 9   afternoon_mou    1114 non-null   float64
 10  night_mou        1114 non-null   float64
 11  avg_call_length  1114 non-null   float64
 12  broadband        1114 non-null   int64  
dtypes: float64(4), int64(9)
memory usage: 113.3 KB
from collections import Counter

# 查看broadband分布情况,随机森林擅长处理数据集不平衡
print('broadband',Counter(df['broadband']))
broadband Counter({0: 908, 1: 206})

2、拆分测试集及训练集

# 客户id没有用,故丢弃cust_id这一列
X = df.iloc[:,1:-1]

y = df['broadband']

from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(
    X,y,test_size=0.3,random_state=888
)

3、决策树建模

from sklearn import tree

# 直接使用交叉网格搜索来优化决策树模型
from sklearn.model_selection import GridSearchCV

# 网格搜索的参数:正常决策树建模中的参数 - 评估指标,树的深度,最小拆分的叶子样本数
# 通常来说,十几层的树已经是比较深了
param_grid = {
    'max_depth': [2, 3, 4, 5, 6, 7, 8],
    'min_samples_split': [4, 8, 12, 16, 20, 24, 28]
}

clf_cv = GridSearchCV(
    estimator=tree.DecisionTreeClassifier(),
    param_grid=param_grid,
    scoring='roc_auc',
    cv=5
)

clf_cv.fit(X_train,y_train)
pred_y_test = clf_cv.predict(X_test)

import sklearn.metrics as metrics

print("决策树 AUC:")
fpr_test, tpr_test, th_test = metrics.roc_curve(y_test, pred_y_test)
print('AUC = %.4f' % metrics.auc(fpr_test, tpr_test))
决策树 AUC:
AUC = 0.7763

4、随机森林建模

# 一样是直接使用网格搜索
param_grid = {
    'max_depth':[5, 6, 7, 8],       # 深度:这里是森林中每棵决策树的深度
    'n_estimators':[11,13,15],      # 决策树个数-随机森林特有参数
    'max_features':[0.3,0.4,0.5],   # 每棵决策树使用的变量占比-随机森林特有参数(结合原理)
    'min_samples_split':[4,8,12,16] # 叶子的最小拆分样本量
}

from sklearn.ensemble import RandomForestClassifier

rfc = RandomForestClassifier()
rfc_cv = GridSearchCV(estimator=rfc,
                      param_grid=param_grid,
                      scoring='roc_auc',
                      cv=5)

rfc_cv.fit(X_train, y_train)

# 使用随机森林对测试集进行预测
test_est = rfc_cv.predict(X_test)
print('随机森林 AUC...')
fpr_test, tpr_test, th_test = metrics.roc_curve(test_est, y_test) # 构造 roc 曲线
print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test))
随机森林 AUC...
AUC = 0.8181
# 查看最佳参数,看是否在决策边界上,还需重新设置网格搜索参数
rfc_cv.best_params_
{'max_depth': 7,
 'max_features': 0.3,
 'min_samples_split': 4,
 'n_estimators': 15}
# 调整决策边界,这里只是做示范
param_grid = {
    'max_depth':[7, 8, 10, 12],
    'n_estimators':[11, 13, 15, 17, 19],              # 决策树个数-随机森林特有参数
    'max_features':[0.2,0.3,0.4, 0.5, 0.6, 0.7],      # 每棵决策树使用的变量占比-随机森林特有参数
    'min_samples_split':[2, 3, 4, 8, 12, 16]          # 叶子的最小拆分样本量
}

# 重复上述步骤,可写成函数供快捷调用
rfc_cv = GridSearchCV(estimator=rfc,
                      param_grid=param_grid,
                      scoring='roc_auc',
                      n_jobs=-1,
                      cv=5)

rfc_cv.fit(X_train, y_train)
# 使用随机森林对测试集进行预测
test_est = rfc_cv.predict(X_test)

print('随机森林 AUC...')
fpr_test, tpr_test, th_test = metrics.roc_curve(test_est, y_test) # 构造 roc 曲线
print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test))
# 这里的 auc 只提升了很多
随机森林 AUC...
AUC = 0.8765

4.2 随机森林分析酒店预定取消率影响的因素

数据集下载地址:https://www.kaggle.com/datasets/jessemostipak/hotel-booking-demand

该数据集依据两家酒店为主体,分别是一家度假酒店与一家城市酒店,这两家均位于葡萄牙(Portugal),度假酒店(Resort hotel)在阿尔加维(Algarve),城市酒店(City hotel)则位于葡萄牙首都里斯本(Lisbon),两个酒店在地理位置上跨度较大,数据之间相互干扰的影响较小。

1、数据探索

import pandas as pd
import matplotlib.pyplot as plt

#显示设置
pd.set_option("display.max_columns", None)  #设置显示全部列
plt.rcParams['font.sans-serif']=['SimHei']  #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  #用来正常显示负号


# 加载数据
data_origin = pd.read_csv("../data/hotel_bookings.csv")

#数据备份
data=data_origin.copy()

print(data.shape)
data.head()
hotel is_canceled lead_time arrival_date_year arrival_date_month arrival_date_week_number arrival_date_day_of_month stays_in_weekend_nights stays_in_week_nights adults children babies meal country market_segment distribution_channel is_repeated_guest previous_cancellations previous_bookings_not_canceled reserved_room_type assigned_room_type booking_changes deposit_type agent company days_in_waiting_list customer_type adr required_car_parking_spaces total_of_special_requests reservation_status reservation_status_date
0 Resort Hotel 0 342 2015 July 27 1 0 0 2 0.0 0 BB PRT Direct Direct 0 0 0 C C 3 No Deposit NaN NaN 0 Transient 0.0 0 0 Check-Out 2015-07-01
1 Resort Hotel 0 737 2015 July 27 1 0 0 2 0.0 0 BB PRT Direct Direct 0 0 0 C C 4 No Deposit NaN NaN 0 Transient 0.0 0 0 Check-Out 2015-07-01
2 Resort Hotel 0 7 2015 July 27 1 0 1 1 0.0 0 BB GBR Direct Direct 0 0 0 A C 0 No Deposit NaN NaN 0 Transient 75.0 0 0 Check-Out 2015-07-02
3 Resort Hotel 0 13 2015 July 27 1 0 1 1 0.0 0 BB GBR Corporate Corporate 0 0 0 A A 0 No Deposit 304.0 NaN 0 Transient 75.0 0 0 Check-Out 2015-07-02
4 Resort Hotel 0 14 2015 July 27 1 0 2 2 0.0 0 BB GBR Online TA TA/TO 0 0 0 A A 0 No Deposit 240.0 NaN 0 Transient 98.0 0 1 Check-Out 2015-07-03
# hotel                           酒店类型
# is_canceled                     预订是否取消(0,1)
# lead_time                       提前预订天数
# arrival_date_year               入住年份
# arrival_date_month              入住月份
# arrival_date_week_number        入住周数
# arrival_date_day_of_month       入住日期
# stays_in_weekend_nights         周末过夜数
# stays_in_week_nights            工作日过夜数
# adults                          成人人数
# children                        儿童人数
# babies                          婴儿人数
# meal                            餐食类型(BB早餐,HB午餐,FB晚餐,Undefined/SC无餐)
# country                         客户来源国家
# market_segment                  市场细分
# distribution_channel            订单渠道
# is_repeated_guest               是否是老客户
# previous_cancellations          历史取消预订的次数
# previous_bookings_not_canceled  历史未取消预订的次数
# reserved_room_type              预定房间类型
# assigned_room_type              实际房间类型
# booking_changes                 预定更改次数
# deposit_type                    押金类型(No Deposit,Non Refund,Refundable)
# agent                           预订旅行社ID
# company                         预订公司/实体ID
# days_in_waiting_list            等待天数
# customer_type                   客户类型
# adr                             客房日均价
# required_car_parking_spaces     车位需求数
# total_of_special_requests       特殊需求数
# reservation_status              预订最终状态(Canceled,Check-out,No-show)
# reservation_status_date         预订最终状态更新日期
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 119390 entries, 0 to 119389
Data columns (total 32 columns):
 #   Column                          Non-Null Count   Dtype  
---  ------                          --------------   -----  
 0   hotel                           119390 non-null  object 
 1   is_canceled                     119390 non-null  int64  
 2   lead_time                       119390 non-null  int64  
 3   arrival_date_year               119390 non-null  int64  
 4   arrival_date_month              119390 non-null  object 
 5   arrival_date_week_number        119390 non-null  int64  
 6   arrival_date_day_of_month       119390 non-null  int64  
 7   stays_in_weekend_nights         119390 non-null  int64  
 8   stays_in_week_nights            119390 non-null  int64  
 9   adults                          119390 non-null  int64  
 10  children                        119386 non-null  float64
 11  babies                          119390 non-null  int64  
 12  meal                            119390 non-null  object 
 13  country                         118902 non-null  object 
 14  market_segment                  119390 non-null  object 
 15  distribution_channel            119390 non-null  object 
 16  is_repeated_guest               119390 non-null  int64  
 17  previous_cancellations          119390 non-null  int64  
 18  previous_bookings_not_canceled  119390 non-null  int64  
 19  reserved_room_type              119390 non-null  object 
 20  assigned_room_type              119390 non-null  object 
 21  booking_changes                 119390 non-null  int64  
 22  deposit_type                    119390 non-null  object 
 23  agent                           103050 non-null  float64
 24  company                         6797 non-null    float64
 25  days_in_waiting_list            119390 non-null  int64  
 26  customer_type                   119390 non-null  object 
 27  adr                             119390 non-null  float64
 28  required_car_parking_spaces     119390 non-null  int64  
 29  total_of_special_requests       119390 non-null  int64  
 30  reservation_status              119390 non-null  object 
 31  reservation_status_date         119390 non-null  object 
dtypes: float64(4), int64(16), object(12)
memory usage: 29.1+ MB
data.describe()
is_canceled lead_time arrival_date_year arrival_date_week_number arrival_date_day_of_month stays_in_weekend_nights stays_in_week_nights adults children babies is_repeated_guest previous_cancellations previous_bookings_not_canceled booking_changes agent company days_in_waiting_list adr required_car_parking_spaces total_of_special_requests
count 119390.000000 119390.000000 119390.000000 119390.000000 119390.000000 119390.000000 119390.000000 119390.000000 119386.000000 119390.000000 119390.000000 119390.000000 119390.000000 119390.000000 103050.000000 6797.000000 119390.000000 119390.000000 119390.000000 119390.000000
mean 0.370416 104.011416 2016.156554 27.165173 15.798241 0.927599 2.500302 1.856403 0.103890 0.007949 0.031912 0.087118 0.137097 0.221124 86.693382 189.266735 2.321149 101.831122 0.062518 0.571363
std 0.482918 106.863097 0.707476 13.605138 8.780829 0.998613 1.908286 0.579261 0.398561 0.097436 0.175767 0.844336 1.497437 0.652306 110.774548 131.655015 17.594721 50.535790 0.245291 0.792798
min 0.000000 0.000000 2015.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000 6.000000 0.000000 -6.380000 0.000000 0.000000
25% 0.000000 18.000000 2016.000000 16.000000 8.000000 0.000000 1.000000 2.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 9.000000 62.000000 0.000000 69.290000 0.000000 0.000000
50% 0.000000 69.000000 2016.000000 28.000000 16.000000 1.000000 2.000000 2.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 14.000000 179.000000 0.000000 94.575000 0.000000 0.000000
75% 1.000000 160.000000 2017.000000 38.000000 23.000000 2.000000 3.000000 2.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 229.000000 270.000000 0.000000 126.000000 0.000000 1.000000
max 1.000000 737.000000 2017.000000 53.000000 31.000000 19.000000 50.000000 55.000000 10.000000 10.000000 1.000000 26.000000 72.000000 21.000000 535.000000 543.000000 391.000000 5400.000000 8.000000 5.000000
# 通过观察数据,发现1个异常点:客房日均价(adr)不为负,应剔除adr为负的异常值,
# adr最大值为5400,显然远远大于均值+3倍标准差。
# 用箱形图看下异常值:
import seaborn as sns


plt.figure(figsize=(12, 1))
sns.boxplot(x=list(data["adr"]))
plt.show()


传统机器学习(六)集成算法(1)—随机森林算法及案例详解

#查看缺失值
missing=data.isnull().sum()
missing[missing != 0]
children         4
country        488
agent        16340
company     112593
dtype: int64

2、数据清洗

1)缺失值填充

① country缺失的,无法补充,缺失值用unknown进行区分 ;
② children缺失的,可理解为没有儿童入住,故订单登记中未填,缺失值用0填充;
③ agent缺失的,可理解为非旅行社预订,故订单登记中未填,缺失值用0填充;
④ company缺失的,可理解为非公司预订,故订单登记中未填,缺失值用0填充;

data_fill = data.fillna(
    {
        "country":"unknown",
        "children":0,
        "agent":0,
        "company":0
    }
)

missing=data_fill.isnull().sum()
missing[missing != 0]
Series([], dtype: int64)

2)异常值处理

(1)adults与children入住人数之和为0;
(2)客房日均价为负,客房日均价高于1000元;
(3)meal中undefined与sc均表示无餐。

# 2)异常值处理

# 1、adults与children入住人数之和为0
drop_a = data_fill[data_fill[["adults","children"]].sum(axis=1) == 0]
# 2、客房日均价为负,客房日均价高于1000元
drop_b = data_fill[(data_fill["adr"]<0) | (data_fill["adr"]>1000)]
data_fill.drop(drop_a.index,inplace=True)

data_done=data_fill[(data_fill["adr"]>=0) & (data_fill["adr"] <1000)]

# 3、meal中undefined与sc均表示无餐
data_done["meal"].replace({"Undefined":"SC"},inplace=True)
data_done.shape # 数据清洗完以后,还剩下119208条记录
(119208, 32)

3、数值型特征归一化,类别特征数值化(one-hot编码)

from sklearn.pipeline import  Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler,OneHotEncoder

# 1、数值型特征进行归一化
num_features = ["lead_time",
                "arrival_date_week_number",
                "arrival_date_day_of_month",
                "stays_in_weekend_nights",
                "stays_in_week_nights",
                "adults",
                "children",
                "babies",
                "is_repeated_guest",
                "previous_cancellations",
                "previous_bookings_not_canceled",
                "agent",
                "company",
                "required_car_parking_spaces",
                "total_of_special_requests",
                "adr"]

num_transformer = Pipeline(
    steps=[
        ('imputer', SimpleImputer(strategy='constant',fill_value=0)), # 将空值填充为自定义的值
        ('scaler', StandardScaler())                     # 数据归一化
    ]
)

# 2、类别特征标准化(one-hot)
cat_features = ["hotel",
                "arrival_date_month",
                "meal",
                "market_segment",
                "distribution_channel",
                "reserved_room_type",
                "deposit_type",
                "customer_type"]



cat_transformer = Pipeline(
    steps=[
        ("imputer", SimpleImputer(strategy="constant", fill_value="Unknown")),
        ("onehot", OneHotEncoder(handle_unknown='ignore'))
    ]
)


features = num_features + cat_features

from sklearn.compose import ColumnTransformer
'''
SimpleImputer类可用于替换缺少的值,MinMaxScaler类可用于缩放数值,而OneHotEncoder可用于编码分类变量。

ColumnTransformer()在Python的机器学习库scikit-learn中,可以选择地进行数据转换。

要使用ColumnTransformer,必须指定一个转换器列表。
每个转换器是一个三元素元组,用于定义转换器的名称,要应用的转换以及要应用于其的列索引,例如:(名称,对象,列)
'''
preprocessor = ColumnTransformer(
    transformers=[
        ('num', num_transformer, num_features),
        ('cat', cat_transformer, cat_features)
    ]
)

4、选用随机森林建模,挑选最佳参数

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import KFold, cross_val_score,train_test_split,GridSearchCV



X = data_done.drop("is_canceled",axis=1)
y = data_done["is_canceled"]

# 初步调参n_estimators
# scorel = []
# for i in range(0,200,10):
#     rfc_model = RandomForestClassifier(n_estimators=i+1,
#                                        n_jobs=-1,
#                                        random_state=0)
#     rfc = Pipeline(
#         steps=[
#             ('preprocessor', preprocessor),
#             ('model',rfc_model)
#         ]
#     )
#     split = KFold(n_splits=10, shuffle=True, random_state=42)
#     rfc_t_s = cross_val_score(rfc,
#                                  X,
#                                  y,
#                                  cv=split,
#                                  scoring="accuracy",
#                                  n_jobs=-1
#                               ).mean()
#     print('step',rfc_t_s)
#     scorel.append(rfc_t_s)
# print(max(scorel),(scorel.index(max(scorel))*10) + 1)
# plt.figure(figsize=[20,5])
# plt.plot(range(1,201,10),scorel)
# plt.show()
# 再次调参n_estimators,注意数据集训练比较慢
scorel = []
for i in range(150,170):
    rfc_model = RandomForestClassifier(n_estimators=i+1,
                                       n_jobs=-1,
                                       random_state=0)
    rfc = Pipeline(
        steps=[
            ('preprocessor', preprocessor),
            ('model',rfc_model)
        ]
    )
    split = KFold(n_splits=10, shuffle=True, random_state=42)
    rfc_t_s = cross_val_score(rfc,
                                 X,
                                 y,
                                 cv=split,
                                 scoring="accuracy",
                                 n_jobs=-1
                              ).mean()
    print('step:',i,',score=',rfc_t_s)
    scorel.append(rfc_t_s)

# 从图像可以看出来,n_estimators应该为161
print(max(scorel),([*range(150,170)][scorel.index(max(scorel))]))
plt.figure(figsize=[20,5])
plt.plot(range(150,170),scorel)
plt.show()
param_grid = {
                "model__max_depth":[*range(1,40,10)],
                'model__min_samples_leaf':[*range(1,50,10)]
             }

rfc_model_t = RandomForestClassifier(n_estimators=161,
                                     criterion="gini",
                                     max_features=0.4,
                                     n_jobs=-1,
                                     random_state=0)
rfc_t = Pipeline(
                    steps=[
                            ('preprocessor', preprocessor),
                            ('model',rfc_model_t)
                    ]
                 )

split_t = KFold(n_splits=10, shuffle=True, random_state=42)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
GS = GridSearchCV(rfc_t, param_grid, cv=split_t)

GS.fit(X_train, y_train)

GS.best_params_
# 

5、选用最佳参数训练模型

#训练模型,评估模型
rfc_model = RandomForestClassifier(n_estimators=161
                                 ,criterion="gini"
                                 ,max_depth=31
                                 ,min_samples_leaf=1
                                 ,max_features=0.4
                                 ,n_jobs=-1
                                 ,random_state=0)

rfc = Pipeline(steps=[
                          ('preprocessor', preprocessor),
                          ('model',rfc_model)
                      ])

rfc = rfc.fit(X_train, y_train)
score_=rfc.score(X_test, y_test)
score_
0.8683835248720745
#交叉检验
split = KFold(n_splits=10, shuffle=True, random_state=42)
rfc_s = cross_val_score(         rfc,
                                 X,
                                 y,
                                 cv=split,
                                 scoring="accuracy",
                                 n_jobs=-1)

plt.plot(range(1,11),rfc_s,label = "RandomForest")


传统机器学习(六)集成算法(1)—随机森林算法及案例详解

6、特征重要性

#特征重要性
import eli5
onehot_columns = list(rfc.named_steps['preprocessor'].
                      named_transformers_['cat'].
                      named_steps['onehot'].
                      get_feature_names_out(input_features=cat_features))

feat_imp_list = num_features + onehot_columns

#按重要排序的前10个重要特征及其系数
feat_imp_df = eli5.formatters.as_dataframe.explain_weights_df(
   rfc.named_steps['model'],
    feature_names=feat_imp_list)



feat_imp_df.head(10)
feature weight std
0 deposit_type_Non Refund 0.144919 0.108617
1 lead_time 0.140804 0.016317
2 adr 0.093004 0.003363
3 deposit_type_No Deposit 0.075658 0.103332
4 arrival_date_day_of_month 0.066274 0.002718
5 arrival_date_week_number 0.053368 0.002609
6 total_of_special_requests 0.052200 0.011193
7 agent 0.043441 0.005135
8 previous_cancellations 0.041885 0.014677
9 stays_in_week_nights 0.039749 0.002220

7、对权重大于10%的影响因素(提前预订时间以及保证金类型)进行进一步分析

# 1、预定取消与提前预定时间的关系
plt.figure(figsize=(12, 8))
lead_cancel_data = data_done.groupby("lead_time")["is_canceled"].describe()
sns.scatterplot(x=lead_cancel_data.index, y=lead_cancel_data["mean"].values * 100,color='olive')

plt.title("提前预定时间对预订取消率的影响", fontsize=16)
plt.xlabel("提前预订时间", fontsize=16)
plt.ylabel("预订取消的概率", fontsize=16)
plt.show()

提前预订时间越长,预订取消率越高。酒店应当结合具体经营成本考虑是否限定提前预订的最长时间,以避免资源的占用。

#预定取消与保证金类型的关系
plt.figure(figsize=(12, 8))
deposit_cancel_data = data_done.groupby("deposit_type")["is_canceled"].describe()
sns.barplot(x=deposit_cancel_data.index, y=deposit_cancel_data["mean"] * 100,palette="Blues")

plt.title("Effect of deposit_type on cancelation", fontsize=16)
plt.xlabel("Deposit type", fontsize=16)
plt.ylabel("Cancelations [%]", fontsize=16)
plt.show()

押金不退还的预订取消率在所有押金类型中最高,这种现象与观念中的消费者心理有些背离,需要再一步分析。

事实上,各个市场细分渠道中,
线下旅行社代理预订以及团体预订的取消率最高,
而在押金类型中,无押金的预订量主要集中在在线预订,
而不退押金的预订量主要集中在线下旅行社代理预订以及团体预订,导致不退押金的预订取消率较高。

酒店可以尝试设计问卷,对线下旅行社以及团体进行访问,探寻原因及解决方法。文章来源地址https://www.toymoban.com/news/detail-430124.html

到了这里,关于传统机器学习(六)集成算法(1)—随机森林算法及案例详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 机器学习算法:线性回归、逻辑回归、决策树和随机森林解析

    引言 机器学习算法是人工智能领域的核心,它们用于解决各种问题,从预测房价到图像分类。本博客将深入探讨四种常见的机器学习算法:线性回归、逻辑回归、决策树和随机森林。 线性回归 什么是线性回归? 线性回归是一种用于建立连续数值输出的机器学习模型的算法。

    2024年02月10日
    浏览(53)
  • 【机器学习】P25 随机森林算法(2) 实现 “波士顿房价” 预测

    随机森林(Random Forest)算法 是一种 集成学习(Ensemble Learning)方法,它由多个决策树组成,是一种分类、回归和特征选择的机器学习算法。 在随机森林中,每个决策树都是独立地训练的,每棵树的建立都是基于随机选取的 特征子集 和随机选取的 训练样本集 。 在分类问题

    2024年02月01日
    浏览(55)
  • 机器学习实战6-糖尿病疾病的预测与分析(随机森林算法)

    大家好,我是微学AI,今天给大家介绍一下机器学习实战6-糖尿病疾病的预测与分析(随机森林算法),糖尿病是一种常见的慢性代谢性疾病,由于生活方式及基因等因素的影响,全球范围内糖尿病患者人数不断增加。预测糖尿病的发生有助于早期筛查和干预治疗,以降低糖尿

    2024年02月04日
    浏览(48)
  • 【机器学习算法】决策树和随机森林在计算机视觉中的应用

    决策树和随机森林在计算机视觉中有着广泛的应用。决策树作为一种简单而强大的分类模型,可以用于图像分类、目标检测、特征提取等任务。它能够根据图像的特征逐层进行判断和分类,从而实现对图像数据的智能分析和理解。随机森林作为一种集成学习方法,利用多棵决

    2024年04月13日
    浏览(62)
  • 传统机器学习(六)集成算法(2)—Adaboost算法原理

    Adaboost(Adaptive Boosting)是一种自适应增强算法,它集成多个弱决策器进行决策。 Adaboost解决二分类问题 ,且二分类的标签为{-1,1}。 注:一定是{-1,1},不能是{0,1} 它的训练过程是通过不断添加新的弱决策器,使损失函数继续下降,直到添加决策器已无效,最终将所有决策器集成

    2024年02月05日
    浏览(45)
  • python机器学习——分类模型评估 & 分类算法(k近邻,朴素贝叶斯,决策树,随机森林,逻辑回归,svm)

    交叉验证:为了让被评估的模型更加准确可信 交叉验证:将拿到的数据,分为训练和验证集。以下图为例:将数据分成5份,其中一份作为验证集。然后经过5次(组)的测试,每次都更换不同的验证集。即得到5组模型的结果,取平均值作为最终结果。又称5折交叉验证。 通常情

    2024年02月03日
    浏览(67)
  • 分类算法-随机森林实战案例

            随机森林是一种 有监督学习算法 ,是以决策树为基学习器的 集成学习算法 。                 那什么是有监督学习呢?有监督学习就是把有已知结果的数据集拿去训练,如果训练结果与标准答案的精度足够高就可以使用这个模型去预测或者分类未知结果

    2023年04月16日
    浏览(44)
  • 机器学习案例:运营商客户流失的数据分析 #数据去重#数据分组整合#缺失值处理#相关性分析#样本平衡#决策树、随机森林、逻辑回归

    前提: 随着业务快速发展、电信市场的竞争愈演愈烈。如何最大程度地挽留在网用户、吸取新客户,是电信企业最 关注的问题之一。 客户流失 会给企业带来一系列损失,故在发展用户每月增加的同时,如何挽留和争取更多 的用户,也是一项非常重要的工作。 能否利用大数

    2024年02月08日
    浏览(52)
  • 机器学习算法原理lightgbm、word2vec、cnn、lstm、textcnn、bert、transformer、随机森林、lr

    首先需要说一说GBDT,它是一种基于决策树的集成算法,它使用的集成方法是boosting,其主要思想是通过多次迭代,每次迭代都学习一棵CART树来拟合之前 t-1 棵树的预测结果与训练样本真实值的残差,最终得到一个准确度更高的模型。 全称为Gradient Boosting Decision Tree。它是一种

    2024年02月13日
    浏览(37)
  • 机器学习——决策树/随机森林

    决策树可以做分类也可以做回归,决策树容易过拟合 决策树算法的基本原理是依据信息学熵的概念设计的(Logistic回归和贝叶斯是基于概率论),熵最早起源于物理学,在信息学当中表示不确定性的度量,熵值越大表示不确定性越大。 ID3算法就是一种通过熵的变化,构造决策

    2024年02月07日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包