数据挖掘实战总结

96
PeTu
0.3 2018.05.21 23:32* 字数 2255

零、 引言

此篇文章的初版,是笔者照着kaggle竞赛社区中Titanic项目中的两篇文章实战后的总结,两篇文章分别为:

  1. Titanic Data Science Solutions

    第一篇文章是以Titanic项目为例完完整整的介绍了一遍数据挖掘实战从理解数据到训练模型最后提交的整个过程,跟着实现一遍可以很清楚的理解与感知数据挖掘实战全过程,非常有助于培养实战的感觉

  2. Introduction to Ensembling/Stacking in Python

    第二篇文章也是一个以Titanic项目为例完整介绍了实战过程的文章,但是这篇文章的重心在于介绍与实践数据挖掘的集成算法 — Stacking 算法

一、 实战过程及常用方法

0. 理解题目与观察数据

- 理解题目

接手任何一竞赛或者项目的时候,第一件事都是要认真的阅读题目,充分理解题目的背景,因为每个项目虽然大体上的流程是差不多,但是每一个步骤的实现都会不一样,例如特征提取与选择方面,除去利用数学知识降维或者提取主要特征之外,还有一个很重要的方面,就是需要理解题目的业务场景,代入背景去思考业务的情况,这种做法可以让我们事先加强对特征的理解,方便我们判断特征工程过程中的合理性,也可以在一些项目场景中利用其特有的数据特征来修正这种场景下的模型已达到很好的效果

这种对于业务场景的思考应是贯彻到整个项目实战过程中的

- 观察数据

初步题目后,紧接着就是对于数据的观察和思考,python中利用pandas进行数据的加载和处理非常方便,其中pandas库中的一些观察数据的方法有

import pandas as pd
train_df = pd.read_csv('./data/train.csv')
train_df.head()
train_df.info()
# describe() 用于观察连续数值特征
train_df.describe()
# describe(include=['O']) 用于观察字符串特征及非连续值分类特征
train_df.describe(include=['O'])

其中describe()是个很有效的描述数据的方法,可以加入percentiles=[.1, .2, .3, .4, .5, .6, .7]这样的分位参数来将连续数值特征排序并显示分位值。描述分类特征则可以返回特征值的个数、频率等值

一般观察思考数据是结合业务场景的,需要理解什么样的场景下会产生这样的数据,哪些数据特征与结果存在明显的对应关系等,在采取合适的操作之前应该有一些我们自己的关于数据的假设,然后在数据中佐证我们的假设

1. 分析数据与特征工程

We need arrive at following assumptions based on data analysis done so far. We may validate these assumptions further before taking appropriate actions.

上句是借用第一篇文章的一句话,也是我们这一部分的思考,数据分析比较多的都是用数据来验证我们的假想,然后再采取更合适的操作

- 特征工程部分的工作流程需要解决七个主要目标:

  • 归类(Classifying)

    需要尝试分类或者归类我们的样例,并且去理解分出的不同的类别的含义及其与我们的目标之间的关联

  • 关联(Correlating)

    一种方法是利用训练数据集中可用的数据特征(feature)。哪些数据集中的特征对我们的解决方案目标有显著作用?从统计的角度说哪些数据特征与待解决目标之间有较大相关性?训练集中的数据特征值改变后待解决目标值是否也一样变化,并且反之亦然?这些都可以针对给定数据集中的数值特征和分类特征进行测试。我们也想要确定各个数据特征之间的相关性,关联一些确定的数据特征可以有效地帮助我们创建、完善或者纠正其他的数据特征

  • 转化(Converting)

    在建模阶段,需要准备适合模型训练的数据。根据模型算法的选择,可能需要将所有特征转换为等价的数值。例如将文本分类值特征转换数值特征

  • 完善(Completing)

    数据准备过程也可能要求我们估计特征中的任何缺失值。 当没有缺失值时,模型算法可能效果最好

  • 纠正(Correcting)

    我们还可以分析给定的训练数据集以查找错误或可能无法使用的特征值,并尝试纠正这些值或排除包含错误的样本。 一种方法是检测样本或特征中的任何异常值。 如果某项特征对分析没有贡献,或者可能会显着影响结果,我们也可能会完全放弃该特征

  • 创造(Creating)

    我们可以基于现有的特征或者一系列特征创造新的数据特征,且新特征遵循相关性,转换成数值和完整性目标

  • 图表(Charting)

    根据数据的性质和解决方案目标来选择正确的可视化图表和图表

上述的七个方向不仅是特征工程部分我们需要思考的,也是我们进行数据分析处理的七个角度

所以对应的数据清洗操作就可包含:

  • Correcting by dropping features
  • Creating new feature extracting from existing || Create new feature combining existing features
  • Converting a categorical feature || Converting categorical feature to numeric
  • Completing numerical continuous feature || Completing a categorical feature

- 数据分析常用的方法有

Analyze by pivoting feature

train_df[['feature1', 'feature2']].groupby(['feature1 or feature2'], as_index=False).mean().sort_values(by='feature1 or feature2', ascending=False)

Analyze by visualizing data

# 利用 seaborn 画图
# sns.barplot | plt.hist | sns.pointplot
grid = sns.FacetGrid(train_df, col='feature1', row='feature2', size=2.2, aspect=1.6)
grid.map(plt.hist, 'feature3', alpha=.5, bins=20)
grid.add_legend()
grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6)
grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')
grid.add_legend()
plt.show()
# 特征值相关性的热力图
colormap = plt.cm.RdBu
plt.figure(figsize=(14,12))
plt.title('Correlation of Features', y=1.05, size=15)
sns.heatmap(train.astype(float).corr(),linewidths=0.1,vmax=1.0, 
            square=True, cmap=colormap, linecolor='white', annot=True)
g = sns.pairplot(train[[u'Survived', u'Pclass', u'Sex', u'Age', u'Parch', u'Fare', u'Embarked',
       u'FamilySize', u'Title']], hue='Survived', palette = 'seismic',size=1.2,diag_kind = 'kde',diag_kws=dict(shade=True),plot_kws=dict(s=10) )
g.set(xticklabels=[])
# 利用 plotly 画图 
# go.Scatter | go.Bar 
trace = go.Scatter(
    y = feature_dataframe['Random Forest feature importances'].values,
    x = feature_dataframe['features'].values,
    mode='markers',
    marker=dict(
        sizemode = 'diameter',
        sizeref = 1,
        size = 25,
#       size= feature_dataframe['AdaBoost feature importances'].values,
        #color = np.random.randn(500), #set color equal to a variable
        color = feature_dataframe['Random Forest feature importances'].values,
        colorscale='Portland',
        showscale=True
    ),
    text = feature_dataframe['features'].values
)
data = [trace]

layout= go.Layout(
    autosize= True,
    title= 'Random Forest Feature Importance',
    hovermode= 'closest',
#     xaxis= dict(
#         title= 'Pop',
#         ticklen= 5,
#         zeroline= False,
#         gridwidth= 2,
#     ),
    yaxis=dict(
        title= 'Feature Importance',
        ticklen= 5,
        gridwidth= 2
    ),
    showlegend= False
)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig,filename='scatter2010')
# 用 plotly 画热力图
data = [
    go.Heatmap(
        z= base_predictions_train.astype(float).corr().values ,
        x=base_predictions_train.columns.values,
        y= base_predictions_train.columns.values,
          colorscale='Viridis',
            showscale=True,
            reversescale = True
    )
]
py.iplot(data, filename='labelled-heatmap')

2. 模型训练与预测

这里是对于数据挖掘算法的选择,一般分类用的算法包括:

  • Logistic Regression
  • KNN or k-Nearest Neighbors
  • Support Vector Machines
  • Naive Bayes classifier
  • Decision Tree
  • Random Forrest
  • Xgboost

- Stacking 算法

此处额外需要讲述一种集成算法 —Stacking 集成算法,以两层Stacking 算法为例:

第一层,可以挑选 4 种或 5 种分类算法,记为model_amodel_bmodel_cmodel_dmodel_e

对训练数据进行训练,此时就需要注意,在Stacking算法中,对训练数据的模型训练需要用到 K-折交叉验证 方法以 5-折交叉验证 为例:

首先假设我们有 m * n维度的训练数据train_set以及k * w维度的测试数据test_set,把train_set分为5份,取出其中的 4 份作为新的(4/5)m * n维度的训练数据记为tr_set,另一份则作为临时的(1/5)m * n维度的测试数据记为te_set,假设模型model_a,利用tr_setmodel_a进行训练,训练好的模型来预测余下的一份te_set,得到的结果为(1/5)m * 1维度,用一种m * 1维度的数据结构model_list_a中的一部分记录下来,然后继续用此时的model_a预测全部的测试数据,得到结果model_a_tmp_1

因为是 **5-折 **交叉验证,所以这个过程会重复五遍,即model_a模型会被不同的(4/5)m * n维度的训练数据训练五遍,最终的model_list_a里保存的则是model_a对于所有训练数据的预测值,每一次的重复又会产生不同的model_a_tmp_(2,3,4,5),将这些model_a_tmp相加求平均得model_a_test

而又因为我们选择了五个训练模型,所以对于model_bmodel_cmodel_dmodel_e四个模型,我们同样会各训练五遍,也就自然会产生model_list_bmodel_list_cmodel_list_dmodel_list_e,分别存储的是四个模型对于全部训练数据的预测值,还会产生每个模型的对于测试数据test_set的平均预测结果 model_b_testmodel_c_testmodel_d_testmodel_e_test

然后将得到的结果拼接,如下代码实现:

x_train = np.concatenate(( model_list_a, model_list_b, model_list_c, model_list_d, model_list_e), axis=1)
x_test = np.concatenate(( model_a_test, model_b_test, model_c_test, model_d_test, model_e_test), axis=1)

此时得到的x_train的数据结构可能是这样的

屏幕快照 2018-05-21 下午11.06.43.png

我们就是利用这个 x_trainx_test 进行 stacking 算法的第二层训练,例如利用xgboost算法进行训练,如下:

gbm = xgb.XGBClassifier(
    #learning_rate = 0.02,
 n_estimators= 2000,
 max_depth= 4,
 min_child_weight= 2,
 #gamma=1,
 gamma=0.9,                        
 subsample=0.8,
 colsample_bytree=0.8,
 objective= 'binary:logistic',
 nthread= -1,
 scale_pos_weight=1).fit(x_train, y_train)
predictions = gbm.predict(x_test)

则此时得到的predictions就是我们利用Stacking算法集成了很多种基础算法得到的最终结果

这个过程中有一个难点就是关于每个模型利用 k-折交叉验证 的思想进行的 k 次重复训练,实现代码如下:

# Some useful parameters which will come in handy later on
ntrain = train.shape[0]
ntest = test.shape[0]
SEED = 0 # for reproducibility
NFOLDS = 5 # set folds for out-of-fold prediction
kf = KFold(ntrain, n_folds= NFOLDS, random_state=SEED)

def get_oof(clf, x_train, y_train, x_test):
    oof_train = np.zeros((ntrain,))
    oof_test = np.zeros((ntest,))
    oof_test_skf = np.empty((NFOLDS, ntest))

    for i, (train_index, test_index) in enumerate(kf):
        x_tr = x_train[train_index]
        y_tr = y_train[train_index]
        x_te = x_train[test_index]

        clf.train(x_tr, y_tr)

        oof_train[test_index] = clf.predict(x_te)
        oof_test_skf[i, :] = clf.predict(x_test)

    oof_test[:] = oof_test_skf.mean(axis=0)
    return oof_train.reshape(-1, 1), oof_test.reshape(-1, 1)

此外推荐阅读 数据比赛大杀器----模型融合(stacking&blending),并且会总结一些其它重要的算法,此处挖坑GBDTxgboost

大数据/数据挖掘
Web note ad 1