机器学习导论-KaggleTitanic视频及教程

来源:互联网 发布:淘宝网 无法购买 编辑:程序博客网 时间:2024/05/21 06:47
  1. 基本概念
  • 对历史数据的计算,对未来还没发生的事情进行预测
  • 训练集:训练模型

测试集:测试模型

验证集:选择参数

建立模型时怎么知道模型的好坏:评估标准

  • 核心:优化误差函数/代价函数Jtheta,目标是优化代价函数,使之达到最小
  • 评估标准:预测正确的/总预测的数目=准确率(很少用准确率衡量,一般都很高)

       一般两个指标:精确Precision,召回Recall

理解:正确率 = 提取出的正确信息条数 / 提取出的信息条数

 召回率 = 提取出的正确信息条数 /样本中的信息条数

 一般来说,Precision就是检索出来的条目(比如:文档、网页等)有多少是准确   的,Recall就是所有准确的条目有多少被检索出来了。例子:某池塘有1400条鲤鱼,300只虾,300只鳖。现在以捕鲤鱼为目的。撒一大网,逮着了700条鲤鱼,200只虾,100只鳖。那么,这些指标分别如下:

正确率 = 700 / (700 + 200 + 100) = 70%

召回率 = 700 / 1400 = 50%

可以看出,正确率是评估捕获的成果中目标成果所占得比例;召回率,顾名思义,就是从关注领域中,召回目标类别的比例

当然希望检索结果Precision越高越好,同时Recall也越高越好,但事实上这两者在某些情况下有矛盾的。比如极端情况下,我们只搜索出了一个结果,且是准确的,那么Precision就是100%,但是Recall就很低;而如果我们把所有结果都返回,那么比如Recall是100%,但是Precision就会很低。因此在不同的场合中需要自己判断希望Precision比较高或是Recall比较高。如果是做实验研究,可以绘制Precision-Recall曲线来帮助分析

(参考知乎:https://www.zhihu.com/question/19645541

 

  1. 机器学习基本过程
  • 数据探索:数据大小,数据分布,数据缺失情况
  • 特征工程:missingvalue,分类变量处理,标准化,原始变量处理
  • 建模:选择合适的模型,选择模型参数,选择评估模型的方式
  • 参数优化:交叉验证
  • 模型评估:recall,precision,roc

 

  1. KaggleTitanic实例
  1. 初探数据

df=pd.concat([train,test],axis=0,ignore_index=True)#这里是把两个数据集联合起来df.shape   df.info()      #看看每条属性分别由多少条记录df.dtypes  #看一下每一列的数据是什么类型的df['Age'].hist()   #看一下某一列的数据分布直方图df.isnull()df.isnull().sum()    #看一下每一列缺失值有多少个df['Cabin'].value_counts()    #缺失值最多的是cabin这一列,那么就看看分别有多少个df['Cabin'][df.Cabin.isnull()]='U0'    #缺失值的地方就赋值为U0df['Age'][df.Age.isnull()]=df['Age'].median()   #这一列用中位值填充pd.get_dummies(df['Embarked'])     #这一列内容为Q/S/Pfrom sklearn.preprocessing import StandardScaler    #数据量纲不同,因此要将数据标准化scaler=StandardScaler()df['Age_scaled']=scaler.fit_transform(df['Age'])    #名字里面可以反映出来性别,地位,富人(富人的存活率比较高)import re      #正则模块df['Names']=df['Name'].map(lambda s:len(re.split(' ',s)))       #得到数值型df['Title']=df['Name'].map(lambdax:re.compile(",(.*?)\.").findall(x)[0])      #这里只取第0个,也就是.前面的那个Df  #这里把Mr,Ms分出来了df['Deck']=df['Cabin'].map(lambdas:re.compile("[a-zA-Z]+").search(s).group()[0])   #group为正则里面的语法,取第0个df['Deck']=df['Cabin'].map(lambdax:re.compile("([0-9]+)").search(x).group())  #这里会报错有某一条记录只有字母没有数字,这样就选不出来,可以用try except函数跳过它。Cabin中C开头的存活率比较高

 

  1. 数据初步分析

看看每个/多个属性和最后的Survived之间有着什么样的关系,数值会看花眼,还是用统计图来表示。

import matplotlib.pyplotasplt fig = plt.figure() fig.set(alpha=0.2)# 设定图表颜色alpha参数

plt.subplot2grid((2,3),(0,0))# 在一张大图里分列几个小图

data_train.Survived.value_counts().plot(kind='bar')# 柱状图

plt.title(u"获救情况 (1为获救)")#标题

plt.ylabel(u"人数")

 

猜测:

  • 不同舱位/乘客等级可能和财富/地位有关系,最后获救概率可能会不一样
  • 年龄对获救概率也一定是有影响的,毕竟前面说了,副船长还说『小孩和女士先走』呢
  • 和登船港口是不是有关系呢?也许登船港口不同,人的出身地位不同?

 

#看看各乘客等级的获救情况

fig= plt.figure()

fig.set(alpha=0.2)# 设定图表颜色alpha参数

Survived_0= data_train.Pclass[data_train.Survived ==0].value_counts()

Survived_1= data_train.Pclass[data_train.Survived ==1].value_counts()

df=pd.DataFrame({u'获救':Survived_1,u'未获救':Survived_0})

df.plot(kind='bar', stacked=True)

 

#看看各性别的获救情况

fig= plt.figure()

fig.set(alpha=0.2)# 设定图表颜色alpha参数

Survived_m= data_train.Survived[data_train.Sex =='male'].value_counts()

Survived_f= data_train.Survived[data_train.Sex =='female'].value_counts()

df=pd.DataFrame({u'男性':Survived_m,u'女性':Survived_f})

df.plot(kind='bar', stacked=True)

 

#看看各种舱级别情况下各性别的获救情况

fig=plt.figure()

fig.set(alpha=0.65)# 设置图像透明度,无所谓

plt.title(u"根据舱等级和性别的获救情况")

ax1=fig.add_subplot(141)#一行,四个子图,第一个图

data_train.Survived[data_train.Sex=='female'][data_train.Pclass !=3].value_counts().plot(kind='bar', label="femalehighclass", color='#FA2479')

ax1.set_xticklabels([u"获救",u"未获救"],rotation=0)

ax1.legend([u"女性/高级舱"], loc='best')

ax2=fig.add_subplot(142, sharey=ax1)data_train.Survived[data_train.Sex =='female'][data_train.Pclass==3].value_counts().plot(kind='bar', label='female,low class', color='pink')

ax2.set_xticklabels([u"未获救",u"获救"],rotation=0) plt.legend([u"女性/低级舱"], loc='best')

 

#看看各登船港口的获救情况

#看看 堂兄弟/妹,孩子/父母有几人,对是否获救的影响

g= data_train.groupby(['SibSp','Survived'])

df= pd.DataFrame(g.count()['PassengerId'])print df

#看看Cabin先把Cabin缺失与否作为条件(虽然这部分信息缺失可能并非未登记,maybe只是丢失了而已,所以这样做未必妥当),先在有无Cabin信息这个粗粒度上看看Survived的情况

fig= plt.figure() fig.set(alpha=0.2)# 设定图表颜色alpha参数

Survived_cabin= data_train.Survived[pd.notnull(data_train.Cabin)].value_counts()

Survived_nocabin= data_train.Survived[pd.isnull(data_train.Cabin)].value_counts()df=pd.DataFrame({u'有':Survived_cabin,u'无':Survived_nocabin}).transpose()

df.plot(kind='bar', stacked=True)

 

  1. 简单数据预处理(特征工程)

#先说Cabin,暂时我们就按照刚才说的,按Cabin有无数据,将这个属性处理成Yes和No两种类型

#再说Age:通常遇到缺值的情况,我们会有几种常见的处理方式

  • 如果缺值的样本占总数比例极高,我们可能就直接舍弃了,作为特征加入的话,可能反倒带入noise,影响最后的结果了
  • 如果缺值的样本适中,而该属性非连续值特征属性(比如说类目属性),那就把NaN作为一个新类别,加到类别特征中
  • 如果缺值的样本适中,而该属性为连续值特征属性,有时候我们会考虑给定一个step(比如这里的age,我们可以考虑每隔2/3岁为一个步长),然后把它离散化,之后把NaN作为一个type加到属性类目中。
  • 有些情况下,缺失的值个数并不是特别多,那我们也可以试着根据已有的值,拟合一下数据,补充上。

 

#本例中,后两种处理方式应该都是可行的,我们先试试拟合补全吧(虽然说没有特别多的背景可供我们拟合,这不一定是一个多么好的选择。

这里用scikit-learn中的RandomForest来拟合一下缺失的年龄数据

from sklearn.ensembleimportRandomForestRegressor### 使用 RandomForestClassifier填补缺失的年龄属性

def set_missing_ages(df):# 把已有的数值型特征取出来丢进Random Forest Regressor中

age_df= df[['Age','Fare','Parch','SibSp','Pclass']]#乘客分成已知年龄和未知年龄两部分

known_age= age_df[age_df.Age.notnull()].as_matrix()

unknown_age= age_df[age_df.Age.isnull()].as_matrix()# y即目标年龄

y =known_age[:,0]#X即特征属性值

X =known_age[:,1:]#fit到RandomForestRegressor之中

rfr= RandomForestRegressor(random_state=0,n_estimators=2000, n_jobs=-1)

rfr.fit(X,y)# 用得到的模型进行未知年龄结果预测

predictedAges= rfr.predict(unknown_age[:,1::])# 用得到的预测结果填补原缺失数据

df.loc[(df.Age.isnull()),'Age' ] = predictedAges

return df, rfr

 

def set_Cabin_type(df):

df.loc[(df.Cabin.notnull()),'Cabin' ] ="Yes"

df.loc[(df.Cabin.isnull()),'Cabin' ] ="No"

return df

 

data_train,rfr = set_missing_ages(data_train) data_train = set_Cabin_type(data_train)

 

#因为逻辑回归建模时,需要输入的特征都是数值型特征,我们通常会先对类目型的特征因子化

使用pandas的”get_dummies”来完成这个工作,并拼接在原来的”data_train”之上

dummies_Cabin=pd.get_dummies(data_train['Cabin'],prefix='Cabin')

dummies_Embarked= pd.get_dummies(data_train['Embarked'],prefix='Embarked')

dummies_Sex= pd.get_dummies(data_train['Sex'], prefix='Sex')

dummies_Pclass= pd.get_dummies(data_train['Pclass'],prefix='Pclass')

df=pd.concat([data_train, dummies_Cabin,dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1)

df.drop(['Pclass','Name','Sex','Ticket','Cabin','Embarked'],axis=1, inplace=True)

 

#Age和Fare两个属性,乘客的数值幅度变化太大,各属性值之间scale差距太大,将对收敛速度造成损害,甚至不收敛。所以我们先用scikit-learn里面的preprocessing模块做一个scaling所谓scaling,其实就是将一些变化幅度较大的特征化到[-1,1]之内

import sklearn.preprocessingas preprocessing scaler =preprocessing.StandardScaler()

age_scale_param= scaler.fit(df['Age'])

df['Age_scaled'] = scaler.fit_transform(df['Age'], age_scale_param)

fare_scale_param= scaler.fit(df['Fare'])

df['Fare_scaled'] = scaler.fit_transform(df['Fare'], fare_scale_param)

 

  1. 逻辑回归建模

#把需要的feature字段取出来,转成numpy格式,使用scikit-learn中的LogisticRegression建模。

from sklearnimportlinear_model# 用正则取出我们要的属性值

train_df=df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')

train_np= train_df.as_matrix()# y即Survival结果

y= train_np[:,0]#X即特征属性值

X= train_np[:,1:]#fit到RandomForestRegressor之中

clf= linear_model.LogisticRegression(C=1.0,penalty='l1', tol=1e-6)

clf.fit(X,y)

 

#”test_data”也要做和”train_data”一样的预处理。

 

#预测取结果

test= df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')

predictions = clf.predict(test)

result= pd.DataFrame({'PassengerId':data_test['PassengerId'].as_matrix(),'Survived':predictions.astype(np.int32)})result.to_csv("/Users/Hanxiaoyang/Titanic_data/logistic_regression_predictions.csv",index=False)

 

  1. 逻辑回归系统优化
  • 模型是过/欠拟合?以确定需要更多的特征还是更多数据,或者其他操作。这里要用到learning curves
  • 思考:

首先,Name和Ticket两个属性被我们完整舍弃了(好吧,其实是因为这俩属性,几乎每一条记录都是一个完全不同的值,我们并没有找到很直接的处理方式)。

然后,我们想想,年龄的拟合本身也未必是一件非常靠谱的事情,我们依据其余属性,其实并不能很好地拟合预测出未知的年龄。再一个,以我们的日常经验,小盆友和老人可能得到的照顾会多一些,这样看的话,年龄作为一个连续值,给一个固定的系数,应该和年龄是一个正相关或者负相关,似乎体现不出两头受照顾的实际情况,所以,说不定我们把年龄离散化,按区段分作类别属性会更合适一些。

 

#把得到的model系数和feature关联起来看看

pd.DataFrame({"columns":list(train_df.columns)[1:],"coef":list(clf.coef_.T)})

 

#我们先看看那些权重绝对值非常大的feature,在我们的模型上:

  • Sex属性,如果是female会极大提高最后获救的概率,而male会很大程度拉低这个概率。
  • Pclass属性,1等舱乘客最后获救的概率会上升,而乘客等级为3会极大地拉低这个概率。
  • 有Cabin值会很大程度拉升最后获救概率(这里似乎能看到了一点端倪,事实上从最上面的有无Cabin记录的Survived分布图上看出,即使有Cabin记录的乘客也有一部分遇难了,估计这个属性上我们挖掘还不够)
  • Age是一个负相关,意味着在我们的模型里,年龄越小,越有获救的优先权(还得回原数据看看这个是否合理
  • 有一个登船港口S会很大程度拉低获救的概率,另外俩港口压根就没啥作用(这个实际上非常奇怪,因为我们从之前的统计图上并没有看到S港口的获救率非常低,所以也许可以考虑把登船港口这个feature去掉试试)。
  • 船票Fare有小幅度的正相关(并不意味着这个feature作用不大,有可能是我们细化的程度还不够,举个例子,说不定我们得对它离散化,再分至各个乘客等级上?)

 

#因为test.csv里面并没有Survived这个字段,我们无法在这份数据上评定我们算法在该场景下的效果,因此要用到交叉验证cross_validation

#通常情况下,这么做crossvalidation:把train.csv分成两部分,一部分用于训练我们需要的模型,另外一部分数据上看我们预测算法的效果。

#先简单看看crossvalidation情况下的打分

from sklearnimportcross_validation#简单看看打分情况

clf= linear_model.LogisticRegression(C=1.0,penalty='l1', tol=1e-6)

all_data= df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')

X= all_data.as_matrix()[:,1:]

y= all_data.as_matrix()[:,0]

print cross_validation.cross_val_score(clf, X, y,cv=5)

 

#既然我们要做交叉验证,那我们干脆先把交叉验证里面的bad case拿出来看看

#下面做数据分割,并且在原始数据集上瞄一眼bad case

# 分割数据,按照 训练数据:cv数据 = 7:3的比例

split_train,split_cv = cross_validation.train_test_split(df, test_size=0.3, random_state=0)

train_df= split_train.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')# 生成模型

clf= linear_model.LogisticRegression(C=1.0,penalty='l1', tol=1e-6)

clf.fit(train_df.as_matrix()[:,1:], train_df.as_matrix()[:,0])# 对crossvalidation数据进行预测

cv_df= split_cv.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')predictions = clf.predict(cv_df.as_matrix()[:,1:])

origin_data_train= pd.read_csv("/Users/HanXiaoyang/Titanic_data/Train.csv")

bad_cases= origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions!= cv_df.as_matrix()[:,0]]['PassengerId'].values)]

 

我们随便列一些可能可以做的优化操作

  • Age属性不使用现在的拟合方式,而是根据名称中的『Mr』『Mrs』『Miss』等的平均值进行填充。
  • Age不做成一个连续值属性,而是使用一个步长进行离散化,变成离散的类目feature。
  • Cabin再细化一些,对于有记录的Cabin属性,我们将其分为前面的字母部分(我猜是位置和船层之类的信息) 和 后面的数字部分(应该是房间号,有意思的事情是,如果你仔细看看原始数据,你会发现,这个值大的情况下,似乎获救的可能性高一些)。
  • Pclass和Sex俩太重要了,我们试着用它们去组出一个组合属性来试试,这也是另外一种程度的细化。
  • 单加一个Child字段,Age<=12的,设为1,其余为0(你去看看数据,确实小盆友优先程度很高啊)
  • 如果名字里面有『Mrs』,而Parch>1的,我们猜测她可能是一个母亲,应该获救的概率也会提高,因此可以多加一个Mother字段,此种情况下设为1,其余情况下设为0
  • 登船港口可以考虑先去掉试试(Q和C本来就没权重,S有点诡异)
  • 把堂兄弟/兄妹 和 Parch 还有自己 个数加在一起组一个Family_size字段(考虑到大家族可能对最后的结果有影响)
  • Name是一个我们一直没有触碰的属性,我们可以做一些简单的处理,比如说男性中带某些字眼的(‘Capt’, ‘Don’, ‘Major’, ‘Sir’)可以统一到一个Title,女性也一样。

#learning curves

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

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

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

 

  1. 模型融合(model ensemble)

 

  • 每次取训练集的一个subset,做训练,这样,我们虽然用的是同一个机器学习算法,但是得到的模型却是不一样的;同时,因为我们没有任何一份子数据集是全的,因此即使出现过拟合,也是在子训练集上出现过拟合,而不是全体数据上,这样做一个融合,可能对最后的结果有一定的帮助。对,这就是常用的Bagging

 

  1. 总结
  • 用机器学习解决问题的过程大概如下图所示


 

 

 

0 0
原创粉丝点击