赞
踩
#前言
Kaggle上的泰坦尼克项目,对于初学机器学习的朋友来说,是一个很好的练手机会,能大概了解整个机器学习的全过程。下面我把自己做这个项目的经验分享一下,希望对想了解机器学习的朋友有所帮助,里面结合很多前辈的智慧,大家可以参考最后的链接。
在文章最开头,我分享一下实际应用项目和竞赛项目的区别,实际应用中,训练的样本相对于实际的数据集总是少的,而它的评分标准,是以实际的数据集的准确度作为参考的,而在竞赛项目中,用于检验结果的测试集相对于训练集是偏少的,但是竞赛的评估结果,是以测试集的匹配度作为依据的,所以有可能会出现实际应用中比较好的模型,但是在竞赛项目中,因为测试集特有的偏差,而导致竞赛的结果并不好,这是很正常的现象。而泰坦尼克号这个项目,就存在这个问题,因为我们最后的标准是那个测试集,所以要达到最好的竞赛结果,需要去迎合那个测试集的分布特征。因为这点差异,所以竞赛项目中要想获得好名次,所要采取的战略是不一样的,泰坦尼克号适合你熟悉整个的机器学习流程,但是对于要想获得好的结果,就需要不断的去调整特征工程,模型和超参数,而且最关键的是要有一个参考点,类似于A/B测试,发现哪些特征适用于这个测试集,不能盲目的尝试。
一般的机器学习分为以下几个步骤:
1、定义问题
2、导入数据
3、理解分析数据
4、清洗转换数据
5、特征工程
6、模型选择及超参数调优
7、模型融合
8、获得问题的答案
在这几个步骤中,有一些操作是会反复出现的,比如说交叉验证,网格搜索,特征选择,管道(算法链),评估指标等等。我会在具体的步骤中解释。
对于一个具体的项目,如何去评估这个项目的优劣很重要,因为每一个项目都是基于真实场景的,我举一个简单的例子,比如说测试癌症的患病率,如果没有患癌症,却被误断出来癌症,对于病人的损失可能是多做几次治疗和所花的费用,这种情况的评估指标就是测准度(precision),也就是预测的结果中,实际存在不正确的例子。另外一种情况,如果一个人患癌了,但是却检测出来没患病,那对于患者的损失可能就是生命了,衡量这种情况的指标,就是测全度(recall)。回到泰坦尼克号的例子,我们判别的指标其实相对比较简单,就是判断预测出来的存活情况是否正确,不存在说,实际是存活了,我们却预测他遇难了,会带来什么实质的影响,这个就叫做精度。当然,精度对于相对平衡(0:1在0.5~2之间)的二分类数据集还是很好的指标,但是对于不平衡的数据集,那么精度就不是很好的度量了,这个时候要用到f-分数。只是给大家开一个头,具体的信息,大家需要到相关统计学或者机器学习的书籍中查找。
对于泰坦尼克号这个项目,我们的指标选择精度就够了。
那么有哪些因素是影响最后的指标的呢,主要是以下四个方面:
1、特征工程的质量
2、样本的数量
3、模型的类型和结合方式
4、模型的超参数
其实就是上面8个步骤的5-7。
下面我就从上面讲的8个步骤,4个要点,来详细演示一下这个项目。
#1、定义问题
这个项目的目的,是预测测试集中人员是否生还,也就是肯定当作二元分类的预测问题,因为是相对平衡的数据集,可以用精度作为衡量指标,而且生还和遇难并没有实质的区别(在数据层面上),所以不考虑准确度和召回率。
简单点说,就是利用训练数据集,训练模型,然后用测试数据集预测模型的精度。
#2、导入数据
该项目的数据源可以在这个链接下载,点这里下载项目数据
##导入需要用到的模块(我写这篇分享,是基于一个脚本写的,所以会把模块一次性全部导入,这样的好处是,所有的过程复现程度很快,实际挖掘过程中,刚开始是探索型的,很多子模块是根据需求加进去的) #基础模块 import pandas as pd import seaborn as sns import matplotlib.pyplot as plt import numpy as np import warnings warnings.filterwarnings('ignore') #模型预处理 from sklearn.preprocessing import StandardScaler from sklearn.preprocessing import LabelEncoder from sklearn.pipeline import Pipeline from sklearn.feature_selection import RFECV from sklearn.model_selection import KFold from sklearn.model_selection import cross_val_score from sklearn.model_selection import GridSearchCV #回归模型模块 from sklearn.ensemble import GradientBoostingRegressor from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor #分类模型模块 from sklearn.linear_model import LogisticRegression from sklearn.linear_model import LogisticRegressionCV from sklearn.linear_model import Perceptron from sklearn.linear_model import SGDClassifier from sklearn.linear_model import PassiveAggressiveClassifier from sklearn.neighbors import KNeighborsClassifier from sklearn.svm import SVC, LinearSVC from sklearn.gaussian_process import GaussianProcessClassifier from sklearn.naive_bayes import GaussianNB from sklearn.naive_bayes import BernoulliNB from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import AdaBoostClassifier from sklearn.ensemble import BaggingClassifier from sklearn.ensemble import ExtraTreesClassifier from sklearn.ensemble import GradientBoostingClassifier from sklearn.ensemble import RandomForestClassifier ##导入数据 train_raw=pd.read_csv('train.csv') test_raw=pd.read_csv('test.csv') #创建新的整体列表 alldata=pd.concat([train_raw,test_raw],ignore_index=True)
#3、理解分析数据
理解分析数据包括两个方面:
1、检查数据是否有缺失,异常,大体的描述统计情况
2、理解数据在实际场景中的含义
##info()和describe()两个方法是对数据大体了解的不错选择,还可以用head(),tail(),或者loc,都是不错的方式 train_raw.info() <class 'pandas.core.frame.DataFrame'> RangeIndex: 891 entries, 0 to 890 Data columns (total 12 columns): PassengerId 891 non-null int64 Survived 891 non-null int64 Pclass 891 non-null int64 Name 891 non-null object Sex 891 non-null object Age 714 non-null float64 SibSp 891 non-null int64 Parch 891 non-null int64 Ticket 891 non-null object Fare 891 non-null float64 Cabin 204 non-null object Embarked 889 non-null object dtypes: float64(2), int64(5), object(5) memory usage: 83.6+ KB test_raw.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 ##还可以用isnull直接得到所有的缺失值,这种方式比较容易发现缺失值,方式其实很多啦,大家可以多试试。 train_raw.isnull().sum(0) Out[23]: PassengerId 0 Survived 0 Pclass 0 Name 0 Sex 0 Age 177 SibSp 0 Parch 0 Ticket 0 Fare 0 Cabin 687 Embarked 2 dtype: int64 test_raw.isnull().sum(0) Out[24]: PassengerId 0 Pclass 0 Name 0 Sex 0 Age 86 SibSp 0 Parch 0 Ticket 0 Fare 1 Cabin 327 Embarked 0 dtype: int64
接下来我们来讲讲,各数据的真实场景含义。
PassengerId:客户的编号,可以当作SQL中的键值,没有实际的含义;
Pclass:船舱的等级,一般和票价是成正比的,但是我发现不少船票价格是0英镑的乘客,可能还有一些其他的因素;
Name:乘客的姓名,最基本的就是姓名加一个称呼;
Sex:性别;
Age:乘客的年龄;
SibSp:乘客的非直系亲戚数量;
Parch:乘客的直系亲戚数量(父母和孩子);
Ticket:船票号码,有连号的说明是一起购买的;
Fare:船票的价格;
Cabin:具体的船舱位置;
Embarked:登陆的港口位置。
我们先对这些数据有一个感性的认识,具体的影响,还要在数据的图像化中发现。
#4、清洗转换数据
##4.1、清洗数据
数据清洗的第一步,先是处理缺失值,因为很多模型在缺失值存在的情况下,是无法运算的,而缺失值处理,一般先从缺的最少的开始,因为其他的缺失字段,比如年龄,我们可以考虑用回归的方式预测,比简单的分组平均要更准确。
##清洗转换数据 #Embarked:用众数处理空缺值 alldata.Embarked.fillna(alldata.Embarked.mode()[0],inplace=True) #Ticket和Fare:计算重票的人数,仔细点会发现,原数据中的票价,其实是和自己同票的人的总票价,所以需要重新整理一下。 alldata['NTickets']=alldata.groupby('Ticket')['Ticket'].transform('count') alldata['Fare_S']=alldata.Fare/alldata.NTickets #用平均值填补空缺 alldata.Fare_S.fillna(alldata.Fare_S.mean(),inplace=True) #Fare_S:根据每个价位段,合理的分配各价位,我选择的分配方式如下: alldata['Fare_0']=np.where(alldata.Fare_S==0,1,0) alldata['Fare_0_10']=np.where(((alldata.Fare_S>0)&(alldata.Fare_S<=10)),1,0) alldata['Fare_10_15']=np.where(((alldata.Fare_S>10)&(alldata.Fare_S<=15)),1,0) alldata['Fare_15_45']=np.where(((alldata.Fare_S>15)&(alldata.Fare_S<=45)),1,0) alldata['Fare_45']=np.where(alldata.Fare_S>45,1,0) #NTickets:查看NTickets和存活率之间的关系,按照1张,2-4张,5张以上分成三组 def NTickets_grouping(x): if x==1: x='Single' elif 1<x<5: x='Little_Group' else: x='Large_Group' return x alldata['NTickets']=alldata.NTickets.map(NTickets_grouping) #查看下船票数字的长度对于存活率的影响,发现,船票数字为5时,存活率更高,所以将船票数字是否为5作为一个评判的标准 alldata['Ticket_Number_Len']=alldata.Ticket.str.extract('([0-9]+$)') alldata['Ticket_Number_Len']=alldata.Ticket_Number_Len.str.len() alldata['Ticket_Num5']=(alldata.Ticket_Number_Len==5).astype(int) #Name处理 #提取出名字中的title alldata['Title']=alldata.Name.str.extract('([A-Za-z]+)\.') mapping_title={'Capt':'Rare','Lady':'Mrs', 'Countess':'Miss', 'Col':'Rare','Don':'Rare', 'Dr':'Rare', 'Major':'Rare', 'Rev':'Rare', 'Sir':'Rare','Jonkheer':
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。