kaggle入门——逻辑回归应用之kaggle泰坦尼克之灾 原文连接:https://blog.csdn.net/han_xiaoyang/article/details/49797143#commentBox
https://www.kesci.com/home/project/5bfe39b3954d6e0010681cd1
Kaggel是一个要数据有数据,要实际应用场景有场景,要一起在数据挖掘领域high得不要不要的小伙伴就有小伙伴的地方啊!!!
据挖掘竞赛),企业或者研究者可以将问题背景、数据、期望指标等发布到Kaggle上,以竞赛的形式向广大的数据科学家征集解决方案。而热爱数(dong)据(shou)挖(zhe)掘(teng)的小伙伴们可以下载/分析数据,使用统计/机器学习/数据挖掘等知识,建立算法模型,得出结果并提交,排名top的可能会有奖金哦!
下面是小伙伴们最爱的forum页面,你会看到各种神级人物厉(qi)害(pa)的数据处理/建模想法,你会直视『世界真奇妙』。
泰坦尼克号问题背景
先看看数据长什么样,在Date下我们的train.csv和test.csv两个文件分别存着官方给的训练和测试数据。
import pandas as pd #数据分析
import numpy as np #科学计算
from pandas import Series,DataFrame
data_train = pd.read_csv("e:/data/Titanic_data/train.csv")
data_train.columns
输出
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
dtype='object')
看到大概有以下这些字段
PassengerId =>乘客ID
Pclass => 乘客登记(1/2/3等舱位)
Name => 乘客姓名
Sex =>性别
Age => 年龄
SibSp => 堂兄弟/妹个数
Parch => 父母与小孩个数
Ticket => 船票信息
Fare => 票价
Cabin => 客舱
Embarked =>登船港口
查看数据集信息
data_train.info()
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
上面的数据可以看出来,训练数据总共又891名乘客,但是有些属性数据不全,比如说:
data_train.describe()
由mean字段的值,大概0.383838的人最后获救了,2/3等舱人数要比1等舱多,平均乘客年龄大概是29.7岁…
上面的简单描述性信息并没有什么用,需要更细力度的分析数据
仅仅上面的对数据的了解,依旧无法提供给我们想法和思路。再深入一点看看数据
查看每个/多个 属性和最后的Survived之间有什么样的关系
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure()
fig.set(alpha=0.2) #设定图表颜色alpha参数
plt.subplot2grid((2,3),(0,0)) #在一张大图里分列几个小图
plt.rcParams['font.sans-serif'] = ['SimHei'] #指定默认字体
plt.rcParams['axes.unicode_minus'] = False #解决中文显示为方框的问题
data_train.Survived.value_counts().plot(kind='bar') #柱状图
plt.title("获救情况 (1为获救)") #标题
plt.ylabel("人数")
plt.subplot2grid((2,3),(0,1))
data_train.Pclass.value_counts().plot(kind='bar')
plt.ylabel("人数")
plt.title("乘客等级分布")
plt.subplot2grid((2,3),(0,2))
plt.scatter(data_train.Survived,data_train.Age) #散点图
plt.ylabel("年龄")
plt.grid(b=True,which='major',axis='y') #显示y轴网格
plt.title("按年龄看获救分布 (1为获救)")
plt.subplot2grid((2,3),(1,0),colspan=2)
data_train.Age[data_train.Pclass == 1].plot(kind='kde')
data_train.Age[data_train.Pclass == 2].plot(kind='kde')
data_train.Age[data_train.Pclass == 3].plot(kind='kde')
plt.xlabel("年龄")
plt.ylabel("密度")
plt.title("各等级的乘客年龄分布")
plt.legend(('头等舱','2等舱','3等舱'),loc='best') #显示图例
plt.subplot2grid((2,3),(1,2))
data_train.Embarked.value_counts().plot(kind='bar')
plt.title("各登船口岸上船人数")
plt.ylabel("人数")
plt.show()
在图上可以看出来,被救的人300多点,不到半数;3等舱人数非常多,遇难和获救的人年龄跨度似乎都很广;3个不同舱年龄总体趋势似乎也一致,2/3等舱乘客20多岁的人最多,1等舱40岁左右的最多(似乎符合财富和年龄的分配);登船港口人数按照S、C、Q递减,而且s远对于另外两个港口。
现在可能会有一些想法了:
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({'未获救':Survived_0,'获救':survived_1})
df.plot(kind='bar',stacked=True) #stacked表示叠加
plt.title('各乘客等级的获救情况')
plt.xlabel('乘客等级')
plt.ylabel('人数')
plt.show()
明显等级为1的乘客,获救的概率高很多。这一定是影响救结果的一个特征。
#### 查看性别的获救情况
fig = plt.figure()
fig.set(alpha=0.2)
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({'男性':Survived_m,'女性':Survived_f})
df.plot(kind='bar',stacked=True)
plt.title('按性别查看获救情况')
plt.xlabel('性别')
plt.ylabel('人数')
plt.show()
外国人果然尊重lady,lady first践行的不错。性别无疑也要作为重要特征加入最后的模型之中。
来个详细版的
#然后来看看各种舱级别情况下各性别的获救情况
fig=plt.figure()
fig.set(alpha=0.65)
plt.title('舱等级和性别的获救情况')
ax1 = fig.add_subplot(141)
data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts().plot(kind='bar',label='female high class',color='#FA2479')
ax1.set_xticklabels(["未获救","获救"],rotation=0)
plt.legend(["女性/低级舱"],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(["未获救","获救"],rotation=0)
plt.legend(["女性/低级舱"],loc='best')
ax3=fig.add_subplot(143,sharey=ax1)
data_train.Survived[data_train.Sex == 'male'][data_train.Pclass != 3].value_counts().plot(kind='bar',label='male high class',color='lightblue')
ax3.set_xticklabels(["未获救","获救"],rotation=0)
plt.legend(["男性/高级舱"],loc="best")
ax4=fig.add_subplot(144,sharey=ax1)
data_train.Survived[data_train.Sex == 'male'][data_train.Pclass == 3].value_counts().plot(kind='bar',label='male low class',color='steelblue')
ax4.set_xticklabels(["未获救","获救"],rotation=0)
plt.legend(["男性/低级舱"],loc="best")
fig = plt.figure()
fig.set(alpha=0.2)
Survived_0 = data_train.Embarked[data_train.Survived == 0].value_counts()
Survived_1 = data_train.Embarked[data_train.Survived == 1].value_counts()
df=pd.DataFrame({"未获救":Survived_0,"获救":Survived_1})
df.plot(kind='bar',stacked=True)
plt.title("各登船港口乘客的获救情况")
plt.xlabel("登船港口")
plt.ylabel("人数")
plt.show()
gg = data_train.groupby(['SibSp','Survived'])['PassengerId'].count()
print(gg)
gp = data_train.groupby(['Parch','Survived'])['PassengerId'].count()
print(gp)
ticket是船票编号,应该是unique的,和最后的结果没有太大的关系,先不纳入考虑的特征范畴
cabin只有204个乘客有值,我们先看一下它的一个分布
data_train.Cabin.value_counts()
数据三三两两的很不集中,也许前面的ABCD是指加班位置,然后编号是房间号
Cabin属性,应该算作类目型的,本来缺失值就多,还如此不集中,第一感觉是,如果直接按照类目特征处理的话,太散了,估计每个因子化后的特征都拿不到什么权重。加上有那么多缺失值,就下那边Cabin缺失与否作为条件(虽然这部分信息缺失可能只是未登记,或者只是丢失了而已,所以这样做未必妥当),现在有无Cabin信息这个粗细力度上看看Survived的情况好了。
fig = plt.figure()
fig.set(alpha=0.2) #设定图标颜色alpha参数
Survived_cabin = data_train.Survived[data_train.Cabin.notnull()].value_counts()
Survived_nocabin = data_train.Survived[data_train.Cabin.isnull()].value_counts()
df=pd.DataFrame({"有":Survived_cabin,"无":Survived_nocabin}).transpose()
df.plot(kind='bar',stacked=True)
plt.title("按Cabin有无看获救情况")
plt.xlabel("Cabin有无")
plt.ylabel("人数")
plt.show()
大体数据的情况看了一遍,对感兴趣的属性也有个大概的了解了。下一步干啥?应该处理处理这些数据,为机器学习建模做点准备了。
这里说的数据预处理,其实就包括了很多kaggler津津乐道的feature engineering过程,非常有必要!
【特征工程太重要了!】
【特征工程太重要了!】
【特征工程太重要了!】
先从最突出的数据属性开始吧,对,Cabin和Age,有缺失数据实在是对下一步工作影响太大。
先说Cabin,暂时就按照上面说的,按Cabin有无数据,将这个属性处理成Yes和No两种类型吧。
再说Age:
通常遇到缺失值的情况,会有几种常见的处理方式
本例中,后两种处理方式应该都是可行的,先试试拟合补全吧(虽然说没有特别多的背景可供我们拟合,这不一定是一个多么好的选择)
我们这里使用scikit-learn种的RandomForest来拟合一下缺失的年龄数据(注:RandomForest是一个用在原始数据中做不同采样,建立多颗DesionTree,再进行average等等来降低过拟合现象,提高结果的机器学习算法,之后会介绍)
from sklearn.ensemble import RandomForestRegressor
### 使用 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()]
unknown_age = age_df[age_df.Age.isnull()]
#y即目标年龄
y = known_age.iloc[:,0]
#x即特征属性
X = known_age.iloc[:,1:]
#fit到RandomForestRegressor之中
rfr = RandomForestRegressor(random_state=0,n_estimators=2000,n_jobs=-1)
rfr.fit(X,y)
#用得到的模型进行未知年龄结果预测
predictedAges = rfr.predict(unknown_age.iloc[:,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)
data_train.info()
嗯,目的达到了,OK了
因为逻辑回归建模时,需要输入的特征 都是数值型特征,通常会先对类目型的特征因子化。什么叫因子化呢?举个例子:
以Cabin为例。原本一个属性维度,因为其取值可以是[‘yes’,‘no’],而将其平展开为’Cabin_yes’,'Cabin_no’两个属性
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) #inplace=True:不创建新的对象,直接对原始对象进行修改
df.columns
df.info()
成功的把类目属性全部转化成0,1的属性值了。
这样,看起来,需要的属性值都有课,且都是数值类型的。?
但是仔细已经按Age和Fare两个属性,乘客的数值幅度变化太大,如果了解逻辑回归与梯度下降的话,会知道,各属性之间scale差距太大,将对收敛速度造成几万点伤害,设置不收敛…所以先用scikit-learn里面的preprocessing模块对这两列做一个scaling,其实就是将一些变化幅度较大的特征化到[-1,1]之内。
import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
#知道shape属性是多少,但是想变成只有一列,行数不知道多少,通过`z.reshape(-1,1)`,Numpy自动计算
age_scale_param = scaler.fit(df['Age'].values.reshape(-1,1))
df['Age_scaled'] = scaler.fit_transform(df['Age'].values.reshape(-1,1),age_scale_param)
fare_scale_param = scaler.fit(df['Fare'].values.reshape(-1,1))
df['Fare_scaled'] = scaler.fit_transform(df['Fare'].values.reshape(-1,1),fare_scale_param)
df
把需要的feature字段取出来
from sklearn import linear_model
#用正则表达式取出我们要的属性值
train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
#y即Survival结果:第0列
y = train_df.iloc[:,0]
#X即特征属性:第1列及后序列
X = train_df.iloc[:,1:]
#fit到LogisticRegression之中
clf = linear_model.LogisticRegression(C=1.0,penalty='l1',tol=1e-6)
clf.fit(X,y)
clf
X.shape
(891, 14)
good 很顺利,我们得到了一个model.
先淡定!淡定!不是直接把test.csv直接丢到model里就能拿到结果啊…我们的"test_data"也要做和"train_data"一样的预处理啊!!
data_test = pd.read_csv("e:/data/Titanic_data/test.csv")
data_test.loc[(data_test.Fare.isnull()),'Fare'] = 0
#接着我们对test_data做和train_data中一致的特征变换
#首先用同样变得RandomForestRegressor模型填上丢失的年龄
tmp_df = data_test[['Age','Fare','Parch','SibSp','Pclass']]
null_age = tmp_df[data_test.Age.isnull()].as_matrix()
#根据特征属性Xy预测年龄并补上
X = null_age[:,1:]
predictedAges = rfr.predict(X)
data_test.loc[(data_test.Age.isnull()),'Age'] = predictedAges
data_test = set_Cabin_type(data_test)
dummies_Cabin = pd.get_dummies(data_test['Cabin'],prefix='Cabin')
dummies_Embarked = pd.get_dummies(data_test['Embarked'],prefix='Embarked')
dummies_Sex = pd.get_dummies(data_test['Sex'],prefix='Sex')
dummies_Pclass = pd.get_dummies(data_test['Pclass'],prefix='Pclass')
df_test = pd.concat([data_test,dummies_Cabin,dummies_Embarked,dummies_Sex,dummies_Pclass],axis=1)
df_test.drop(['Pclass','Name','Sex','Ticket','Cabin','Embarked'],axis=1,inplace=True)
df_test['Age_scaled'] = scaler.fit_transform(df_test['Age'].values.reshape(-1,1),age_scale_param)
df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'].values.reshape(-1,1),fare_scale_param)
df_test
418 rows × 17 columns
不错不错,数据很ok,差最后一步了。下面就做预测结果吧!!
test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions = clf.predict(test)
result = pd.DataFrame({'PassengerId':data_test['PassengerId'],'Survived':predictions.astype(np.int32)})
result.to_csv("e:/data/Titanic_data/logistic_regression_predictions.csv",index=False)
pd.read_csv('e:/data/Titanic_data/logistic_regression_predictions.csv')
418 rows × 2 columns
在Kaggle的Make a submission页面,提交上结果。如下:
0.76555,结果还不错,毕竟,这只是简单分析处理过后的一个baseline模型。
提交只是万里长征第一步,这才刚撸完baseline model,模型还需要优化
看过Andrew Ng老师的machine Learning课程的同学们知道,现在应该分析分析模型现在的状态了,是过/欠拟合,以确定我们需要更多的特征还是更多的数据,或者其他操作。
我们有一条很著名的learning curves对吧。
不过在现在的场景下,先不着急做这个事情,我们这个baseline系统还有些粗糙,先再挖掘挖掘。
pd.DataFrame({"columns":list(train_df.columns)[1:],"coef":list(clf.coef_.T)})
from sklearn.model_selection import cross_val_score,train_test_split
#简单看看打分情况
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.iloc[:,1:]
y = all_data.iloc[:,0]
print(cross_val_score(clf,X,y,cv=5))
[ 0.81564246 0.81564246 0.78651685 0.78651685 0.81355932]
结果是[ 0.81564246 0.81564246 0.78651685 0.78651685 0.81355932]
似乎比Kaggle上的结果略高,毕竟用的不是同一份数据集评估的。
既然要做交叉验证,干脆就把交叉验证里面bad case拿出来看看,看看人眼审核,是否能发现什么蛛丝马迹,是忽略了哪些信息,使得这些乘客被判定错了。再把bad case上面得到的想法和前面系数分析的合在一起,然后逐个试试。
下面做数据分割,并再原始数据集上瞄一眼bad case:
#分隔数据,按照训练数据:测试数据 = 7:3
split_train,split_cv = train_test_split(df,test_size=0.3,random_state=42)
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.iloc[:,1:],train_df.iloc[:,0])
#对cross validation数据进行预测
cv_df = split_cv.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions = clf.predict(cv_df.iloc[:,1:])
origin_data_train = pd.read_csv("e:/data/Titanic_data/train.csv")
bad_cases = origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.iloc[:,0]]['PassengerId'].values)]
bad_cases
可以跑一遍自己试试,拿到bad cases之后,仔细看看。也会有一些猜测和想法。其中会有一部分印证在系数分析部分的猜测,那这些优化的想法的优先级可以放高一些。
现在有了"train_df"和"cv_df"两个数据部分,前者用于训练model,后者用于评定和选择模型。可以开始可劲折腾了。
我们随便列一些可能可以做的优化操作:
大家接着往下挖掘,可能还可以想到更多可以细挖的部分。我这里先列这些了,然后我们可以使用手头上的”train_df”和”cv_df”开始试验这些feature engineering的tricks是否有效了
试验的过程比较漫长,也需要有耐心,而且我们经常会面临很尴尬的状况,就是我们灵光一闪,想到一个feature,然后坚信它一定有效,结果试验下来,效果还不如试验之前的结果。恩,需要坚持和耐心,以及不断的挖掘。
有一个很可能发生的问题是,我们不断的做feature engineering,产生的特征越来越多,用这些特征去训练模型,
会对我们的训练集拟合的越来越好,同时也可能在逐步丧失泛化能力,从而在待预测的数据上表现不佳,也就是发生过拟合问题。
从另一个角度上说,如果模型在待预测的数据上表现不佳,除掉上面说的过拟合问题,也有可能是欠拟合问题,也就是说在训练集上,其实拟合的也不是那么好。》
过拟合和欠拟合解释起来就是:
对过拟合而言,通常以下策略对结果优化是有用的:
而对于欠拟合而言,通常需要更多的feature,更复杂的模型来提高准确度。
著名的learing curve可以帮我们判定我们的模型现在所处状态。我们以样本数为横坐标,训练和交叉验证集上的错误率为纵坐标,两种状态分别如下两张图所示:过拟合(overfitting/high variace),欠拟合(underfitting/high bias)
我们也可可以把错误率替换成准确率,得到另一种形式的learning curve(sklearn 里面是这么做的)。
回到我们的问题,我们用scikit-learn里面的learning_curve来帮我们分辨我们的模型状态。举个例子,我们画一下我们最先得到的baseline model的learning curve
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import learning_curve
#用sklearn的learning_curve 得到training_score和cv_scoer,使用matplotlib画出learning curve
def plot_learing_curve(estimator,title,X,y,ylim=None,cv=None,n_jobs=1,
train_size=np.linspace(.05,1.,20),verbose=0,plot=True):
'''
画出data在某模型上的learning curve.
参数解释
--------
estimator:使用的分类器
title:表格的标题
X:输入的feature,numpy类型
y:输入的target vactor
ylim:tuple格式的(ymin,ymax),设定图像中纵坐标的最低点和最高点
cv:做cross-validation的时候,数据分成的份数
n_jobs:并行的任务数(默认1)
'''
train_sizes,train_scores,test_scores = learning_curve(
estimator,X,y,cv=cv,n_jobs=n_jobs,train_sizes=train_size,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"训练样本数")
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.gca().invert_yaxis()
plt.show()
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_learing_curve(clf, u"学习曲线", X, y)
从实际数据上看,我们得到的learning curve 没有推到的那么光滑,但是可以大致看出来,训练集和交叉验证集上的得分走势曲线还是符合预期的。
目前的曲线来看,我们的model并不处于overfitting的状态,(overfintting的表现一般是训练集上得分高,而交叉验证集上要低很多,中间的gap比较大)。因此我们可以再做一些feature engineering的工作,添加一些新产出的特征或组合特征到模型中。
好了,终于到这一步了,我们要祭出机器学习/数据挖掘上通常最后会用到的大杀器了。嗯,模型融合。
重要的事情说三遍。
什么是模型融合呢,举例子来直观理解一下。
大家都看过知识问答的综艺节目中,求助现场观众的时候,让观众投票,最高的答案作为自己的答案的形式吧,每个人都有一个判定结果,最后我们相信答案在大多数人手里。
再举一个通俗一点的例子。你和你班某数学大神关系好,每次作业都模仿他的,于是大多数情况下,
他做对了,你也对了。突然有一天大神脑子犯糊涂,手一抖,写错了一个数,于是…嗯,你也跟着错了。另一个场景是,你跟你班5个大神的关系都很好,每次都把他们作业拿过来,对比一下,再自己做,如果哪天某大神犯糊涂了,写错了,but另外4个写对了,那你肯定相信另外4个人的是正确答案吧?
最简单的模型融合大概就是这么个意思,比如分类问题,当我们手上有一堆在同一份数据集上训练得到的分类器(比如logistic regression,SVM,KNN,random forest,神经网络),那我们让它们都分别去做判定,然后对结果做投票统计,取票数最多的结果为最后结果。
bingo.问题就这么完美的解决了。
话说回来,回到我们现在的问题,你看,我们现在只讲了logistic regression。如果我们还想用这个融合思想取提高我们的结果,我们应该怎么做呢?
既然这个时候模型没得选,而欧美让你就在数据上动动手脚。如果模型出现过拟合,一定是在我们的训练上出现拟合度过度造成的对吧。
我们干脆就不要用全部的训练集,每次取训练集的一个subset,做训练,这样,我们虽然用的是同一个机器学习算法但是得到的模型却是不一样的;同时,因为我们没有任何一份子数据集是全的,因此即使粗线过拟合,也是在子训练集上,而不是全体数据上,这样做一个融合,可能
对最后的结果有一个帮助。对,这就是常用的Bagging
from sklearn.ensemble import BaggingRegressor
train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')
#y即Survived结果
y = train_df.iloc[:,0]
#X即特征属性值
X = train_df.iloc[:,1:]
#fir到BaggingRegressor之中
clf = linear_model.LogisticRegression(C=1.0,penalty='l1',tol=1e-6)
bagging_clf = BaggingRegressor(clf,n_estimators=20,max_samples=0.8,max_features=1.0,bootstrap=True,bootstrap_features=False,n_jobs=-1)
bagging_clf.fit(X,y)
test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')
predictions = bagging_clf.predict(test)
result = pd.DataFrame({'PassengerId':data_test['PassengerId'],'Survived':predictions})
result.to_csv("e:/data/Titanic_data/logistic_regression_bagging_predictions.csv",index=False)
pd.read_csv('e:/data/Titanic_data/logistic_regression_bagging_predictions.csv')
然后再Make a submission,嗯,发现对结果还是有帮助的。
对于任何机器学习的问题,不要一上来就追求尽善美,先用自己会的算法撸一个baseline的model出来, 然后进行后续的分析步骤,一步步提高。
在问题的结果过程中:
【对数据的认识太重要了!】
【数据中的特殊点/离群点的分析和处理太重要了!】
【特征工程(feature engineering)太重要了!】
【模型融合(model ensemble)太重要了!】
本文中用机器学习解决问题的过程大概如下所示: