Kaggle Titanic 机器学习实践笔记(二)

在做完数据分析以后,先对测试集有个了解

test_data.describe()
Out[1]: 
       PassengerId      Pclass         Age       SibSp       Parch        Fare
count   418.000000  418.000000  332.000000  418.000000  418.000000  417.000000
mean   1100.500000    2.265550   30.272590    0.447368    0.392344   35.627188
std     120.810458    0.841838   14.181209    0.896760    0.981429   55.907576
min     892.000000    1.000000    0.170000    0.000000    0.000000    0.000000
25%     996.250000    1.000000   21.000000    0.000000    0.000000    7.895800
50%    1100.500000    3.000000   27.000000    0.000000    0.000000   14.454200
75%    1204.750000    3.000000   39.000000    1.000000    0.000000   31.500000
max    1309.000000    3.000000   76.000000    8.000000    9.000000  512.329200


test_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    418 non-null int64
Pclass         418 non-null int64
Name           418 non-null object
Sex            418 non-null object
Age            332 non-null float64
SibSp          418 non-null int64
Parch          418 non-null int64
Ticket         418 non-null object
Fare           417 non-null float64
Cabin          91 non-null object
Embarked       418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB


Cabin有大量缺失,Age有少量缺失,和训练集的情况很像

与训练集不同的是有一行数据的Fare字段为空,查看一下

test_data[test_data['Fare'].isnull()==True]
Out[2]: 
     PassengerId  Pclass                Name   Sex   Age  SibSp  Parch Ticket  \
152         1044       3  Storey, Mr. Thomas  male  60.5      0      0   3701   
     Fare Cabin Embarked  
152   NaN   NaN        S  

用同Pclass,同Sex,同Embarked的平均值填充:

test_data.loc[test_data['Fare'].isnull(),'Fare']=test_data[(test_data.Pclass==3)&(test_data.Sex=='male')&(test_data.Embarked=='S')].dropna().Fare.mean()#符合条件的就一行数据,fare是7.65
(注意是&不是and,因为是3个筛选条件并列,写and报错)



因为Fare的值域太大,需要对其进行归一化。

那么为什么值域太大的特征就需要对其进行归一化呢?这个答案很好:

http://www.open-open.com/lib/view/open1429697131932.html 

简单来说就是:1)归一化后加快了梯度下降求最优解的速度;2)归一化有可能提高精度”

import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
#Fare
fare_scale_param = scaler.fit(train_data['Fare'].values.reshape(-1, 1))
train_data['Fare'] = fare_scale_param.transform(train_data['Fare'].values.reshape(-1, 1))
test_data['Fare'] = fare_scale_param.transform(test_data['Fare'].values.reshape(-1, 1))





先做一个baseline model,全代码如下:

import  pandas as pd
import sklearn.preprocessing as preprocessing
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression

train_data=pd.read_csv('../data/train.csv')
test_data=pd.read_csv('../data/test.csv')


#归一化
scaler = preprocessing.StandardScaler()
fare_scale_param = scaler.fit(train_data['Fare'].values.reshape(-1, 1))


#以5岁为一个周期离散,同时10以下,60岁以上的年分别归类
def age_map(x):
    if x<10:
        return '10-'
    if x<60:
        return '%d-%d'%(x//5*5, x//5*5+5)
    elif x>=60:
        return '60+'
    else:
        return 'Null'





#对训练集的预处理
train_data['Embarked'].fillna('S',inplace=True)# 把训练集Embarked是空的那两行的Embarked填充为S
train_data['Fare'] = fare_scale_param.transform(train_data['Fare'].values.reshape(-1, 1))
#fillna( ) Fill NA/NaN values using the specified method
train_data['Age_map'] = train_data['Age'].apply(lambda x: age_map(x))
#将类别型变量全部One-hot编码,因为逻辑回归只识别数值型,不识别类别型
train_data['Cabin']=train_data['Cabin'].isnull().apply(lambda  x:'Null' if x is True else 'Not Null')
train_x = pd.concat([train_data[['SibSp','Parch','Fare']], pd.get_dummies(train_data[['Pclass','Sex','Cabin','Embarked','Age_map']])],axis=1)
train_y = train_data.Survived
#train_data.groupby('Age_map')['Survived'].agg(['count','mean']) 打印出来看看


#对测试集的预处理
test_data.loc[test_data['Fare'].isnull(),'Fare']=test_data[(test_data.Pclass==3)&(test_data.Sex=='male')&(test_data.Embarked=='S')].dropna().Fare.mean()#符合条件的就一行数据,fare是7.65
test_data['Fare'] = fare_scale_param.transform(test_data['Fare'].values.reshape(-1, 1))
#注意写&不能写and
test_data['Age_map'] = test_data['Age'].apply(lambda x: age_map(x))
test_data['Cabin']=test_data['Cabin'].isnull().apply(lambda  x:'Null' if x is True else 'Not Null')
test_x = pd.concat([test_data[['SibSp','Parch','Fare']], pd.get_dummies(test_data[['Pclass', 'Sex','Cabin','Embarked', 'Age_map']])],axis=1)


if __name__ == '__main__':

    base_line_model = LogisticRegression()
    param = {'penalty':['l1','l2'],
            'C':[0.1, 0.5, 1.0,5.0]}
    grd = GridSearchCV(estimator=base_line_model, param_grid=param, cv=5, n_jobs=3)
    grd.fit(train_x, train_y)
    gender_submission = pd.DataFrame({'PassengerId': test_data.iloc[:, 0], 'Survived': grd.predict(test_x)})
    gender_submission.to_csv('gender_submission.csv', index=None)
    # 模型优化:
    # 老老实实先把得到的model系数和feature关联起来看看。
    print(pd.DataFrame({"columns": list(train_x.columns)[:], "coef": list(grd.best_estimator_.coef_.T)}))

'''
grd.best_estimator_
Out[1]: 
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l1', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False)
'''




提交结果是6092名,0.77033分



pd.DataFrame({"columns": list(train_x.columns)[:], "coef": list(grd.best_estimator_.coef_.T)})
Out[11]: 
                 coef         columns
0   [-0.427371345792]           SibSp
1     [-0.2089718658]           Parch
2    [0.141110392011]            Fare
3   [-0.680177573933]          Pclass
4      [2.7955717529]      Sex_female
5               [0.0]        Sex_male
6    [0.653247015747]  Cabin_Not Null
7               [0.0]      Cabin_Null
8    [0.183840181375]      Embarked_C
9   [0.0100687020896]      Embarked_Q
10  [-0.245022175391]      Embarked_S
11    [2.19167169207]     Age_map_10-
12              [0.0]   Age_map_10-15
13   [0.114171699169]   Age_map_15-20
14              [0.0]   Age_map_20-25
15   [0.289951314871]   Age_map_25-30
16   [0.421529404794]   Age_map_30-35
17  [0.0374208659749]   Age_map_35-40
18              [0.0]   Age_map_40-45
19  [-0.185169993649]   Age_map_45-50
20              [0.0]   Age_map_50-55
21  [-0.749003768057]   Age_map_55-60
22  [-0.440792601238]     Age_map_60+
23              [0.0]    Age_map_Null

这个表coef为正代表与获救结果正相关,就是更容易获救;

coef为负代表与获救结果负相关,即更不容易获救。

可以观察出:

--小于10岁和女性明显更容易获救

--Cabin有值比Cabin无值更容易获救

--年轻力壮的比老年人容易获救

--S港口登录的更不容易获救


模型优化:

from sklearn.model_selection import cross_val_score
cross_val_score(grd.best_estimator_,train_x,train_y,cv=5)
[ 0.80446927  0.79329609  0.78089888  0.79213483  0.81920904]



  1. sklearn.cross_validation模块

cross validation大概的意思是:对于原始数据我们要将其一部分分为traindata,一部分分为test data。train data用于训练,test data用于测试准确率在test data上测试的结果叫做validation error。将一个算法作用于一个原始数据,我们不可能只做出随机的划分一次train和testdata,然后得到一个validation error,就作为衡量这个算法好坏的标准。因为这样存在偶然性。我们必须多次的随机的划分train data和test data,分别在其上面算出各自的validation error。这样就有一组validationerror,根据这一组validationerror,就可以较好的准确的衡量算法的好坏。crossvalidation是在数据量有限的情况下的非常好的一个evaluate performance的方法。而对原始数据划分出train data和testdata的方法有很多种,这也就造成了cross validation的方法有很多种。

  • 主要函数

sklearn中的cross validation模块,最主要的函数是如下函数:

sklearn.cross_validation.cross_val_score

调用形式是:sklearn.cross_validation.cross_val_score(estimator, X, y=None, scoring=None, cv=None,n_jobs=1, verbose=0, fit_params=None, pre_dispatch='2*n_jobs')

返回值就是对于每次不同的的划分raw data时,在test data上得到的分类的准确率。

  • 参数解释

estimator:是不同的分类器,可以是任何的分类器。比如支持向量机分类器:estimator = svm.SVC(kernel='linear', C=1)

cv:代表不同的cross validation的方法如果cv是一个int,并且如果提供了rawtarget参数,那么就代表使用StratifiedKFold分类方式如果cv是一个int值,并且没有提供rawtarget参数,那么就代表使用KFold分类方式也可以给定它一个CV迭代策略生成器,指定不同的CV方法

scoring:默认Nnoe准确率的算法可以通过score_func参数指定如果不指定的话,是用estimator默认自带的准确率算法。





交叉验证看看是不是过拟合了,画出学习曲线

    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(当cv=None时,默认为3份)
        n_jobs : 并行的的任务数(默认1)
        train_sizes:数组,表示用百分之多少的样本去训练。train_sizes=np.linspace(.05, 1., 20)就是依次用start=5%,,end=100%,分成20份(即20个点)去训练

        返回值:
        train_sizes:array, shape = (n_unique_ticks,), dtype int
        实际用于生成learning curve的训练集的样本数。由于重复的输入将会被删除,所以ticks可能会少于n_ticks.
        对于本例就是把总样本数*5%,一直到总样本数*100%,分成20份
        train_scores : array, shape= (n_ticks那么多行, n_cv_folds(即cv值)那么多列)
        在训练集上的分数
        test_scores : array, shape =(n_ticks行, n_cv_folds(即cv值)列)
        在交叉验证集上的分数
        """
        train_sizes, train_scores, test_scores = learning_curve(
            estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes, verbose=verbose,score)
        #print("train_sizes are")
        #print(train_sizes)
        #print("train_scores are")
        #print(train_scores)
        #print("test_scores are")
        #print(test_scores)
        #print("train_scores_mean")
        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)
        #print(train_scores_mean)
        #print("type of train_socres_mean is %s",type(train_scores_mean))
        #print(train_scores_mean[-1])
        if plot:
            plt.figure()
            plt.title(title)
            if ylim is not None:
                plt.ylim(*ylim)
            plt.xlabel(u"训练样本数")
            plt.ylabel(u"得分")
            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"训练集上得分")
            plt.plot(train_sizes, test_scores_mean, 'o-', color="r", label=u"交叉验证集上得分")

            plt.legend(loc="best")

            plt.draw()
            plt.show()
            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





如何读懂学习曲线:http://sklearn.lzjqsdd.com/modules/learning_curve.html#learning-curve

对过拟合而言,通常以下策略对结果优化是有用的:

  • 做一下feature selection,挑出较好的feature的subset来做training
  • 提供更多的数据,从而弥补原始数据的bias问题,学习到的model也会更准确

而对于欠拟合而言,我们通常需要更多的feature,更复杂的模型来提高准确度。

著名的learning curve可以帮我们判定我们的模型现在所处的状态。我们以样本数为横坐标,训练和交叉验证集上的错误率作为纵坐标,两种状态分别如下两张图所示:过拟合(overfitting/high variace),欠拟合(underfitting/high bias)




再说回本题,根据代码我读懂了:

--线两侧的阴影部分,是mean 加减 std所得,所以阴影上下幅度越宽表明当前训练样本数,参数cv那么多折进行cv那么多次交叉验证,每次得到的分数差异较大

   所以当上下幅度的阴影越收敛的时候,表明mean值(即点的值)越稳定越可信

--目前我们画的的曲线看来,我们的model并不处于overfitting的状态(overfitting的表现一般是训练集上得分高,而交叉验证集上要低很多,中间的gap比较大)。因此我们可以再做些feature engineering的工作,添加一些新产出的特征或者组合特征到模型中。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值