美文网首页Machine Learning & Data Analysis机器学习机器学习
机器学习笔记(1)-分析框架-以Kaggle Titanic问题

机器学习笔记(1)-分析框架-以Kaggle Titanic问题

作者: 漫步寻路 | 来源:发表于2017-05-02 08:18 被阅读3785次

    如果对问题更感兴趣,可以跳过引言,直接看第二部分 数据处理, Github地址

    引言

    最近这段时间使用机器学习分析了Titanic等几个问题后,发现机器学习的实践过程流程繁多,需要反复调整特征和参数进行迭代,此外,很多流程是基于经验的或者启发式的;机器学习问题处理数据的流程往往是类似的,每个问题处理的结果和方法对其他问题是有参考价值的。类似的流程和发掘模式的过程没有被总结起来。

    scikit-learn在流程模块化方面做得不错,将一些重要的方法按模块进行了分类:

    • 数据预处理模块preprocessing、模型选择model_selection、模型损失函数metric、特征提取feature selection and feature extraction
    • 流水线管理pipeline
    • 数据集管理datasets

    Aurélien Géron介绍了工程实践中机器学习的重要步骤。

    1. 熟悉业务类型、业绩解决方法和机器学习模块在工程实践架构中的位置(包括输入与输出等)。
    2. 数据的第一印象。
    3. 数据预处理以及特征工程。
    4. 尝试选用模型,并选出基础模型。
    5. 调参找出当前合适模型,得到完整模型参数和实验结果。
    6. 完整表述你的模型,找出核心模式,重复步骤3,直到达到最低的预期结果。
    7. 发布、监视和调整模型。

    我理想的机器学习分析框架是应该包含知识库和工具集,类似于机械公敌中的机器管家。他应该具有以下特点:

    1. 数据印象报告。由于不同的数据集问题互相是可以提供参考和依据的,所以分析框架应该能够提供其它机器学习的数据印象。
    2. 特征选择和学习报告。特征的选择和学习是优化数据的重要模块,不同特征选择情况下模型的差异化需要进行分析和整理。
    3. 流程模块化。类似于scikit-learn的流程模块。
    4. wiki化。记录以前学习过的例子的印象。

    接下来写下我分析Titanic的基本流程,然后搭建机器学习的基础分析框架。

    Kaggle Titanic

    先使用jupyter结合scikit-learn、pandas、matplotlib进行分析。
    初始化环境

    %matplotlib inline
    import matplotlib
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    
    一、问题类型

    1.1 基本分析
    Titanic需要根据每个人的年龄、仓位等信息判断生还可能性,属于标准二分类监督学习问题。一般处理流程是先使用决策树线性逻辑回归这类基础而且具有解释性的模型进行初步分析,得到基础的权重值(Embbed嵌入式特征选择:正则化和决策树);结合Filter过滤(例如相关性分析)进行特征的学习和选择;部分特征值具有二选一的特性时或者相关性较低时可以使用Wrapped特征选择(例如启发性搜索或者随机搜索);最后使用集成学习方法进行训练得到最终模型。

    问:是否有类似的问题,是否存在此类问题较好的解决方案?

    • 问题经验:妇女、儿童、老人以及富人都可能获得较高的生存可能性,这些字段需要重点考察。
    • 应用:倒是想到一些合理的应用:消防方面、交通事故这些领域可以通过综合天气、过往数据等来分配警力和检查频率等。
    二、数据印象

    2.1 数据构成分析
    机器学习的数据是表格化的,很适合使用pandas来进行分析数据。pandas的基本使用方法可以参考pandas

    def get_titanic_data():
        train_data =pd.read_csv("../data/titanic/train.csv")
        test_data = pd.read_csv("../data/titanic/test.csv")
        return train_data, test_data
    
    train_data, test_data = get_titanic_data()
    train_data.info()
    train_data.head()
    
    Titanic数据描述
    Titanic原始数据

    根据上面的数据,得到一些结论:

    • 数据规模:采样偏差不大,训练规模较小。总共船员是2224人,死亡1502人,训练数据有891人,训练集死亡人数需要对比一下比例。数据条目m = 891, 有用特征数目n = 10(除去PassengerId字段), 目标输出k = 1(Survived字段)

    • 数据特征:类型多样,需要进行筛选和处理。文本:Name;文本+数字:Ticket, Cabin;类目:Sex,Pclass,Embarked; 连续值:Age,Fare,SibSp,Parch。

    • 数据质量:部分数据缺失严重,需要处理(拟合、填充或者参照组忽略)登陆港口少量缺失,年龄缺失1/4,客舱缺失3/4;异常值探测会在下面执行(比如某人的fare特别大等可以排查掉)。

    2.2 数据统计学分析
    根据数据特征分析,将训练数据进行划分:连续和离散,暂不分析包含文本字段。

    target_column = 'Survived'
    continuous_column_list = ['Age', 'SibSp', 'Fare',
                          'Parch']
    discrete_column_list = ['Sex', 'Pclass', 'Embarked']
    text_column_list = ['Name', 'Ticket', 'Cabin']
    
    continuous_train_data = train_data.filter(continuous_column_list)
    discrete_train_data = train_data.filter(discrete_column_list)
    

    2.2.1 统计特征
    2.2.1.1 连续值分析
    continuous_train_data.describe()

    连续值分析
    总体情况
    • 年龄:主要集中在15~40之间,根据四分区间可知分布较均匀,老人和小孩是少数,即skewed data,很可能被忽略,所以后期可以增加额外的特征字段;根据经验,年龄字段适合作为区间来处理。
    • 票价:票价分配不均匀,均值在32,中值在14,方差波动大,少数人的票价较高,同样是skewed data,可以增加高价票字段。
    • SibSp(旁系亲属)和Parch(直系亲属):范围取值均在0~10以内,可以考虑作为类目字段处理。75%以上人的Parch都是0,可以考虑换成0/1,考虑结合后期的相关性分析来获得结果。

    柱状图

    柱状图
    由柱状图可知:Parch、SibSp取值少,分布不均匀,不适合作为连续值来处理;Fare和Age部分区间取值过少,这部分特征容易被掩盖,需要提取额外的特征值。

    相关性分析
    二分类问题不适合皮尔逊相关系数,使用卡方校验检测Parch和SibSp和Survived相关性。

    from sklearn.feature_selection import chi2
    print "Parch:", chi2(train_data.filter(["Parch"]), train_data['Survived'])
    print "SibSp:", chi2(train_data.filter(["SibSp"]), train_data['Survived'])
    

    Parch: 10.09749911, 0.00148471
    SibSp: 2.58186538, 0.10809421

    可以看到Parch比SibSp的卡方校验取值大,p-value小,相关性更强。

    Parch直系亲属:

    feature = 'Parch'
    feature_data = train_data.filter([feature, 'Survived'])
    survived_data = feature_data[feature][feature_data.Survived == 1].value_counts()
    unsurvived_data = feature_data[feature][feature_data.Survived == 0].value_counts()
    df = pd.DataFrame({'Survived': survived_data, 'UnSurvivied': unsurvived_data})
    df.plot(kind='bar', stacked=True)
    plt.title('Survived_' + feature)
    plt.xlabel(feature)
    plt.ylabel(u'Number of people')
    plt.show()
    
    feature_data.groupby(feature).hist()
    
    直系亲属 旁系亲属

    由上图可知不同取值情况下,Survived和Unsurvived概率不同,Parch/Sib和Survived是具有相关性的。在有3个旁系亲属或者3个直系情况下生还概率会急剧下降,但是由于数据有限,可以使用多亲属来作为新特征。

    2.2.1.2 离散值分析
    上面在分析Parch和SibSp时,已经进行了离散值分析。同理将Sex、Pclass、Embarked也分析一下。

    for column in discrete_train_data.columns:
        print discrete_train_data[column].value_counts()
    
    离散值
    总体情况
    不存在skewed data的情况。
    相关性分析
    from sklearn.feature_selection import chi2
    from sklearn.preprocessing import LabelEncoder
    sex_label_data = LabelBinarizer().fit_transform(train_data['Sex'])
    embarked_label_data = LabelEncoder().fit_transform(train_data['Embarked'].fillna('S'))
    print "Embarked", chi2(pd.DataFrame(embarked_label_data), train_data['Survived'])
    print "Sex:", chi2(sex_label_data, train_data['Survived'])
    print "Pclass:", chi2(train_data.filter(["Pclass"]), train_data['Survived'])
    

    输出
    Embarked (array([ 10.20252466]), array([ 0.00140249]))
    Sex: (array([ 92.70244698]), array([ 6.07783826e-22]))
    Pclass: (array([ 30.87369944]), array([ 2.75378563e-08]))

    由此可见:Sex以及Pclass的相关性特别强。Embarked需要进一步分析。

    def print_stacked_hist(feature):
        feature_data = train_data.filter([feature, 'Survived'])
        survived_data = feature_data[feature][feature_data.Survived == 1].value_counts()
        unsurvived_data = feature_data[feature][feature_data.Survived == 0].value_counts()
        df = pd.DataFrame({'Survived': survived_data, 'UnSurvivied': unsurvived_data})
        df.plot(kind='bar', stacked=True)
        plt.title('Survived_' + feature)
        plt.xlabel(feature)
        plt.ylabel(u'Number of people')
        plt.show()
    
    print_stacked_hist('Sex')
    print_stacked_hist('Pclass')
    print_stacked_hist('Embarked')
    
    柱状图

    通过柱状图可以,女性和富人的存活率会更高。不同港口也存在差异,但是不是很明显。可以考虑去除。

    三、数据预处理

    3.1 基础操作
    缺失值
    缺失值处理方法:

    1. 拟合:缺失个数不多时,并且有背景特征可以使用时。
    2. 填充:缺失数据不多,而且数据可以离散化数据(连续数据考虑step为步长离散化),直接填充NaN新类型或者使用均值、中值。
    3. 0/1化:当是否有记录本身与否就有意义时,可以考虑将是否有数据作为一个特征。例如可能存在逃票等。
    4. 丢弃:缺失数据过多时为避免引入噪声,可以直接丢弃。

    数据具体处理过程

    • Age: 年龄缺失1/4,由于特征和年龄大多没有关系,采取Step(采用5年,由于数据量不大,避免过于分散)离散化+填充NaN新类型的方法。
    • 客舱Cabin: 缺失3/4,因为船舱可能和身份有关,所以直接使用0/1化
    • 登录港口Embarked:登录港口缺失少量数据,直接填充为最常见类型。
    filled_data = train_data.copy()
    # transform Age
    filled_data.loc[np.isnan(train_data['Age']), 'Age'] = 0
    def transform_category(data, start, step, category):
        """
        data是一个array数据
        """
        result = ((data - start) / step).astype(int) + category
        return result
    
    step = 5
    filled_data['Age'] = transform_category(filled_data['Age'], 0, step, 0)
    
    filled_data.loc[filled_data['Cabin'].notnull(), 'Cabin'] = 1
    filled_data.loc[filled_data['Cabin'].isnull(), 'Cabin'] = 0
    
    def get_most_common_category(series):
        return series.value_counts().axes[0][0]
    
    most_common = get_most_common_category(filled_data['Embarked'])
    filled_data.loc[filled_data['Embarked'].isnull(), 'Embarked'] = most_common
    
    缺失值处理后结果

    通过上面的处理后,所有数据参数完整。

    离散化
    采样数据和真实数据存在差距。离散化处理好处是可以避免数据分布不均匀的情况,减少误差,坏处是丢失了部分精度。由于Titanic数据量少,所以将年龄离散化处理。

    Dummy Coding
    部分类目数据,本身是无序的,例如登录港口等,如果作为有序的离散值处理,对后续的距离计算造成误导,例如类别1和类别3距离为2,但是类别1和类别2距离为1。所以使用(1, 0, 0)、(0, 1, 0)和(0,0,1)来存储数据,称作虚拟编码dummy coding。

    • Sex\Embarked\Cabin进行dummy coding
    • Pclass本身有序的,暂时不进行dummy coding,当然等距离的假设可能不当,后期可以尝试使用dummy coding调整
    dummy_cabin = pd.get_dummies(new_data["Cabin"], prefix="Cabin")
    dummy_sex = pd.get_dummies(new_data['Sex'], prefix='Sex')
    dummy_embarked = pd.get_dummies(new_data['Embarked'], prefix='Embarked')
    
    dummied_data = pd.concat([new_data, dummy_cabin, dummy_sex, dummy_embarked], axis=1)
    dummied_data.drop(['Cabin', 'Sex', 'Embarked'], axis=1, inplace=True)
    dummied_data.head()
    
    Dummy_Coding

    标准化
    根据Ng老师的梯度梯度下降等高线可知,Feature Scaling影响收敛速度。

    Feature Scaling
    • 将Fare数据进行归一化
    from sklearn.preprocessing import StandardScaler
    dummied_data['Fare'] = StandardScaler().fit_transform(dummied_data.filter(['Fare']))
    

    3.2 数据清洗
    异常值处理主要用于非监督学习中,空间分布奇异点。

    3.3 经验处理
    Ticket和Name字段为文本字段,需要经验处理。
    搜罗了一下一些博客。

    • Name中可以提取到性别信息Mrs、Mr,可以用于校验Sex正确性。
    • Name中有‘Capt’、‘Don'等可以作为Title字段
    • Ticket可以统计出类别,Titanic中类别过多,暂不使用。

    综上,基础模型,暂时去除PassengerId、Ticket和Name字段

    unsed_column = ['PassengerId', 'Name', 'Ticket']
    target_prepared_y = dummied_data['Survived']
    train_prepared_data = dummied_data.drop(unsed_column + ['Survived'], axis=1)
    
    四、模型印象

    先构建具有解释性的基础模型,例如LogisticRegression和DecisionTree。

    LogisticRegression模型可以提供各特征的线性相关性分析。使用CV得到各特征的相关性如下。

    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import cross_val_score
    from sklearn.model_selection import KFold
    from sklearn import cross_validation, metrics
    def modelfit(alg, X, Y, performCV=True, printFeatureImportance=True, cv_folds=5):
        #Fit the algorithm on the data
        #alg.fit(X, Y)
            
        #Predict training set:
        dtrain_predictions = alg.predict(X)
        dtrain_predprob = alg.predict_proba(X)[:,1]
        
        #Perform cross-validation:
        if performCV:
            cv_score = cross_validation.cross_val_score(alg, X, Y, cv=cv_folds, scoring='roc_auc')
        
        #Print model report:
        print("\nModel Report")
        print("Accuracy : %.4g" % metrics.accuracy_score(Y.values, dtrain_predictions))
        print("AUC Score (Train): %f" % metrics.roc_auc_score(Y, dtrain_predprob))
        
        if performCV:
            print("CV Score : Mean - %.7g | Std - %.7g | Min - %.7g | Max - %.7g" % (np.mean(cv_score),np.std(cv_score),np.min(cv_score),np.max(cv_score)))
            
        #Print Feature Importance:
        if printFeatureImportance:
            feat_imp = pd.Series(alg.feature_importances_).sort_values(ascending=False)
            feat_imp.plot(kind='bar', title='Feature Importances')
            plt.ylabel('Feature Importance Score')
    def train_model(model_class, print_coef=False, *args, **kwargs):
        kf = KFold(n_splits=10)
        best_lr = None
        best_score = 0
        for train_index, test_index in kf.split(train_prepared_data):
            train_sub_data, target_sub_data = train_prepared_data.loc[train_index], target_prepared_y.loc[train_index]
            test_sub_data, test_target_sub_data = train_prepared_data.loc[test_index], target_prepared_y.loc[test_index]
            lr = model_class(*args, **kwargs)
            lr.fit(train_sub_data, target_sub_data)
            score = lr.score(test_sub_data, test_target_sub_data)
            if score > best_score:
                best_lr = lr
                best_score = score
    
        print best_score
        print best_lr
    
        modelfit(best_lr, train_prepared_data, target_prepared_y, printFeatureImportance=False)
        if print_coef:
            columns = list(train_prepared_data.columns)
            plot_df = pd.DataFrame(best_lr.coef_.ravel(), index=columns)
            plot_df.plot(kind='bar')
        return best_lr
    
    train_model(LogisticRegression, print_coef=True)
    
    逻辑回归
    • Pclass: 负相关。即class 1的人生存可能性更高。
    • Age:负相关。年龄越小越容易存活,但是特征作用不明显。
    • SibSp和Parch均为负相关:作用不明显。
    • Fare为正相关:作用不明显。
    • Cabin_1为正相关:有舱位的信息明显,不符合常识。
    • Female为正相关,Male为负相关。
    • C、Q、S港口均为正相关,且高于年龄等字段。

    决策树分类边界可以更加复杂,兼顾多区间字段。
    逻辑回归中Age字段的分类边界无法同时考虑到儿童和老人。使用决策树的分类边界可能可以兼顾儿童和老人。
    决策树在数据不均匀时,容易过拟合。

    from sklearn.tree import DecisionTreeClassifier
    model = train_model(DecisionTreeClassifier, max_depth=5)
    
    from IPython.display import Image
    from sklearn import tree
    import pydotplus
    dot_data = tree.export_graphviz(model, out_file=None, 
                             feature_names=list(train_prepared_data.columns), 
                             class_names=['UnSurvived', 'Survived'],
                             filled=True, rounded=True,  
                             special_characters=True)  
    graph = pydotplus.graph_from_dot_data(dot_data)  
    Image(graph.create_png())
    
    决策树模型 决策图
    • 由上图的Survived颜色可知,Female Survived的可能性极大。
    • 左分支Male在年龄Age较小财富Fare较少的一个分支上有大量存活。
    • 右分支Female在高等级Pclass情况下容易存活。
    • 右分支Embarked_S港口登船且财富Fare不少容易存活。
    • 有分支Embarked_S港口登船且Age较小容易存活。

    由此可知:

    • Sex和Pclass均有较大的影响。
    • Age、Fare和Embarked_S影响比较大。

    比较线性回归和决策树发现:

    • Sex和Pclass均是正相关作用。
    • Age字段适合在决策树中发挥作用。
    • 决策树容易过拟合。忽视大量特征。

    提交kaggle结果
    线性回归结果:
    0.74163

    决策树结果:
    0.73684

    综上:baseline模型完成,数据进行了初步的处理,得到了一些解释现象。下面通过特征工程,并分析错误用例来优化系统。

    五、特征选择与特征学习

    根据之前的数据分析和baseline模型,关于特征可以有下面的总结。

    • 高质量特征包括Sex和Pclass,可以作为特征空间搜索的起始区间。两个特征在决策树和Logistic中均占有较高的信息熵和权重; 同时两者和Survived的卡方校验值高。

    • 潜力特征Age和Fare:两者在决策树和Logistic中均有不可忽视的影响,但是相对Sex和Pclass影响小;根据之前的数据分析可知,两者分布不均匀,提取新字段:老人、儿童、富人

    • Embarked字段:此字段在两个模型中的表现存在较大的差异,同时根据之前的风险,它的相关性一般。可以尝试去除它。

    • SibSp和Parch字段:表现不明显,根据其分布可知,不均匀。可以考虑将两者结合在一块生成新的字段或者bucket。根据分布数据可知,Parch大部分为0,可以0/1处理。

    生成新特征

    child_age = 15 / 5
    old_man_age = 50 / 5
    
    (train_data['Fare'] > 80).value_counts()
    rich_fare = 1
    def add_new_features(df):
        df['Child'] = df['Age'].apply(lambda x: 1 if 0 < x<= child_age else 0)
        df['Senior'] = df['Age'].apply(lambda x: 1 if x >= old_man_age else 0)
        df['Rich'] = df['Fare'].apply(lambda x: 1 if x >= rich_fare else 0)
        df['FamilySize'] = df['SibSp'] + df['Parch']
        df['Single'] = df['Parch'].apply(lambda x: 1 if x == 0 else 0)
        return
    
    add_new_features(train_prepared_data)
    

    如上所示,生成了Child、Senior、Rich、FamilySize、Single字段。

    生成字段后提交结果:0.76077

    特征生成

    特征选择
    使用后向搜索,Sex、Pclass属性必需选项不进行筛选。

    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import cross_val_score
    saved_list = ['Sex', 'Pclass']
    def choose_feature_list(X, y, saved_list):
        left_columns = list(X.columns)
        lr = LogisticRegression()
        best_score = np.mean(cross_val_score(lr, X, y, cv=5))
        final_column_list = [x for x in left_columns]
        while len(left_columns) >= 3:
            new_item = None
            for item in left_columns:
                if item in saved_list:
                    continue
                
                lr = LogisticRegression()
                new_column = [x for x in left_columns]
                new_column.remove(item)
                score = np.mean(cross_val_score(lr, X.filter(new_column), y, cv=5))
                if score > best_score:
                    best_score = score
                    new_item = item
            
            if new_item is None:
                break
            
            final_column_list.remove(item)
            print "remove:", new_item, "best_score ever", best_score
            left_columns.remove(item)
    
    choose_feature_list(train_prepared_data, target_prepared_y, saved_list)
    

    需要去除Senior, Age

    后向搜索

    使用完全搜索:

    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import cross_val_score
    import re
    import itertools
    drop_str = 'Age|SibSp|Parch|Cabin_*|Embarked_*|Senior|Rich|Single'
    drop_list = [column for column in train_prepared_data.columns if re.match(drop_str, column)]
    print drop_list
    
    def choose_feature_list(X, y, drop_list):
        
        lr = LogisticRegression()
        best_score = np.mean(cross_val_score(lr, X, y, cv=5))
        final_drop_list = []
        
        cnt = 0
        for i in xrange(1, len(drop_list) + 1):
            drop = None
            for combination in itertools.combinations(drop_list, i):
                cnt += 1
                new_train_X = X.drop(list(combination), axis=1)
                
                lr = LogisticRegression()
                score = np.mean(cross_val_score(lr, new_train_X, y, cv=5))
                
                if score > best_score:
                    best_score = score
                    final_drop_list = combination
                    drop = True
                
                if cnt % 100 == 0:
                    print cnt
            if drop:
                print "drop_list:", final_drop_list, "best_score ever", best_score
    
    choose_feature_list(train_prepared_data, target_prepared_y, drop_list)
    

    需要去除的参数为
    'Age', 'SibSp', 'Parch', 'Cabin_0', 'Senior', 'Rich', 'Single'

    完全搜索

    结论:两者相差不大,而且在数据集不大的情况下倾向于使用完全搜索得到的数据。

    六、分析结果

    判断模型的拟合状态,使用准确率来绘制learning curve。

    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.learning_curve import learning_curve
    
    # 用sklearn的learning_curve得到training_score和cv_score,使用matplotlib画出learning curve
    def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, n_jobs=1, 
                            train_sizes=np.linspace(.05, 1., 20), verbose=0, plot=True):
        """
        画出data在某模型上的learning curve.
        参数解释
        ----------
        estimator : 你用的分类器。
        title : 表格的标题。
        X : 输入的feature,numpy类型
        y : 输入的target vector
        ylim : tuple格式的(ymin, ymax), 设定图像中纵坐标的最低点和最高点
        cv : 做cross-validation的时候,数据分成的份数,其中一份作为cv集,其余n-1份作为training(默认为3份)
        n_jobs : 并行的的任务数(默认1)
        """
        train_sizes, train_scores, test_scores = learning_curve(
            estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes, verbose=verbose)
    
        train_scores_mean = np.mean(train_scores, axis=1)
        train_scores_std = np.std(train_scores, axis=1)
        test_scores_mean = np.mean(test_scores, axis=1)
        test_scores_std = np.std(test_scores, axis=1)
    
        if plot:
            plt.figure()
            plt.title(title)
            if ylim is not None:
                plt.ylim(*ylim)
            plt.xlabel(u"sample n")
            plt.ylabel(u"score")
            plt.gca().invert_yaxis()
            plt.grid()
    
            plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, 
                             alpha=0.1, color="b")
            plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, 
                             alpha=0.1, color="r")
            plt.plot(train_sizes, train_scores_mean, 'o-', color="b", label=u"train")
            plt.plot(train_sizes, test_scores_mean, 'o-', color="r", label=u"cv")
    
            plt.legend(loc="best")
            plt.gca().invert_yaxis()
    
        midpoint = ((train_scores_mean[-1] + train_scores_std[-1]) + (test_scores_mean[-1] - test_scores_std[-1])) / 2
        diff = (train_scores_mean[-1] + train_scores_std[-1]) - (test_scores_mean[-1] - test_scores_std[-1])
        return midpoint, diff
    
    plot_learning_curve(lr, u"learning curve", new_prepared, target_prepared_y);
    
    Learning Curve

    由曲线可知,模型不处于过拟合状态,由于两者相差不大而且准确率不低。

    七、迭代问题

    Bagging
    使用bagging模型直接迭代之前的LR模型。

    from sklearn.ensemble import BaggingRegressor
    from sklearn.grid_search import GridSearchCV   #Perforing grid search
    clf = LogisticRegression()
    bagging_clf = BaggingRegressor(clf, n_estimators=20, max_samples=0.8)
    bagging_clf.fit(new_prepared, target_prepared_y)
    print bagging_clf.score(new_prepared, target_prepared_y)
    drop_data = dummied_prepard_data.drop(remove_feature_list, axis=1)
    result = model.predict(drop_data)
    result = model.predict(drop_data)
    passage_list = test_data['PassengerId']
    # print_result(passage_list, result)
    

    提交后结果0.77512,还是不错的。看下learning curve。
    不存在过拟合现象。


    Bagging

    Boost
    使用ensemble模型,GBDT进行学习。

    from sklearn.model_selection import cross_val_score
    from sklearn.ensemble import GradientBoostingClassifier
    gb_clf = GradientBoostingClassifier()
    new_prepared = train_prepared_data.drop(remove_feature_list, axis=1)
    model = train_model(new_prepared, target_prepared_y, GradientBoostingClassifier)
    
    drop_data = dummied_prepard_data.drop(remove_feature_list, axis=1)
    result = model.predict(drop_data)
    passage_list = test_data['PassengerId']
    print_result(passage_list, result)
    

    由图可知,得到的结果提交后,发现数据结果并不好。
    判断learning curve:


    GBDT

    发现存在过拟合现象,进行参数调整。

    from sklearn import cross_validation, metrics   #Additional scklearn functions
    from sklearn.grid_search import GridSearchCV   #Perforing grid search
    param_test = {
    #     'n_estimators': range(20, 10, 40),
        'min_samples_split':range(2, 20, 4), 
        'min_samples_leaf':range(1,10,2), 
        'learning_rate': (0.05, 0.2, 0.05), 
        'max_depth': range(6, 10, 2)
    }
    gv_search = GridSearchCV(estimator = GradientBoostingClassifier(n_estimators=5, max_features='sqrt', subsample=0.8, random_state=10), 
    param_grid = param_test, scoring='accuracy', iid=False, cv=5)
    gv_search.fit(new_prepared, target_prepared_y)
    print gv_search.best_score_
    # modelfit(gv_search.best_estimator_, new_prepared, target_prepared_y)
    

    得到结果:0.76555

    八、表述模型

    由于数据集有限,每个LR子学习器都是强学习器的情况下,集成学习最终的表现并不是太好。

    当前最佳模型是LR模型,模型去除了Senior, Age得到最佳结果。Sex和Pclass重要性很高。

    当前模型由于部分数据不太均匀,挖掘Fare和Age方面的意义不突出。FamilySize参数被接纳。可以考虑继续往Sex以及FamilySize方向继续特征学习和迭代。

    通过Titanic熟悉了数据分析的基本流程,过程特别多,希望总结一个分析框架,方便快速分析问题。下面是基本分析框架的重要内容,具体实现下次再具体说明。

    基础分析框架

    类型划分

    • 按label划分:监督学习、非监督学习、半监督学习和强化学习
    • 按数据来源划分:online learningbatch learning
    • 按输出模型划分: instance-based learningmodel-based learning

    主要挑战

    • 缺乏足够的数据。儿童识别苹果只需要少量的数据,机器识别图片需要上百万的数据。
    • 采样数据和真实数据有偏差。
    • 数据质量不佳。噪声、异常值。忽略特征或者拟合、补充数据。
    • 无用特征。需要筛选特征、构造新特征。

    模型评价

    • learning curve拟合程度:underfit or overfit
    • 交叉验证cv: 检测泛化能力同时避免浪费数据。有些模型最后将模型再跑一遍数据。
    • 评价标准和损失函数

    框架实现

    TODO: 具体框架实现和总结在下一篇说明。

    经验总结

    1. metric选择
    • 距离范式越高,越大的维度权重越高。越容易收到异常点的影响。

    参考内容

    1. Aurélien Géron. Hands-on machine learning with scikit-learn and tensorflow
    2. 特征选择和特征学习
    3. 机器学习系列(3)_逻辑回归应用之Kaggle泰坦尼克之灾
    4. 使用sklearn做单机特征工程
    5. 文本分类入门(十)特征选择算法之开方检验
    6. GDBT简介

    相关文章

      网友评论

        本文标题:机器学习笔记(1)-分析框架-以Kaggle Titanic问题

        本文链接:https://www.haomeiwen.com/subject/qzfctxtx.html