集成学习

1.目的:让机器学习效果更好,单个不行,群殴走起。
2.集成学习分类
      Bagging:训练多个分类器取平均f(x)=1Mm=1Mfm(x)f\left( x \right)=\frac{1}{M}\sum\limits_{m=1}^{M}{{{f}_{m}}\left( x \right)}
      Boosting:从弱学习器开始加强,通过加权来进行训练。Fm(x)=Fm1(x)+argminhi=1nL(yi,Fm1(xi)+h(xi)){{F}_{m}}\left( x \right)={{F}_{m-1}}\left( x \right)+\arg {{\min }_{h}}\sum\limits_{i=1}^{n}{L\left( {{y}_{i}},{{F}_{m-1}}\left( {{x}_{i}} \right)+h\left( {{x}_{i}} \right) \right)}(加入一棵树,要比原来强)
      Stacking:聚合多个分类或回归模型(可以分阶段来做)
3. Bagging模型
      全称:bootstrap aggregation(说白了就是并行训练一堆分类器)
      最典型的代表就是随机森林啦。
      随机:数据采样随机,特征选择随机,但是随机的数据量是一样的。
      森林:很多个决策树并行放在一起。
集成学习与实战
3.1随机森林
      构造树模型:
集成学习与实战
      由于二重随机性,使得每棵树基本上都不会一样,最终的结果也会不一样
      二重抽样:一般情况下,先从总体的N个单位中抽取一个较大的初始样本D,称为第一重(相)样本,对之进行调查以获取总体的某些辅助信息,为下一步的抽样估计提供条件;然后进行第二重抽样,即从初始样本D中抽取一个容量为M(M<N)的子样本。第二重样本相对较小,但是第二重抽样调查才是主调查。
      随机的重要性:想要得到泛化能力强的集成,集成中的个体学习器应尽可能相互独立;虽然“独立”在现实任务中很难实现,但可以设法使基学习器尽可能具有较大的差异。给定一个训练集,一种可能的做法就是对训练样本进行采样(也就是随机),产生出若干个不同的子集,再从每个数据子集中训练出一个基学习器。这样由于训练数据不同,我们获得的基学习器可望具有比较大的差异。然而,为了获得好的集成,我们还希望个体学习器不能太差。如果采样出的每个子集都完全不同,则每个基学习器只用到了一小部分训练数据,甚至不足以进行有效学习,这显然不能确保产生比较好的基学习器。
随机森林的优势
      ① 它能够处理很高维度(feature很多)的数据,并且不用做特征选择(降维);
      ② 能够有效地运行在大数据集上;
      ③ 对于缺省值问题也能够获得很好的结果;
      ④ 在训练完后,它能够给出那些feature比较重要;
      ⑤ 容易做成并行化方法,速度比较快;
      ⑥ 可以进行可视化展示,便于分析。
      KNN就不太适合,因为很难去随机让泛化能力变强!
      理论上越多的树效果越好,但实际上基本超过一定数量就效果就差不多了。
4. Boosting模型
      典型代表:Adaboost,Xgboost。
      Adaboost会根据前一次的分类效果调整数据权重。
      问题1:在每一轮如何改变训练数据的权值或概率分布?
      解释:如果某一个数据在这次分错了,那么下一次就会给他更大的权重,使得它能够在下一次分辨出来。
      问题2:如何将弱分类器组合成一个强分类器?
      最终的结果:每个分类器根据自身的准确性来确定各自的权重,再合体。
      举例:假如整个样本数量为100,第一次分类正确数量为80,那么第二次会基于第一次的分类结果,调整参数(第一次权重大的参数,可能在第二次变小了;第一次权重小的 参数,可能在第二次权重变大了)对剩下的20个样本进行分类,假如分类正确的数量为15,第三次…
      Adaboost工作流程
      每一次切一刀!
      最终合在一起。
      弱分类器这就升级了!
5.Stacking模型
      stacked 产生方法是一种截然不同的组合多个模型的方法,它讲的是组合各种各样的分类器(KNN,SVM,RF等)的概念,但是使用的相对于bagging和boosting较少,它不像bagging和boosting,而是组合不同的模型,具体的过程如下:
      ① 划分训练数据集为两个不相交的集合。
      ② 在第一个集合上训练多个学习器。
      ③ 在第二个集合上测试这几个学习器。
      ④ 把第三步得到的预测结果作为输入,把正确的回应作为输出,训练一个高层学习器。
这里需要注意的是1-3步的效果与cross-validation,我们不是用赢家通吃,而是使用非线性组合学习器的方法。
集成学习与实战集成学习与实战
集成学习与实战集成学习与实战
      堆叠在一起确实可以提升准确率,但是速度是个问题。集成算法是竞赛与论文的神器,当我们更关注于结果时不妨来试试。
6.集成学习实战
(1)获取数据,并输出数据分析的结果,可以发现Age这列有缺失值

import pandas as pd
titanic = pd.read_csv('titanic_train.csv')
titanic.head()
print(titanic.describe())

集成学习与实战
(2)使用中值进行数据填充

titanic['Age'] = titanic['Age'].fillna(titanic['Age'].median())
print(titanic.describe())

集成学习与实战
(3)将Sex这一列的字符串转成整数,方便计算

print(titanic['Sex'].unique())
#0:表示男,1表示女
titanic.loc[titanic['Sex'] == 'male','Sex'] = 0
titanic.loc[titanic['Sex'] == 'female','Sex'] = 1

(4)将Embarked这一列的字符串转成整数

print(titanic['Embarked'].unique())
titanic["Embarked"] = titanic["Embarked"].fillna('S')
titanic.loc[titanic["Embarked"] == "S", "Embarked"] = 0
titanic.loc[titanic["Embarked"] == "C", "Embarked"] = 1
titanic.loc[titanic["Embarked"] == "Q", "Embarked"] = 2

(5)使用线性回归方法

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
#预测所用到的特征
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
#初始化线性回归函数
lg = LinearRegression()
#初始化K折交叉验证函数
kf = KFold(n_splits=3,shuffle=False)
predictions = []
for train,test in kf.split(titanic):
    train_predictors = titanic[predictors].loc[train,:]
    train_target = titanic['Survived'].loc[train]
    lg.fit(train_predictors,train_target)
    test_predictions = lg.predict(titanic[predictors].loc[test,:])
    predictions.append(test_predictions)
import numpy as np
predictions = np.concatenate(predictions,axis = 0)
# print(predictions.shape)
#匹配输出结果,1表示生存,0表示死亡
predictions[predictions > .5] = 1
predictions[predictions <= .5] = 1
accuracy = sum(predictions [predictions== titanic['Survived']]) / len(predictions)
print(accuracy)

输出结果为:0.3838383838383838
(6)使用逻辑回归方法

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
#初始化逻辑回归函数
lr = LogisticRegression(random_state=1,solver='liblinear')
#应用交叉验证并计算精确分数
scores = cross_val_score(lr,titanic[predictors],titanic['Survived'],cv=3)
print(scores.mean())

输出结果为:0.7878787878787877
(7)读取测试数据集,并对其进行处理

titanic_test = pd.read_csv("test.csv")
titanic_test["Age"] = titanic_test["Age"].fillna(titanic["Age"].median())
titanic_test["Fare"] = titanic_test["Fare"].fillna(titanic_test["Fare"].median())
titanic_test.loc[titanic_test["Sex"] == "male", "Sex"] = 0 
titanic_test.loc[titanic_test["Sex"] == "female", "Sex"] = 1
titanic_test["Embarked"] = titanic_test["Embarked"].fillna("S")
titanic_test.loc[titanic_test["Embarked"] == "S", "Embarked"] = 0
titanic_test.loc[titanic_test["Embarked"] == "C", "Embarked"] = 1
titanic_test.loc[titanic_test["Embarked"] == "Q", "Embarked"] = 2

(8)使用随机森林方法

from sklearn.model_selection import KFold,cross_val_score
from sklearn.ensemble import RandomForestClassifier
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
# n_estimators:数的个数
# min_samples_split:如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划
# 分如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
# min_samples_leaf:这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,
# 则会和兄弟节点一起被剪枝,如果样本量不大,不需要管这个值,大些如10W可是尝试下5
rfc = RandomForestClassifier(random_state=1,
							 n_estimators=10,
							min_samples_split=2,
							min_samples_leaf=1)
# 初始化K折交叉验证函数
kf = KFold(n_splits=3,shuffle=False)
scores = cross_val_score(rfc,titanic[predictors],titanic['Survived'],cv=kf)
print(scores.mean())
输出结果:0.7856341189674523
(9)调整参数之后的随机森林
rfc = RandomForestClassifier(random_state=1,
						     n_estimators=100,
						     min_samples_split=4,
						     min_samples_leaf=2)
# 初始化K折交叉验证函数
kf = KFold(n_splits=3,shuffle=False)
scores = cross_val_score(rfc,titanic[predictors],titanic['Survived'],cv=kf)
print(scores.mean())

输出结果:0.8148148148148148
(10)构造新的特征

# 生成一个家庭人数列
titanic["FamilySize"] = titanic["SibSp"] + titanic["Parch"]
# 生成一个姓名长度series
titanic["NameLength"] = titanic["Name"].apply(lambda x:len(x))
import re
def get_title(name):
    # 使用正则表达式搜索标题。标题总是由大写字母和小写字母组成,以句号结尾。
    title_search = re.search(' ([A-Za-z]+)\.',name)
    # 如果标题存在,提取并返回它.
    if title_search:
        return title_search.group(1)
    return ''
# 得到所有的名字的简称(title)
titles = titanic["Name"].apply(get_title)
print(pd.value_counts(titles))
# 把每个title映射成整数,一些title是比较稀少的,所以被压缩成与其他标题相同的编码。
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Dr": 5, "Rev": 6, 
"Major": 7, "Col": 7, "Mlle": 8, "Mme": 8, "Don": 9, "Lady": 10, "Countess": 10,
 "Jonkheer": 10, "Sir": 9, "Capt": 7, "Ms": 2}
for k,v in title_mapping.items():
    titles[titles == k] = v
print(pd.value_counts(titles))
# 添加一个新的列
titanic['Title'] = titles

集成学习与实战
:正则表达式search的用法
集成学习与实战
(11)特征重要性分析

import numpy as np
from sklearn.feature_selection import SelectKBest,f_classif
# f_classif为方差分析,用来计算特征的f统计量的。
import matplotlib.pyplot as plt
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked", 
"FamilySize", "Title", "NameLength"]
# 特征选择
selector = SelectKBest(f_classif,k=5)
selector.fit(titanic[predictors],titanic['Survived'])

# 得到每一维特征的p值,把p值转化成分数
scores = -np.log10(selector.pvalues_)
# 画出分数,观察特征的分数
plt.bar(range(len(predictors)),scores)
plt.xticks(range(len(predictors)),predictors,rotation='vertical')
plt.show()

集成学习与实战
根据上一幅图找出前四个最好的特征

predictors = ["Pclass", "Sex", "Fare", "Title"]
rfc = RandomForestClassifier(random_state=1,
							 n_estimators=50,
							 min_samples_split=8,
							 min_samples_leaf=4)
scores = cross_val_score(rfc,titanic[predictors],titanic['Survived'],cv=3)
print(scores.mean())

输出结果:0.8170594837261503
:f_classif为方差分析,计算f值,利用f=SA/(r1)SE/(nr)f=\frac{{{S}_{A}}/\left( r-1 \right)}{{{S}_{E}}/\left( n-r \right)}值这个检验统计量,可以判断假设H0是否成立:f值越大,大到一定程度时,就有理由拒绝零假设,认为不同总体下的均值存在显著差异。则认为该特征相对来说比较重要。
(12)将boosting算法与逻辑回归算法分别进行训练,然后给与不同的权值使得结果更加精确,类似于Stacking的做法,但不是Stacking。

from sklearn.ensemble import GradientBoostingClassifier
import numpy as np
algorithms = [
    [GradientBoostingClassifier(random_state=1,n_estimators=25,max_depth=3), ["Pclass", "Sex", "Age", "Fare", "Embarked", "FamilySize", "Title"]],
    [LogisticRegression(random_state=1,solver='liblinear'), ["Pclass", "Sex", "Fare", "FamilySize", "Title", "Age", "Embarked"]]
]
# 初始化交叉熵
kf = KFold(n_splits=3,shuffle=False)
predictions = []
for train,test in kf.split(titanic):
    train_target = titanic['Survived'].loc[train]
    full_test_predictions = []
    # 使用每个算法进行预测
    for alg,predictors in algorithms:
        alg.fit(titanic[predictors].loc[train,:],train_target)
        # 在测试集上进行预测
        test_predictions = alg.predict_proba(titanic[predictors].loc[test,:].astype(float))[:,1]
        full_test_predictions.append(test_predictions)
    test_predictions = (full_test_predictions[0] + full_test_predictions[1]) / 2
    test_predictions[test_predictions <= .5] = 0
    test_predictions[test_predictions > .5] = 1
    predictions.append(test_predictions)
    
# Put all the predictions together into one array.
predictions = np.concatenate(predictions, axis=0)

# Compute accuracy by comparing to the training data.
accuracy = sum(predictions[predictions == titanic["Survived"]]) / len(predictions)
print(accuracy)

输出结果:0.27946127946127947
进行特征处理

titles = titanic_test["Name"].apply(get_title)
# 把每个title映射成整数,一些title是比较稀少的,所以被压缩成与其他标题相同的编码。
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Dr": 5, "Rev": 6, "Major": 7, "Col": 7, "Mlle": 8, "Mme": 8, "Don": 9, "Lady": 10, "Countess": 10, "Jonkheer": 10, "Sir": 9, "Capt": 7, "Ms": 2, "Dona": 10}
for k,v in title_mapping.items():
    titles[titles == k] = v
titanic_test["Title"] = titles
# 得到title的组数
print(pd.value_counts(titanic_test["Title"]))
# 添加一个新的家庭大小列
titanic_test["FamilySize"] = titanic_test["SibSp"] + titanic_test["Parch"]

特征处理后,进行训练模型,然后赋予不同的权值

predictors = ["Pclass", "Sex", "Age", "Fare", "Embarked", "FamilySize", "Title"]
algorithms = [
    [GradientBoostingClassifier(random_state=1, n_estimators=25, max_depth=3), predictors],
    [LogisticRegression(random_state=1,solver='liblinear'), ["Pclass", "Sex", "Fare", "FamilySize", "Title", "Age", "Embarked"]]
]
full_predictions = []
for alg, predictors in algorithms:
    # Fit the algorithm using the full training data.
    alg.fit(titanic[predictors], titanic["Survived"])
    # Predict using the test dataset.  We have to convert all the columns to floats to avoid an error.
    predictions = alg.predict_proba(titanic_test[predictors].astype(float))[:,1]
    full_predictions.append(predictions)
# The gradient boosting classifier generates better predictions, so we weight it higher.
predictions = (full_predictions[0] * 3 + full_predictions[1]) / 4
predictions

相关文章: