以下内容为讲课时使用到的泰坦尼克数据集分析、建模过程,整体比较完整,分享出来,希望能帮助大家。部分内容由于版本问题,可能无法顺利运行。
【Jack and Rose】的故事大家都熟悉,然而今天,我们要探讨的问题是我们该如何预测豪华游轮上的乘客的存活状况。也就是说,我们要利用已有的训练和测试数据,其中包含一些乘客的个人信息及存活状况,利用这些信息,生成合适的模型,最终去预测其他人的存活状况。
这是一个二分类问题,有很多分类算法都可以用来解决这个问题。我们希望:
通过完成这个项目,我们希望同学们可以学习到:
# 进行数据分析和整合数据
import pandas as pd
import numpy as np
import random as rnd# 用于数据可视化
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline# 使用机器学习算法进行建模
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
八卦一句,有人的地方就有鄙视链。跟知乎一样。Kaggle的也是个处处呵呵的危险地带。Kaggle上默认把数据放在input文件夹下。所以我们没事儿写个教程什么的,也可以依据这个convention来,显得很入流。
# 使用Pandas分别加载训练数据和测试数据
train_df = pd.read_csv('../input/train.csv')
test_df = pd.read_csv('../input/test.csv')
# 将训练集和测试集合并
combine = [train_df, test_df]
已给的数据描述:
PassengerId/乘客ID
Survived/是否获救 ==> 0/否, 1/是
Pclass/船票等级 ==> 1/上等, 2/中等, 3/下等
Name/乘客姓名
Sex/乘客性别
Age/乘客年龄
SibSp/乘客的堂兄弟或堂妹或配偶的个数
Parch/父母与小孩个数
Ticket/船票号码
Fare/乘客的船票票价
Cabin/客舱号码
Embarked/登船港口 ==> C/Cherbourg, Q/Queenstown, S/Southampton
# 显示训练集和测试集总体信息
train_df.info()
print ("#"*40)
test_df.info()
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):# Column Non-Null Count Dtype
--- ------ -------------- ----- 0 PassengerId 891 non-null int64 1 Survived 891 non-null int64 2 Pclass 891 non-null int64 3 Name 891 non-null object 4 Sex 891 non-null object 5 Age 714 non-null float646 SibSp 891 non-null int64 7 Parch 891 non-null int64 8 Ticket 891 non-null object 9 Fare 891 non-null float6410 Cabin 204 non-null object 11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
########################################
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):# Column Non-Null Count Dtype
--- ------ -------------- ----- 0 PassengerId 418 non-null int64 1 Pclass 418 non-null int64 2 Name 418 non-null object 3 Sex 418 non-null object 4 Age 332 non-null float645 SibSp 418 non-null int64 6 Parch 418 non-null int64 7 Ticket 418 non-null object 8 Fare 417 non-null float649 Cabin 91 non-null object 10 Embarked 418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
# 显示train_df/test_df列数据类型
train_df.dtypes,test_df.dtypes
(PassengerId int64Survived int64Pclass int64Name objectSex objectAge float64SibSp int64Parch int64Ticket objectFare float64Cabin objectEmbarked objectdtype: object,PassengerId int64Pclass int64Name objectSex objectAge float64SibSp int64Parch int64Ticket objectFare float64Cabin objectEmbarked objectdtype: object)
# 显示train_df和test_df的行数和特征数量
train_df.shape,test_df.shape
((891, 12), (418, 11))
# 打印训练集和测试集中所有特征列的名称
print(train_df.columns.values)
print("#"*40)
print(test_df.columns.values)
['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch''Ticket' 'Fare' 'Cabin' 'Embarked']
########################################
['PassengerId' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch' 'Ticket' 'Fare''Cabin' 'Embarked']
问题1:特征在python中的数据类型和数据集基本信息
对于大数据集,我们很难去查看所有的数据记录,然而,我们可以查看其中的多个小样本来了解数据。这些样本可以直接告诉我们哪些特征是需要修正的。
# 显示数据集前5行的记录
train_df.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
# 显示训练集最后5行的记录
train_df.tail()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
886 | 887 | 0 | 2 | Montvila, Rev. Juozas | male | 27.0 | 0 | 0 | 211536 | 13.00 | NaN | S |
887 | 888 | 1 | 1 | Graham, Miss. Margaret Edith | female | 19.0 | 0 | 0 | 112053 | 30.00 | B42 | S |
888 | 889 | 0 | 3 | Johnston, Miss. Catherine Helen "Carrie" | female | NaN | 1 | 2 | W./C. 6607 | 23.45 | NaN | S |
889 | 890 | 1 | 1 | Behr, Mr. Karl Howell | male | 26.0 | 0 | 0 | 111369 | 30.00 | C148 | C |
890 | 891 | 0 | 3 | Dooley, Mr. Patrick | male | 32.0 | 0 | 0 | 370376 | 7.75 | NaN | Q |
问题2:特征类型
# python中数据类型为float64和int64的特征信息
train_df.describe()
PassengerId | Survived | Pclass | Age | SibSp | Parch | Fare | |
---|---|---|---|---|---|---|---|
count | 891.000000 | 891.000000 | 891.000000 | 714.000000 | 891.000000 | 891.000000 | 891.000000 |
mean | 446.000000 | 0.383838 | 2.308642 | 29.699118 | 0.523008 | 0.381594 | 32.204208 |
std | 257.353842 | 0.486592 | 0.836071 | 14.526497 | 1.102743 | 0.806057 | 49.693429 |
min | 1.000000 | 0.000000 | 1.000000 | 0.420000 | 0.000000 | 0.000000 | 0.000000 |
25% | 223.500000 | 0.000000 | 2.000000 | 20.125000 | 0.000000 | 0.000000 | 7.910400 |
50% | 446.000000 | 0.000000 | 3.000000 | 28.000000 | 0.000000 | 0.000000 | 14.454200 |
75% | 668.500000 | 1.000000 | 3.000000 | 38.000000 | 1.000000 | 0.000000 | 31.000000 |
max | 891.000000 | 1.000000 | 3.000000 | 80.000000 | 8.000000 | 6.000000 | 512.329200 |
# 查看train_df不同特征的分位数,以确定不同特征的分布情况
train_df['Survived'].quantile([.61, .62])
train_df['Parch'].quantile([.75, .8])
train_df['Age'].quantile([.1, .2, .3, .4, .5, .6, .7, .8, .9, .99])
train_df['Fare'].quantile([.1, .2, .3, .4, .5, .6, .7, .8, .9, .99])
0.10 7.55000
0.20 7.85420
0.30 8.05000
0.40 10.50000
0.50 14.45420
0.60 21.67920
0.70 27.00000
0.80 39.68750
0.90 77.95830
0.99 249.00622
Name: Fare, dtype: float64
问题3:统计指标与信息
统计意义上:
# python中数据类型为object的特征信息
train_df.describe(include=['O'])
Name | Sex | Ticket | Cabin | Embarked | |
---|---|---|---|---|---|
count | 891 | 891 | 891 | 204 | 889 |
unique | 891 | 2 | 681 | 147 | 3 |
top | Braund, Mr. Owen Harris | male | 347082 | B96 B98 | S |
freq | 1 | 577 | 7 | 4 | 644 |
问题4:区分特征
统计意义上(注意不是以Python中的数据类型来定义的):
特征间相关性的热图可以帮助我们了解哪些变量可能更重要。
# 定义特征间相关性热图函数,查看变量之间的相关性大小
def plot_correlation_map(df):corr = df.corr() # 查看的是train_df的特征间相关性_ , ax = plt.subplots(figsize = (10,8))cmap = sns.diverging_palette(220,10,as_cmap = True)_ = sns.heatmap(corr, cmap = cmap,square=True, cbar_kws={'shrink':.9}, ax=ax, annot = True, annot_kws = {'fontsize':12})
plot_correlation_map(train_df)
问题5:特征相关性
问题6:你想到了哪些可能性?
根据目前我们已经完成的【描述性统计分析】部分提供给我们的信息,我们可以提出以下假设:
【描述性统计分析】部分提供的信息,只是帮助我们对数据集的总体情况有一个初步的了解,但是对于最终建模和预测结果的帮助非常有限。接下来让我们从其他唯独来详细分析一下数据,探索每个/多个特征与Survived之间的关系,进一步验证自己提出的假设。
接下来,我们将通过关联性分析和数据可视化的方法开始验证以上相关假设。
Ticket是船票编号,PassengerId是乘客ID和Survived没有太大的关系,不纳入我们的考虑范围。
Sex,Pclass,SibSp和Parch这四个特征没有缺失值,并且对于他们的探索不需要对数据进行处理,所以我们首先来探索这四个特征与Survived之间的关系。
结论:
# Pclass与Survived
train_df[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Pclass | Survived | |
---|---|---|
0 | 1 | 0.629630 |
1 | 2 | 0.472826 |
2 | 3 | 0.242363 |
# Sex与Survived
train_df[["Sex", "Survived"]].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Sex | Survived | |
---|---|---|
0 | female | 0.742038 |
1 | male | 0.188908 |
# SibSp与Survived
train_df[["SibSp", "Survived"]].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)
SibSp | Survived | |
---|---|---|
1 | 1 | 0.535885 |
2 | 2 | 0.464286 |
0 | 0 | 0.345395 |
3 | 3 | 0.250000 |
4 | 4 | 0.166667 |
5 | 5 | 0.000000 |
6 | 8 | 0.000000 |
# Parch与Survived
train_df[["Parch", "Survived"]].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Parch | Survived | |
---|---|---|
3 | 3 | 0.600000 |
1 | 1 | 0.550847 |
2 | 2 | 0.500000 |
0 | 0 | 0.343658 |
5 | 5 | 0.200000 |
4 | 4 | 0.000000 |
6 | 6 | 0.000000 |
Age与Survived
直方图对于分析连续型特征是非常有帮助的:
备注: Y轴表示乘客的人数
问题7:关于Age特征,你有什么发现或结论?
观察发现:
结论:
# Age与Survived
g = sns.FacetGrid(train_df, col='Survived')
g.map(plt.hist, 'Age', bins=20) # 年龄每4岁进行分割
Fare与Survived
问题8:关于Fare特征,你有什么发现或结论?
观察发现:
结论:
# Fare与Survived
g = sns.FacetGrid(train_df, col='Survived')
g.map(plt.hist, 'Fare', bins=30)
我们可以通过一幅图来探索多个特征之间的相关性,但是不管是数值特征还是分类特征,它们的取值必须都是数值,不能是字符串。
问题9:Pclass、Age和Survived之间,你有什么发现或结论?
观察发现:
结论:
考虑将Pclass放入模型训练。
# grid = sns.FacetGrid(train_df, col='Pclass', hue='Survived')
grid = sns.FacetGrid(train_df, col='Survived', row='Pclass', size=2.2, aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend();
接下来,我们来探索多个分类特征与Survived之间的关系。
问题10:Sex、Embarked、Pclass和Survived之间,你有什么发现或结论?
观察发现:
结论:
# grid = sns.FacetGrid(train_df, col='Embarked')
grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6)
grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')
grid.add_legend()
问题11:Sex, Fare,Embarked和Survived之间,你有什么发现或结论?
观察发现:
结论:后续需要对Fare进行离散化处理。
# grid = sns.FacetGrid(train_df, col='Embarked', hue='Survived', palette={0: 'k', 1: 'w'})
grid = sns.FacetGrid(train_df, row='Embarked', col='Survived', size=2.2, aspect=1.6)
grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)
grid.add_legend()
通过关联性分析和数据可视化的方式对数据进行分析,验证了我们最初提出的假设,这些结论将作为我们下一步工作流程的起点。
基于关联性分析和数据可视化为我们提供的信息,下一步,我们将对特征进行处理相应的处理,最终将每个特征符合放入逻辑回归中特征的要求。
注意:
基于最初的假设,我们将删掉Ticket和Cabin特征。
# 删除前的每个df的结构
print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)train_df = train_df.drop(['Ticket','Cabin'], axis=1)
test_df = test_df.drop(['Ticket', 'Cabin'], axis=1)
combine = [train_df, test_df]# 删除后的每个df的结构
print("After", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)
Before (891, 12) (418, 11) (891, 12) (418, 11)('After', (891, 10), (418, 9), (891, 10), (418, 9))
通常遇到缺值的情况,我们会有几种常见的处理方式:
针对这个数据集,第3和4中处理方式应该都是可行的。我们先尝试拟合补全缺失值,虽然这可能并不是一个很好的选择,因为没有太多背景信息来支持拟合,我们只好先试试看。同学们可以自行尝试使用第3种方法来实现对缺失值的处理,并带入模型对比不同方法对模型的影响。
针对拟合补全Age缺失值的处理方法,我们提供两种思路。
思路1:利用需要填补缺失值的特征和其他与之存在相关关系的特征这一特点来填补缺失值。
在这个数据集中,我们已经发现Age,Sex和Pclass三个特征之间是相关的。因此,我们可以利用Sex和Pclass的组合特征来找到这个组合特征下Age上午中位数,用这个中位数来填补缺失值。即我们需要找到Pclass=1和Sex=0时Age的中位数,用这个中位数去填补Pclass=1和Sex=0时,有缺失值的Age。Sex和Pclass共有6中组合,因此,我们需要找到6个中位数去分别填补对应情况下Age的缺失值。
# 建立一个空的数组来存放基于Pclass和Gender组合信息来猜测Age的值
guess_ages = np.zeros((2,3))
guess_ages
array([[0., 0., 0.],[0., 0., 0.]])
# 将Sex转变值为0,1的变量。
for dataset in combine:dataset['Sex'] = dataset['Sex'].map( {'female': 1, 'male': 0} ).astype(int)
# 循环Sex(0, 1)和Pclass(1, 2, 3)来猜测Age的值,共有6中组合
for dataset in combine:for i in range(0, 2):for j in range(0, 3):guess_df = dataset[(dataset['Sex'] == i) & \(dataset['Pclass'] == j+1)]['Age'].dropna()# age_mean = guess_df.mean()# age_std = guess_df.std()# age_guess = rnd.uniform(age_mean - age_std, age_mean + age_std)age_guess = guess_df.median()# 将age_guess的值近似为最近的整数guess_ages[i,j] = int( age_guess/0.5 + 0.5 ) * 0.5for i in range(0, 2):for j in range(0, 3):dataset.loc[ (dataset.Age.isnull()) & (dataset.Sex == i) & (dataset.Pclass == j+1),\'Age'] = guess_ages[i,j]dataset['Age'] = dataset['Age'].astype(int)train_df.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | 0 | 22 | 1 | 0 | 7.2500 | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | 1 | 38 | 1 | 0 | 71.2833 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | 1 | 26 | 0 | 0 | 7.9250 | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | 1 | 35 | 1 | 0 | 53.1000 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | 0 | 35 | 0 | 0 | 8.0500 | S |
连续型数值特征常常需要被离散化或面元(bin)划分,使用pandas库中的cut函数实现。
说明:详见5.5.3 连续特征离散化
# 创建AgeBand
train_df['AgeBand'] = pd.cut(train_df['Age'], 5)
train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)
AgeBand | Survived | |
---|---|---|
0 | (-0.08, 16.0] | 0.550000 |
1 | (16.0, 32.0] | 0.337374 |
2 | (32.0, 48.0] | 0.412037 |
3 | (48.0, 64.0] | 0.434783 |
4 | (64.0, 80.0] | 0.090909 |
# 根据AgeBand提供的区间切分信息,对Age进行分组处理
for dataset in combine: dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3dataset.loc[ dataset['Age'] > 64, 'Age'] = 4
train_df.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | AgeBand | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | 0 | 1 | 1 | 0 | 7.2500 | S | (16.0, 32.0] |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | 1 | 2 | 1 | 0 | 71.2833 | C | (32.0, 48.0] |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | 1 | 1 | 0 | 0 | 7.9250 | S | (16.0, 32.0] |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | 1 | 2 | 1 | 0 | 53.1000 | S | (32.0, 48.0] |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | 0 | 2 | 0 | 0 | 8.0500 | S | (32.0, 48.0] |
# 删掉AgeBand,更新combine
train_df = train_df.drop(['AgeBand'], axis=1)
combine = [train_df, test_df]
train_df.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | 0 | 1 | 1 | 0 | 7.2500 | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | 1 | 2 | 1 | 0 | 71.2833 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | 1 | 1 | 0 | 0 | 7.9250 | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | 1 | 2 | 1 | 0 | 53.1000 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | 0 | 2 | 0 | 0 | 8.0500 | S |
思路2:使用scikit-learn中的RandomForest来拟合Age的缺失值。
注意:使用思路1方法处理完数据后,不能直接使用下面的代码会报错误,因为:
# # 使用RandomForestClassifier填补年龄的缺失值
# from sklearn.ensemble import RandomForestRegressor
# def set_missing_ages(df):# # 把已有的数值型特征取出来丢进Random Forest Regressor中
# age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]# # 乘客分成已知年龄和未知年龄两部分
# known_age = age_df[age_df.Age.notnull()].values
# unknown_age = age_df[age_df.Age.isnull()].values# print(unknown_age)
# # 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# # 处理训练集Age特征
# train_df, rfr = set_missing_ages(train_df)
# train_df.head(10)
对测试集中的Fare特征进行处理。
处理步骤:
# 使用中位数测试集中的Fare的空值进行填充
test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)
test_df.head()
PassengerId | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | |
---|---|---|---|---|---|---|---|---|---|
0 | 892 | 3 | Kelly, Mr. James | 0 | 2 | 0 | 0 | 7.8292 | Q |
1 | 893 | 3 | Wilkes, Mrs. James (Ellen Needs) | 1 | 2 | 1 | 0 | 7.0000 | S |
2 | 894 | 2 | Myles, Mr. Thomas Francis | 0 | 3 | 0 | 0 | 9.6875 | Q |
3 | 895 | 3 | Wirz, Mr. Albert | 0 | 1 | 0 | 0 | 8.6625 | S |
4 | 896 | 3 | Hirvonen, Mrs. Alexander (Helga E Lindqvist) | 1 | 1 | 1 | 1 | 12.2875 | S |
# 创造FareBand特征
train_df['FareBand'] = pd.qcut(train_df['Fare'], 4) # 分成4份
train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)
FareBand | Survived | |
---|---|---|
0 | (-0.001, 7.91] | 0.197309 |
1 | (7.91, 14.454] | 0.303571 |
2 | (14.454, 31.0] | 0.454955 |
3 | (31.0, 512.329] | 0.581081 |
# 基于FareBand将Fare转变为定序特征
for dataset in combine:dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare'] = 2dataset.loc[ dataset['Fare'] > 31, 'Fare'] = 3dataset['Fare'] = dataset['Fare'].astype(int)# 删掉FareBand
train_df = train_df.drop(['FareBand'], axis=1)
combine = [train_df, test_df]train_df.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | 0 | 1 | 1 | 0 | 0 | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | 1 | 2 | 1 | 0 | 3 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | 1 | 1 | 0 | 0 | 1 | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | 1 | 2 | 1 | 0 | 3 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | 0 | 2 | 0 | 0 | 1 | S |
训练集中Embarked特征有两个缺失值,我们现在用Embarked的众数去填补缺失值。
# 查看非空Embarked特征的众数
freq_port = train_df.Embarked.dropna().mode()[0]
freq_port
'S'
# 将使用众数对Embarked的空值进行填充
for dataset in combine:dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)train_df[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Embarked | Survived | |
---|---|---|
0 | C | 0.553571 |
1 | Q | 0.389610 |
2 | S | 0.339009 |
# 转变成数值特征
for dataset in combine:dataset['Embarked'] = dataset['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)train_df.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | 0 | 1 | 1 | 0 | 0 | 0 |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | 1 | 2 | 1 | 0 | 3 | 1 |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | 1 | 1 | 0 | 0 | 1 | 0 |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | 1 | 2 | 1 | 0 | 3 | 0 |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | 0 | 2 | 0 | 0 | 1 | 0 |
我们想分析Name特征是否能够提取title信息,并验证title与Survived之间的相关性。
观察发现:
当我们绘制称呼、年龄和Survived时,我们注意到以下几点:
结论: 保留Title特征,放入模型进行训练。
# 通过正则表达式提取所有title字符串
# ([A-Za-z]+)\. 匹配符号“.”之前第一个单词
for dataset in combine:dataset['Title'] = dataset.Name.str.extract('([A-Za-z]+)\.', expand=False) # expand=False返回一个DataFrame
# 交叉表查看提取出的title与sex关系
pd.crosstab(train_df['Title'], train_df['Sex'])
Sex | 0 | 1 |
---|---|---|
Title | ||
Capt | 1 | 0 |
Col | 2 | 0 |
Countess | 0 | 1 |
Don | 1 | 0 |
Dr | 6 | 1 |
Jonkheer | 1 | 0 |
Lady | 0 | 1 |
Major | 2 | 0 |
Master | 40 | 0 |
Miss | 0 | 182 |
Mlle | 0 | 2 |
Mme | 0 | 1 |
Mr | 517 | 0 |
Mrs | 0 | 125 |
Ms | 0 | 1 |
Rev | 6 | 0 |
Sir | 1 | 0 |
# 交叉表查看提取出的title与Survived关系
pd.crosstab(train_df['Title'], train_df['Survived'])
Survived | 0 | 1 |
---|---|---|
Title | ||
Capt | 1 | 0 |
Col | 1 | 1 |
Countess | 0 | 1 |
Don | 1 | 0 |
Dr | 4 | 3 |
Jonkheer | 1 | 0 |
Lady | 0 | 1 |
Major | 1 | 1 |
Master | 17 | 23 |
Miss | 55 | 127 |
Mlle | 0 | 2 |
Mme | 0 | 1 |
Mr | 436 | 81 |
Mrs | 26 | 99 |
Ms | 0 | 1 |
Rev | 6 | 0 |
Sir | 0 | 1 |
# 将有相同title的文本进行替换,或者归为Rare类
# 对于当时外国称呼的说明,可以参考:https://zhidao.baidu.com/question/591037721.html
for dataset in combine:dataset['Title'] = dataset['Title'].replace(['Lady' ,'Capt', 'Col',\'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')dataset['Title'] = dataset['Title'].replace(['Mlle', 'Ms'], 'Miss')dataset['Title'] = dataset['Title'].replace(['Mme','Countess'], 'Mrs') train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
Title | Survived | |
---|---|---|
0 | Master | 0.575000 |
1 | Miss | 0.702703 |
2 | Mr | 0.156673 |
3 | Mrs | 0.795276 |
4 | Rare | 0.318182 |
# 对应分类
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5}
for dataset in combine:dataset['Title'] = dataset['Title'].map(title_mapping)dataset['Title'] = dataset['Title'].fillna(0)train_df.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | Title | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | 1 | 2 | 1 | 0 | 3 | 1 | 3 |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | 1 | 1 | 0 | 0 | 1 | 0 | 2 |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | 1 | 2 | 1 | 0 | 3 | 0 | 3 |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | 0 | 2 | 0 | 0 | 1 | 0 | 1 |
# 在训练集和测试集中都删除Name,把PassengerId从训练集中删除
train_df = train_df.drop(['Name'], axis=1)
test_df = test_df.drop(['Name'], axis=1)
combine = [train_df, test_df]
train_df.shape, test_df.shape
((891, 10), (418, 9))
我们可以将Parch和SibSp组合获得新特征FamilySize,我们发现FamilySize中有两个取值Survived取值为零。进而创建新特征IsAlone,判断乘客是否是一个登船。
# 组合Parch和SibSp特征创建FamilySize
for dataset in combine:dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False)
FamilySize | Survived | |
---|---|---|
3 | 4 | 0.724138 |
2 | 3 | 0.578431 |
1 | 2 | 0.552795 |
6 | 7 | 0.333333 |
0 | 1 | 0.303538 |
4 | 5 | 0.200000 |
5 | 6 | 0.136364 |
7 | 8 | 0.000000 |
8 | 11 | 0.000000 |
# 创建IsAlone特征
for dataset in combine:dataset['IsAlone'] = 0dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1train_df[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()
IsAlone | Survived | |
---|---|---|
0 | 0 | 0.505650 |
1 | 1 | 0.303538 |
# 在train_df,test_df删掉Parch,SibSp和FamilySize
train_df = train_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
test_df = test_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
combine = [train_df, test_df]train_df.head()
PassengerId | Survived | Pclass | Sex | Age | Fare | Embarked | Title | IsAlone | |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | 0 | 1 | 0 | 0 | 1 | 0 |
1 | 2 | 1 | 1 | 1 | 2 | 3 | 1 | 3 | 0 |
2 | 3 | 1 | 3 | 1 | 1 | 1 | 0 | 2 | 1 |
3 | 4 | 1 | 1 | 1 | 2 | 3 | 0 | 3 | 0 |
4 | 5 | 0 | 3 | 0 | 2 | 1 | 0 | 1 | 1 |
交叉特征指的是将两个或更多的类别属性组合成一个新特征。值得注意的是,当数据量较大时,或者暴力进行特征交叉时,交叉后的新特征容易产生数据稀疏问题,同学们在未来应用这个方法的时,需要特别去处理数据稀疏问题。
针对这个数据集,我们已经在5.2.1.1 Age部分对Age特征按照区间定义了不同类别。在这个Age类别和Pclass类别的基础上,通过特征交叉的方法,建立新特征Age*PClass。
如果同学们认为,在这个数据集中,还有其他分类特征的交叉能够对模型预测有帮助,也可以尝试。
# 创建交叉特征Age*Class
for dataset in combine:dataset['Age*Class'] = dataset.Age * dataset.Pclasstrain_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)
Age*Class | Age | Pclass | |
---|---|---|---|
0 | 3 | 1 | 3 |
1 | 2 | 2 | 1 |
2 | 3 | 1 | 3 |
3 | 2 | 2 | 1 |
4 | 6 | 2 | 3 |
5 | 3 | 1 | 3 |
6 | 3 | 3 | 1 |
7 | 0 | 0 | 3 |
8 | 3 | 1 | 3 |
9 | 0 | 0 | 2 |
在5.2.1 连续型数值特征部分,我们对数据集中的两个连续数值特征填补缺失值并进行了分桶处理。大家已经学习过逻辑回归与梯度下降的相关知识,我们知道实际上逻辑回归模型中可以放入连续数值特征,但是如果特征中各属性值之间数值差距太大,将对收敛速度有很大的影响。仔细观察Age和Fare两个特征分布,我们发现这两个特征的数值变化幅度很大。
因此,如果我们想要将Age和Fare连续型数值特征不做分桶处理,直接带入到逻辑回归中,我们就需要先用scikit-learn中的preprocessing模块对这两个特征进行特征缩放,就是将一些变化幅度较大的特征化到[-1,1]区间之内。
sklearn.preprocessing.StandardScaler官方文档说明
# 将一些变化幅度较大的特征化到[-1,1]之内,这样可以加速logistic regression的收敛
import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
age_scale_param = scaler.fit(train_df['Age'].values.reshape(-1, 1))
train_df['Age_scaled'] = scaler.fit_transform(train_df['Age'].values.reshape(-1, 1), age_scale_param)
fare_scale_param = scaler.fit(train_df['Fare'].values.reshape(-1, 1))
train_df['Fare_scaled'] = scaler.fit_transform(train_df['Fare'].values.reshape(-1, 1), fare_scale_param)train_df
PassengerId | Survived | Pclass | Sex | Age | Fare | Embarked | Title | IsAlone | Age*Class | Age_scaled | Fare_scaled | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | 0 | 1 | 0 | 0 | 1 | 0 | 3 | -0.392999 | -1.346777 |
1 | 2 | 1 | 1 | 1 | 2 | 3 | 1 | 3 | 0 | 2 | 0.827078 | 1.337738 |
2 | 3 | 1 | 3 | 1 | 1 | 1 | 0 | 2 | 1 | 3 | -0.392999 | -0.451938 |
3 | 4 | 1 | 1 | 1 | 2 | 3 | 0 | 3 | 0 | 2 | 0.827078 | 1.337738 |
4 | 5 | 0 | 3 | 0 | 2 | 1 | 0 | 1 | 1 | 6 | 0.827078 | -0.451938 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
886 | 887 | 0 | 2 | 0 | 1 | 1 | 0 | 5 | 1 | 2 | -0.392999 | -0.451938 |
887 | 888 | 1 | 1 | 1 | 1 | 2 | 0 | 2 | 1 | 1 | -0.392999 | 0.442900 |
888 | 889 | 0 | 3 | 1 | 1 | 2 | 0 | 2 | 0 | 3 | -0.392999 | 0.442900 |
889 | 890 | 1 | 1 | 0 | 1 | 2 | 1 | 1 | 1 | 1 | -0.392999 | 0.442900 |
890 | 891 | 0 | 3 | 0 | 1 | 0 | 2 | 1 | 1 | 3 | -0.392999 | -1.346777 |
891 rows × 12 columns
问题12:如果直接将Age和Fare的连续数值特征放入逻辑回归模型中,其他特征不变的情况,最终预测结果会有怎样的变化?为什么会有这种变化?
有的时候,特征与Survived小幅相关,并不意味着这个特征作用不大,有可能是我们细化的程度还不够,举个例子,Fare特征如果对它离散化,再分至各个乘客等级上会有更高的权重?
因为逻辑回归建模时,需要输入的特征都是数值型特征,我们通常会先对类目型的特征因子化/one-hot编码。
对于连续特征,如年龄、薪水、阅读数、身高等特征,如果需要放入逻辑回归模型中,则最好先进行离散处理,也叫 one-hot 编码;离散化处理的方式有几种,
一些属性是类别型而不是数值型,拿这个数据集中的Embarked来举例说明。Embarked由{S,C,Q}三个类别组成,最常用的方式是把每个类别属性转换成二元属性,即从{0,1}取一个值。因此基本上增加的属性等于相应数目的类别,并且对于你数据集中的每个实例,只有一个是1(其他的为0),这也就是独热(one-hot)编码方式。
以Embarked为例,原本一个属性维度,因为其取值可以是[‘S’,’C’,’Q‘],one-hot编码后将其平展开为’Embarked_C’,’Embarked_S’, ‘Embarked_Q’三个属性。
即{1,0,0}表示Embarked取值为S,{0,1,0}表示Embarked取值为C,{0,0,1}表示Embarked取值为Q。
如果你不了解这个编码的话,你可能会觉得分解会增加没必要的麻烦,因为独热编码大量地增加了数据集的维度。相反,你可能会尝试将类别属性转换成一个标量值,例如Embarked可能会用{1,2,3}表示{S,C,Q}。这里存在两个问题:
实际上,针对这个数据集中的Age和Fare特征,我们已经在5.2.1 连续型数值特征进行了等值分桶处理,将两个特征转变为分类特征。
在这里,我们系统的说明一下对连续特征进行one-hot编码常用的三种方法:
总结:
对于连续型数值特征,我们有两种处理方法,一种是离散化,如5.2.1连续型数值特征部分处理过程,另一种是5.4 特征缩放将连续之缩放到符合模型要求的区间内。
# 对train_df和test_df中需要进行one-hot编码的进行处理
train_df = pd.get_dummies(train_df,columns=['Pclass', 'Sex', 'Embarked','Title','IsAlone','Age','Fare'])
test_df = pd.get_dummies(test_df,columns=['Pclass', 'Sex', 'Embarked','Title','IsAlone','Age','Fare'])
combine = [train_df, test_df]# 查看one-hot编码数据结果
train_df.head()
PassengerId | Survived | Age*Class | Age_scaled | Fare_scaled | Pclass_1 | Pclass_2 | Pclass_3 | Sex_0 | Sex_1 | ... | IsAlone_1 | Age_0 | Age_1 | Age_2 | Age_3 | Age_4 | Fare_0 | Fare_1 | Fare_2 | Fare_3 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | -0.392999 | -1.346777 | 0 | 0 | 1 | 1 | 0 | ... | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
1 | 2 | 1 | 2 | 0.827078 | 1.337738 | 1 | 0 | 0 | 0 | 1 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
2 | 3 | 1 | 3 | -0.392999 | -0.451938 | 0 | 0 | 1 | 0 | 1 | ... | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
3 | 4 | 1 | 2 | 0.827078 | 1.337738 | 1 | 0 | 0 | 0 | 1 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
4 | 5 | 0 | 6 | 0.827078 | -0.451938 | 0 | 0 | 1 | 1 | 0 | ... | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
5 rows × 29 columns
# 确认是否分类特征全部进行one-hot编码
train_df.info()
RangeIndex: 891 entries, 0 to 890
Data columns (total 29 columns):# Column Non-Null Count Dtype
--- ------ -------------- ----- 0 PassengerId 891 non-null int64 1 Survived 891 non-null int64 2 Age*Class 891 non-null int64 3 Age_scaled 891 non-null float644 Fare_scaled 891 non-null float645 Pclass_1 891 non-null uint8 6 Pclass_2 891 non-null uint8 7 Pclass_3 891 non-null uint8 8 Sex_0 891 non-null uint8 9 Sex_1 891 non-null uint8 10 Embarked_0 891 non-null uint8 11 Embarked_1 891 non-null uint8 12 Embarked_2 891 non-null uint8 13 Title_1 891 non-null uint8 14 Title_2 891 non-null uint8 15 Title_3 891 non-null uint8 16 Title_4 891 non-null uint8 17 Title_5 891 non-null uint8 18 IsAlone_0 891 non-null uint8 19 IsAlone_1 891 non-null uint8 20 Age_0 891 non-null uint8 21 Age_1 891 non-null uint8 22 Age_2 891 non-null uint8 23 Age_3 891 non-null uint8 24 Age_4 891 non-null uint8 25 Fare_0 891 non-null uint8 26 Fare_1 891 non-null uint8 27 Fare_2 891 non-null uint8 28 Fare_3 891 non-null uint8
dtypes: float64(2), int64(3), uint8(24)
memory usage: 55.8 KB
我们终于可以建模了!
# 准备放入模型中的最终训练数据和测试数据
X_train = train_df.drop(["Survived","PassengerId"], axis=1)
Y_train = train_df["Survived"]
X_test = test_df.drop("PassengerId", axis=1).copy()
X_train.shape, Y_train.shape, X_test.shape
((891, 27), (891,), (418, 25))
x_1=X_train.columns
x_2=X_test.columns
[x for x in x_1 if x in x_1 and not x in x_2]
['Age_scaled', 'Fare_scaled']
del X_train["Age_scaled"]
del X_train["Fare_scaled"]
# Logistic Regression
logreg = LogisticRegression()
logreg.fit(X_train, Y_train)
Y_pred = logreg.predict(X_test)
acc_log = round(logreg.score(X_train, Y_train) * 100, 2)
acc_log
81.71
# 输出预测结果
lr_result = pd.DataFrame({"PassengerId": test_df["PassengerId"],"Survived": Y_pred})
lr_result.to_csv('../input/logistic_regression_bagging_predictions.csv', index=False)
from sklearn.ensemble import BaggingRegressor# fit到BaggingRegressor之中
logreg = LogisticRegression()
bagging_clf = BaggingRegressor(logreg, n_estimators=25, max_samples=0.8, max_features=1.0, bootstrap=True, bootstrap_features=False, n_jobs=-1)
ensemble = bagging_clf.fit(X_train, Y_train)
predictions = bagging_clf.predict(X_test)acc_bagging = round(ensemble.score(X_train, Y_train) * 100, 2)
acc_bagging
34.39
我们可以通过每个特征的相关系数的值来判断每个特征对于最终预测结果的影响程度。
coeff_df = pd.DataFrame(train_df.columns.delete(0))
coeff_df.columns = ['Feature']
coeff_df["Correlation"] = pd.Series(logreg.coef_[0]) # 获得特征的相关系数
coeff_df
---------------------------------------------------------------------------AttributeError Traceback (most recent call last)Input In [68], in ()1 coeff_df = pd.DataFrame(train_df.columns.delete(0))2 coeff_df.columns = ['Feature']
----> 3 coeff_df["Correlation"] = pd.Series(logreg.coef_[0]) # 获得特征的相关系数4 coeff_dfAttributeError: 'LogisticRegression' object has no attribute 'coef_'
|
# 根据相关系数的大小从高到底排序
coeff_df.sort_values(by='Correlation', ascending=False)
问题13:如何解释特征权重?从特征工程的角度,还有什么可以优化?
到目前为止,总算是把模型建完,得到了一个baseline的预测结果了!有没有尝试把自己跑出来的预测结果提交到Kaggle上去看看自己的名次?
难道你真的以为只要完成数据预处理,再用sklearn跑完所有可能的算法,挑选一个预测效果最好的结果提交,这样就大功告成了嘛?
看到自己现在的排名,没有想过进一步优化嘛?
万里长征才刚刚开始,我们还是继续干活儿吧!
交叉验证的基本思想是把在某种意义下将原始数据(dataset)进行分组,一部分做为训练集(train set),另一部分做为验证集(validation set or test set),首先用训练集对分类器进行训练,再利用验证集来测试训练得到的模型(model),以此来做为评价分类器的性能指标。
也就是说,我们把题目中所给的训练集当成一个原始数据,将这个数据切分成训练集和测试集。利用切分出的训练集进行训练,切分出的测试集进行验证。
1. 重复随机子抽样验证
将数据集随机的划分为训练集和测试集。对每一个划分,用训练集训练分类器或模型,用测试集评估预测的精确度。进行多次划分,用均值来表示效能。
优点:与k倍交叉验证相比,这种方法的与k无关。
缺点:有些数据可能从未做过训练或测试数据;而有些数据不止一次选为训练或测试数据。
2. K倍交叉验证(K>=2)
将样本数据集随机划分为K个子集(一般是均分),将一个子集数据作为测试集,其余的K-1组子集作为训练集;将K个子集轮流作为测试集,重复上述过程,这样得到了K个分类器或模型,并利用测试集得到了K个分类器或模型的分类准确率。用K个分类准确率的平均值作为分类器或模型的性能指标。10-倍交叉证实是比较常用的。
优点:每一个样本数据都即被用作训练数据,也被用作测试数据。避免的过度学习和欠学习状态的发生,得到的结果比较具有说服力。
3. 留一法交叉验证
假设样本数据集中有N个样本数据。将每个样本单独作为测试集,其余N-1个样本作为训练集,这样得到了N个分类器或模型,用这N个分类器或模型的分类准确率的平均数作为此分类器的性能指标。
优点:每一个分类器或模型都是用几乎所有的样本来训练模型,最接近样本,这样评估所得的结果比较可靠。实验没有随机因素,整个过程是可重复的。
缺点:计算成本高,当N非常大时,计算耗时。
from sklearn.model_selection import cross_val_score
from sklearn import linear_model#简单看看打分情况
clf = linear_model.LogisticRegression(C=1.0, penalty='l2', tol=1e-6)
X = train_df.values[:,1:]
y = train_df.values[:,0]
# 交叉验证的结果
cross_val_score(clf, X_train, Y_train, cv=5) # cv=5,5倍交叉验证
array([0.79888268, 0.80337079, 0.81460674, 0.78651685, 0.83146067])
我们已经进行了交叉验证,其实我们可以把交叉验证里预测错误的记录拿出来,通过人工审核,去帮助我们发现新的优化方向,继续探索。
对于以下方法对应的参数不了解的,可以参考官方文档说明:
linear_model.LogisticRegression官方文档说明
cross_validation.cross_val_score官方文档说明
cross_validation.train_test_split官方文档说明
from sklearn.model_selection import train_test_split# 分割数据,按照 训练数据:cv数据 = 7:3的比例
split_train, split_cv = train_test_split(train_df, test_size=0.3, random_state=0) # test_size测试集占数据的比例
train_df = split_train# 生成模型
clf = linear_model.LogisticRegression(C=1.0, penalty='l2', tol=1e-6)
clf.fit(train_df.values[:,1:], train_df.values[:,0])# 对cross validation数据进行预测
cv_df = split_cv
predictions = clf.predict(cv_df.values[:,1:])origin_data_train = pd.read_csv("../input/train.csv")# 交叉验证没有预测对的记录
bad_cases = origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.values[:,0]]['PassengerId'].values)]
bad_cases
D:\Anaconda3\lib\site-packages\sklearn\linear_model\_logistic.py:814: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.Increase the number of iterations (max_iter) or scale the data as shown in:https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:https://scikit-learn.org/stable/modules/linear_model.html#logistic-regressionn_iter_i = _check_optimize_result(
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
32 | 33 | 1 | 3 | Glynn, Miss. Mary Agatha | female | NaN | 0 | 0 | 335677 | 7.7500 | NaN | Q |
67 | 68 | 0 | 3 | Crease, Mr. Ernest James | male | 19.0 | 0 | 0 | S.P. 3464 | 8.1583 | NaN | S |
87 | 88 | 0 | 3 | Slocovski, Mr. Selman Francis | male | NaN | 0 | 0 | SOTON/OQ 392086 | 8.0500 | NaN | S |
88 | 89 | 1 | 1 | Fortune, Miss. Mabel Helen | female | 23.0 | 3 | 2 | 19950 | 263.0000 | C23 C25 C27 | S |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
858 | 859 | 1 | 3 | Baclini, Mrs. Solomon (Latifa Qurban) | female | 24.0 | 0 | 3 | 2666 | 19.2583 | NaN | C |
861 | 862 | 0 | 2 | Giles, Mr. Frederick Edward | male | 21.0 | 1 | 0 | 28134 | 11.5000 | NaN | S |
866 | 867 | 1 | 2 | Duran y More, Miss. Asuncion | female | 27.0 | 1 | 0 | SC/PARIS 2149 | 13.8583 | NaN | C |
879 | 880 | 1 | 1 | Potter, Mrs. Thomas Jr (Lily Alexenia Wilson) | female | 56.0 | 0 | 1 | 11767 | 83.1583 | C50 | C |
881 | 882 | 0 | 3 | Markun, Mr. Johann | male | 33.0 | 0 | 0 | 349257 | 7.8958 | NaN | S |
64 rows × 12 columns
观察这些预测错误的记录,提出新的优化方向,继续尝试挖掘,可能还可以想到更多可以细挖的部分。将新的特征和已有特征组合在一起,查看模型预测准确度是否有所提升。继续迭代下去。
问题14:观察这些预测错误的记录,你还能想到什么优化的方向?
仅供参考:
当我们不断地丰富特征时,模型对训练集拟合越来越好,有可能同时在丧失泛华能力,对测试集变现不佳,存在过拟合问题。而我们最终的目的是希望我们训练的出来的模型,不仅能对训练数据集有很好的预测效果,更希望它对测试数据集也有很好的预测效果。过拟合问题是机器学习建模过程中常见的问题。
实际上,如果模型在测试集上表现不佳,除了过拟合问题,也有可能出现欠拟合问题,也就是说在训练集上,其实拟合的也不是那么好。
举个来说:
过拟合就像是你班那个学数学比较刻板的同学,老师讲过的题目,一字不漏全记下来了,于是老师再出一样的题目,分分钟精确出结果。but数学考试,因为总是碰到新题目,所以成绩不咋地。
欠拟合就像是连老师讲的练习题也记不住的同学,于是连老师出一样题目复习的周测都做不好,考试更是可想而知了。
经典解释
解释1
解释2
在机器学习的问题上,对于过拟合和欠拟合两种情形。我们优化的方式是不同的。
对过拟合而言,通常以下策略对结果优化是有用的:
通常需要增加更多的特征,使模型变得更复杂来提高准确度。
著名的learning curve可以帮我们判定我们的模型现在所处的状态。
我们以样本数为横坐标,训练和交叉验证集上的错误率作为纵坐标,我们也可以把错误率替换成准确率(得分),得到另一种形式的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_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): # train_sizes训练集占数据的比例
# """
# 画出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"training_sample")
# plt.ylabel(u"cv_sample")
# 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_scores")
# plt.plot(train_sizes, test_scores_mean, 'o-', color="r", label=u"cv_scores")# 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# plot_learning_curve(clf, u"learning curve", X, y)
在实际数据上看,我们得到的learning curve没有理论推导的那么光滑哈,但是可以大致看出来,训练集和交叉验证集上的得分曲线走势还是符合预期的。
目前的曲线看来,我们的model并不处于overfitting的状态(overfitting的表现一般是训练集上得分高,而交叉验证集上要低很多,中间的gap比较大)。因此我们可以再做些feature engineering的工作,添加一些新产出的特征或者组合特征到模型中。
模型融合是机器学习/数据挖掘中经常使用到的一个利器,它通常可以在各种不同的机器学习任务中使结果获得提升。顾名思义,模型融合就是综合考虑不同模型的情况,并将它们的结果融合到一起。
举个例子来说,你和你班某数学大神关系好,每次作业都『模仿』他的,于是绝大多数情况下,他做对了,你也对了。突然某一天大神脑子犯糊涂,手一抖,写错了一个数,于是…恩,你也只能跟着错了。
我们再来看看另外一个场景,你和你班5个数学大神关系都很好,每次都把他们作业拿过来,对比一下,再『自己做』,那你想想,如果哪天某大神犯糊涂了,写错了,but另外四个写对了啊,那你肯定相信另外4人的是正确答案吧?
最简单的模型融合大概就是这么个意思,比如分类问题,当我们手头上有一堆在同一份数据集上训练得到的分类器(比如logistic regression,SVM,KNN,random forest,神经网络),那我们让他们都分别去做判定,然后对结果做投票统计,取票数最多的结果为最后结果。
模型融合可以比较好地缓解,训练过程中产生的过拟合问题,从而对于结果的准确度提升有一定的帮助。
问题15:模型融合实现:将逻辑回归和随机森林进行模型融合
思考题:
到目前为止,我们只实现了逻辑回归一个模型,这是时候如果我们想利用模型融合的思路提高模型的预测准确率,我们应该怎么做呢?
思路:
如果模型出现过拟合,一定是在我们的训练上出现拟合过度造成的对吧。
那我们干脆就不要用全部的训练集,每次取训练集的一个subset,做训练,这样,我们虽然用的是同一个机器学习算法,但是得到的模型却是不一样的;同时,因为我们没有任何一份子数据集是全的,因此即使出现过拟合,也是在子训练集上出现过拟合,而不是全体数据上,这样做一个融合,可能对最后的结果有一定的帮助。这就是常用的方法之一Bagging。
Bagging:
Bagging的特点在于随机采样,随机采样(bootsrap)就是从我们的训练集里面采集固定个数的样本,每采集一个样本后,都将样本放回,是有放回的随机抽样。
sklearn.ensemble.BaggingRegressor官方文档说明 回归器组合
sklearn.ensemble.BaggingClassifier官方文档说明 用于分类器组合
两个结果预测准确性效果差别很大,BaggingClassifier的结果更好一些,比最初的81.71提高了0.33.
代码参考如下:
from sklearn.ensemble import BaggingRegressor# fit到BaggingRegressor之中
logreg = LogisticRegression() # 基于baseline逻辑回归的结果
bagging_clf = BaggingRegressor(logreg, n_estimators=25, max_samples=0.8, max_features=1.0, bootstrap=True, bootstrap_features=False, n_jobs=-1)
ensemble = bagging_clf.fit(X_train, Y_train)
predictions = bagging_clf.predict(X_test)acc_BaggingRegressor = round(ensemble.score(X_train, Y_train) * 100, 2)
acc_BaggingRegressor
35.25
from sklearn.ensemble import BaggingClassifier# fit到BaggingClassifier之中
logreg = LogisticRegression() # 基于baseline逻辑回归的结果
bagging_clf = BaggingClassifier(logreg, n_estimators=25, max_samples=0.8, max_features=1.0, bootstrap=True, bootstrap_features=False, n_jobs=-1)
ensemble = bagging_clf.fit(X_train, Y_train)
predictions = bagging_clf.predict(X_test)acc_BaggingClassifier = round(ensemble.score(X_train, Y_train) * 100, 2)
acc_BaggingClassifier
81.03
# 保存预测结果
bagging_result = pd.DataFrame({"PassengerId": test_df["PassengerId"],"Survived": predictions})
bagging_result.to_csv('../input/logistic_regression_baggingRegressor_predictions.csv', index=False)
bagging_result.to_csv('../input/logistic_regression_baggingClassifier_predictions.csv', index=False)
实际上,除逻辑回归算法外,我们有60多个算法供我们选择。然而我们应该如何思考,今儿选择合适的算法进行尝试呢?
我们还可以使用mlxtend中的其他工具进行模型融合。
需要理解清楚我们正在解决的问题和所需给出的答案要求的类型,然后进一步缩小范围,最终去选择一些算法用于解决问题,并进一步来评估算法的预测结果。
问题界定:
通过以上两点,我们可以确定缩小范围,选择以下算法来预测结果:
- Logistic Regression
- KNN/k-Nearest Neighbors
- Support Vector Machines
- Naive Bayes classifier
- Decision Tree
- Random Forrest
- Perceptron
- RVM or Relevance Vector Machine
- Artificial neural network
根据大家已经学过的知识,不同的模型对放入模型中的特征值要求不同,处理方法不同。那么大家能不能基于上面我们已经完成的特征工程部分的思路,对数据进行相应的处理以适应不同算法,获得以上其他算法的预测结果,并验证是否过拟合进行相应的处理。
实际上,放入随机森林中的特征,只要根据上面特征工程部分进行相同的处理,最后一步不进行one-hot编码即可。在这里我们就不再给出具体代码了。
不同的模型对于放入模型中的特征有不同的要求,我们暂时先不管那么多,先暴力实现其他算法,开心开心。
说明:所得结果,均使用最终放入随机森林算法中的特征处理结果带入,仅供参考。
import sklearn.metrics as sm# Support Vector Machines
svc = SVC()
svc.fit(X_train, Y_train)
Y_pred = svc.predict(X_train)
# acc_svc = round(svc.score(Y_pred, Y_train) * 100, 2)
# acc_svc
print(sm.classification_report(Y_pred, Y_train))
precision recall f1-score support0 0.92 0.83 0.87 6061 0.70 0.84 0.76 285accuracy 0.83 891macro avg 0.81 0.83 0.82 891
weighted avg 0.85 0.83 0.84 891
# KNN
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X_train, Y_train)
Y_pred = knn.predict(X_train)
# acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
# acc_knn
print(sm.classification_report(Y_pred, Y_train))
precision recall f1-score support0 0.92 0.84 0.88 6001 0.72 0.85 0.78 291accuracy 0.84 891macro avg 0.82 0.85 0.83 891
weighted avg 0.86 0.84 0.85 891
# Gaussian Naive Bayes
gaussian = GaussianNB()
gaussian.fit(X_train, Y_train)
Y_pred = gaussian.predict(X_train)
# acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
# acc_gaussian
print(sm.classification_report(Y_pred, Y_train))
precision recall f1-score support0 0.78 0.85 0.81 5041 0.78 0.69 0.73 387accuracy 0.78 891macro avg 0.78 0.77 0.77 891
weighted avg 0.78 0.78 0.78 891
# Perceptron
perceptron = Perceptron()
perceptron.fit(X_train, Y_train)
Y_pred = perceptron.predict(X_train)
# acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)
# acc_perceptron
print(sm.classification_report(Y_pred, Y_train,digits=4))
precision recall f1-score support0 0.8725 0.7983 0.8338 6001 0.6462 0.7595 0.6983 291accuracy 0.7856 891macro avg 0.7593 0.7789 0.7660 891
weighted avg 0.7986 0.7856 0.7895 891
# Linear SVC
linear_svc = LinearSVC()
linear_svc.fit(X_train, Y_train)
Y_pred = linear_svc.predict(X_train)
# acc_linear_svc = round(linear_svc.score(X_train, Y_train) * 100, 2)
# acc_linear_svc
print(sm.classification_report(Y_pred, Y_train,digits=4))
precision recall f1-score support0 0.8743 0.8421 0.8579 5701 0.7368 0.7850 0.7602 321accuracy 0.8215 891macro avg 0.8056 0.8136 0.8090 891
weighted avg 0.8248 0.8215 0.8227 891D:\Anaconda3\lib\site-packages\sklearn\svm\_base.py:1206: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.warnings.warn(
# Stochastic Gradient Descent
sgd = SGDClassifier(max_iter=5000)
sgd.fit(X_train, Y_train)
Y_pred = sgd.predict(X_train)
# acc_sgd = round(sgd.score(X_train, Y_train) * 100, 2)
# acc_sgd
print(sm.classification_report(Y_pred, Y_train,digits=4))
precision recall f1-score support0 0.8871 0.8144 0.8492 5981 0.6754 0.7884 0.7276 293accuracy 0.8058 891macro avg 0.7813 0.8014 0.7884 891
weighted avg 0.8175 0.8058 0.8092 891
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScalerpipe_sgd = Pipeline([ ("scaler",StandardScaler() ),('sgd', SGDClassifier(loss="modified_huber",penalty="l1", max_iter=1000))])
pipe_sgd.fit(X_train,Y_train)
y_pre=pipe_sgd.predict(X_train)
y_pre_prob_=pipe_sgd.predict_proba(X_train)
y_pre_prob=[]
for i in range(len(y_pre)):if y_pre[i]==1:y_pre_prob.append(y_pre_prob_[i][1])else:y_pre_prob.append(1-y_pre_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
precision recall f1-score support0 0.9290 0.8644 0.8955 5901 0.7661 0.8704 0.8149 301accuracy 0.8664 891macro avg 0.8475 0.8674 0.8552 891
weighted avg 0.8739 0.8664 0.8683 8910.22110828951156772
# xgb
from xgboost.sklearn import XGBClassifier
from sklearn.model_selection import RandomizedSearchCVsample_weight=[]
for i in range(len(Y_train)):if Y_train[i]==0:sample_weight.append(1)else:sample_weight.append(2)
xgb= XGBClassifier(n_estimators=5000,max_depth=10,scale_pos_weight =0.3,learning_rate =0.01,gamma =0.05,subsample=0.7,colsample_bytree=0.4,min_child_weight=0.2)xgb.fit(X_train,Y_train)Y_pred=xgb.predict(X_train)
Y_pred_prob_=xgb.predict_proba(X_train)
Y_pred_prob=[]
for i in range(len(Y_pred)):if Y_pred[i]==1:Y_pred_prob.append(Y_pred_prob_[i][1])else:Y_pred_prob.append(1-Y_pred_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
precision recall f1-score support0 0.9891 0.7915 0.8794 6861 0.5819 0.9707 0.7276 205accuracy 0.8328 891macro avg 0.7855 0.8811 0.8035 891
weighted avg 0.8954 0.8328 0.8444 8910.19012825731208963
# Decision Tree
decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train, Y_train)
Y_pred = decision_tree.predict(X_train)
Y_pred_prob_=decision_tree.predict_proba(X_train)
Y_pred_prob=[]
for i in range(len(Y_pred)):if Y_pred[i]==1:Y_pred_prob.append(Y_pred_prob_[i][1])else:Y_pred_prob.append(1-Y_pred_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
precision recall f1-score support0 0.9344 0.8607 0.8961 5961 0.7573 0.8780 0.8132 295accuracy 0.8664 891macro avg 0.8459 0.8694 0.8546 891
weighted avg 0.8758 0.8664 0.8686 8910.1870742685334913
# Random Forest
random_forest = RandomForestClassifier(n_estimators=1000) # 定义决策树的个数为100
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_train)
Y_pred_prob_=random_forest.predict_proba(X_train)
Y_pred_prob=[]
for i in range(len(Y_pred)):if Y_pred[i]==1:Y_pred_prob.append(Y_pred_prob_[i][1])else:Y_pred_prob.append(1-Y_pred_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
precision recall f1-score support0 0.9253 0.8669 0.8952 5861 0.7719 0.8656 0.8161 305accuracy 0.8664 891macro avg 0.8486 0.8662 0.8556 891
weighted avg 0.8728 0.8664 0.8681 8910.19873776366870244
from sklearn.ensemble import AdaBoostClassifier,HistGradientBoostingRegressor,GradientBoostingClassifier# gbdt
gbdt = GradientBoostingClassifier(criterion="friedman_mse",n_estimators=1000,learning_rate=0.05)
gbdt.fit(X_train,Y_train)
Y_pred=gbdt.predict(X_train)
Y_pred_prob_=gbdt.predict_proba(X_train)
Y_pred_prob=[]
for i in range(len(Y_pred)):if Y_pred[i]==1:Y_pred_prob.append(Y_pred_prob_[i][1])else:Y_pred_prob.append(1-Y_pred_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
precision recall f1-score support0 0.9199 0.8707 0.8946 5801 0.7807 0.8585 0.8178 311accuracy 0.8664 891macro avg 0.8503 0.8646 0.8562 891
weighted avg 0.8713 0.8664 0.8678 8910.19961541192640012
#mlp
from sklearn.ensemble import AdaBoostClassifier,HistGradientBoostingRegressor,GradientBoostingClassifier# mlp
gbdt = MLPClassifier(solver='lbfgs', alpha=1e-5,hidden_layer_sizes=(32,16,4),learning_rate="invscaling",random_state=1)
gbdt.fit(X_train,Y_train)
Y_pred=gbdt.predict(X_train)
Y_pred_prob_=gbdt.predict_proba(X_train)
Y_pred_prob=[]
for i in range(len(Y_pred)):if Y_pred[i]==1:Y_pred_prob.append(Y_pred_prob_[i][1])else:Y_pred_prob.append(1-Y_pred_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
precision recall f1-score support0 0.9162 0.8672 0.8911 5801 0.7749 0.8521 0.8116 311accuracy 0.8620 891macro avg 0.8455 0.8597 0.8513 891
weighted avg 0.8669 0.8620 0.8633 8910.1949298895378452
问题16:神经网络实现
我们已经尝试使用这么多的算法来尝试解决问题,现在只差一个神经网络了。是时候自己动手,实现你的第一个神经网络了!
sklearn中已经有了成熟的实现。
我们将上面所有的模型结果进行排序,选择一个分数最高的模型作为最终预测结果。
决策树和随机森林的得分一样,我们最终选择随机森林,因为它也刚好修正了决策树对于训练集的过拟合问题。
models = pd.DataFrame({'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression', 'Random Forest', 'Naive Bayes', 'Perceptron', 'Stochastic Gradient Decent', 'Linear SVC', 'Decision Tree'],'Score': [acc_svc, acc_knn, acc_log, acc_random_forest, acc_gaussian, acc_perceptron, acc_sgd, acc_linear_svc, acc_decision_tree]})
models.sort_values(by='Score', ascending=False)
Model | Score | |
---|---|---|
3 | Random Forest | 86.64 |
8 | Decision Tree | 86.64 |
1 | KNN | 84.40 |
0 | Support Vector Machines | 83.28 |
7 | Linear SVC | 82.15 |
2 | Logistic Regression | 81.71 |
5 | Perceptron | 78.56 |
4 | Naive Bayes | 78.11 |
6 | Stochastic Gradient Decent | 76.88 |
问题17:实现逻辑回归以外的其他算法
到这里,我们把解决Titanic数据集问题的思路,全部带领同学们过了一遍。希望大家可以把除了逻辑回归以外的其他模型,都认真地自己动手实现一遍,以更好的理解每一个算法和使用机器学习解决问题中需要应对的问题和处理的方法。
from mlxtend.classifier import StackingClassifier
from sklearn.linear_model import SGDClassifier
sreg = StackingClassifier(verbose=2,classifiers=[xgb,decision_tree,gbdt,random_forest,pipe_sgd], meta_classifier=SGDClassifier( loss="log",penalty="l1", max_iter=5000)
)
sreg.fit(X_train, Y_train)
Y_pred = sreg.predict(X_train)Y_pred_prob_= sreg.predict_proba(X_train)Y_pred_prob=[]
for i in range(len(Y_pred)):if Y_pred[i]==1:Y_pred_prob.append(Y_pred_prob_[i][1])else:Y_pred_prob.append(1-Y_pred_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
Fitting 5 classifiers...
Fitting classifier1: xgbclassifier (1/5)
XGBClassifier(base_score=0.5, booster='gbtree', callbacks=None,colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,early_stopping_rounds=None, enable_categorical=False,eval_metric=None, feature_types=None, gamma=0, gpu_id=-1,grow_policy='depthwise', importance_type=None,interaction_constraints='', learning_rate=0.300000012,max_bin=256, max_cat_threshold=64, max_cat_to_onehot=4,max_delta_step=0, max_depth=10, max_leaves=0, min_child_weight=1,missing=nan, monotone_constraints='()', n_estimators=3000,n_jobs=0, num_parallel_tree=1, predictor='auto', random_state=0, ...)
Fitting classifier2: decisiontreeclassifier (2/5)
DecisionTreeClassifier()
Fitting classifier3: mlpclassifier (3/5)
MLPClassifier(alpha=1e-05, hidden_layer_sizes=(32, 16, 4),learning_rate='invscaling', random_state=1, solver='lbfgs')Fitting classifier4: randomforestclassifier (4/5)
RandomForestClassifier(n_estimators=1000)
Fitting classifier5: pipeline (5/5)
Pipeline(steps=[('scaler', StandardScaler()),('sgd', SGDClassifier(loss='modified_huber', penalty='l1'))])precision recall f1-score support0 0.9217 0.8694 0.8948 5821 0.7778 0.8608 0.8172 309accuracy 0.8664 891macro avg 0.8497 0.8651 0.8560 891
weighted avg 0.8718 0.8664 0.8679 8910.24045011034849822
from sklearn.neural_network import MLPClassifiersreg = StackingClassifier(verbose=2,classifiers=[xgb,decision_tree,gbdt,random_forest,pipe_sgd],meta_classifier=MLPClassifier(solver='lbfgs', alpha=1e-5,hidden_layer_sizes=(8,4,4),learning_rate="invscaling",random_state=1))
sreg.fit(X_train, Y_train)
Y_pred = sreg.predict(X_train)
Y_pred_prob_= sreg.predict_proba(X_train)Y_pred_prob=[]
for i in range(len(Y_pred)):if Y_pred[i]==1:Y_pred_prob.append(Y_pred_prob_[i][1])else:Y_pred_prob.append(1-Y_pred_prob_[i][0])print(sm.classification_report(Y_pred, Y_train,digits=4))
print(sm.mean_absolute_error(Y_pred_prob,Y_train))
Fitting 5 classifiers...
Fitting classifier1: xgbclassifier (1/5)
XGBClassifier(base_score=0.5, booster='gbtree', callbacks=None,colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,early_stopping_rounds=None, enable_categorical=False,eval_metric=None, feature_types=None, gamma=0, gpu_id=-1,grow_policy='depthwise', importance_type=None,interaction_constraints='', learning_rate=0.300000012,max_bin=256, max_cat_threshold=64, max_cat_to_onehot=4,max_delta_step=0, max_depth=5, max_leaves=0, min_child_weight=1,missing=nan, monotone_constraints='()', n_estimators=3000,n_jobs=0, num_parallel_tree=1, predictor='auto', random_state=0, ...)
Fitting classifier2: decisiontreeclassifier (2/5)
DecisionTreeClassifier()
Fitting classifier3: mlpclassifier (3/5)
MLPClassifier(alpha=1e-05, hidden_layer_sizes=(32, 16, 4),learning_rate='invscaling', random_state=1, solver='lbfgs')Fitting classifier4: randomforestclassifier (4/5)
RandomForestClassifier(n_estimators=1000)
Fitting classifier5: pipeline (5/5)
Pipeline(steps=[('scaler', StandardScaler()),('sgd', SGDClassifier(loss='modified_huber', penalty='l1'))])precision recall f1-score support0 0.9162 0.8733 0.8942 5761 0.7865 0.8540 0.8189 315accuracy 0.8664 891macro avg 0.8514 0.8636 0.8565 891
weighted avg 0.8704 0.8664 0.8676 8910.22000066108562064
submission = pd.DataFrame({"PassengerId": test_df["PassengerId"],"Survived": Y_pred})
submission.to_csv('../input/submission.csv', index=False)
通过这个项目,你觉得哪些工作对模型预测准确度的提升有较大影响?