最近在学习如何打数据挖掘比赛,感觉以前自己根本没有分析的去做比赛,因此我在重温之前的一些比赛,想看一下大神的思路是怎样的,今天这个比赛就是kaggle的入门比赛:泰坦尼克号比赛。虽然是入门的,但是有太多的大神对这份数据提出了自己的见解,今天我看的这份报告是一个完全版的分析,英文版的,我想把它翻译过来,也记录一下大神的思路。(如果这是侵权,我就删除了。。。)
工作流程
竞争解决方案工作流程经历了数据科学解决方案一书中描述的七个阶段。
1.问题或问题的定义。
2.获取训练集和测试集。
3.讨论,准备,清理数据。
4.分析,识别模式并探索数据。
5.建模,预测和解决问题。
6.可视化,报告和呈现问题解决步骤和最终解决方案。
7.提供或提交结果。
注意:工作流程指示每个阶段如何跟随另一个阶段的一般顺序。 但是有一些例外的用例。这里就不举例了,进入我们的比赛分析。
问题和问题的定义
泰坦尼克号生存竞赛的问题或问题定义在Kaggle中有所描述。
根据列出在泰坦尼克号灾难中幸存或未幸存的乘客的一组训练样本,我们的模型可以根据不包含生存信息的给定测试数据确 定测试数据集中的这些乘客是否存活。
我们可能还希望对我们问题的领域有一些早期的了解。 这在Kaggle比赛描述页面上有所描述。 以下是要注意的要点。
1.1912年4月15日,在她的处女航中,泰坦尼克号在与冰山相撞后沉没,在2224名乘客和机组人员中造成1502人死亡。 翻译成功率为32%。
2.造成海难失事的原因之一是乘客和机组人员没有足够的救生艇。
3.尽管幸存下沉有一些运气因素,但有些人比其他人更容易生存,比如女人,孩子和上流社会。
工作流程目标
数据科学解决方案工作流程解决了七个主要目标。
分类。 我们可能希望对样本进行分类。 我们可能还想了解不同类的含义或相关性与我们的解决方案目标。
相关。 可以基于训练数据集中的可用特征来解决问题。 数据集中的哪些特征对我们的解决目标有重大贡献? 从统计上讲,特征和解决目标之间是否存在相关性? 随着特征值的变化,解决方案状态也会发生变化,反之亦然? 这可以针对给定数据集中的数字和分类特征进行测试。 我们可能还希望确定除后续目标和工作流程阶段的生存之外的特征之间的相关性。 关联某些功能可能有助于创建,完成或更正功能。
转换。 对于建模阶段,需要准备数据。 根据模型算法的选择,可能需要将所有特征转换为数值等效值。 例如,将文本分类值转换为数值。
完成。 数据准备可能还需要我们估计要素中的任何缺失值。 当没有缺失值时,模型算法可能最有效。
纠正。 我们还可以分析给定的训练数据集中的错误或可能在特征内删除值,并尝试纠正这些值或排除包含错误的样本。 一种方法是检测我们的样本或特征中的任何异常值。 如果某项特征无法进行分析,或者可能会严重影响结果,我们也可能会完全丢弃该特征。
创建。 我们是否可以基于现有特征或一组特征创建新特征,以便新特征遵循关联,转换和完整性目标。
图表。 如何根据数据的性质和解决方案目标选择正确的可视化图表和图表。
(这几个目标真的是每次做比赛时都应该进行思考的目标!!!)
# data analysis and wrangling
import pandas as pd
import numpy as np
import random as rnd
# visualization
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
# machine learning
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
train_df = pd.read_csv('../input/train.csv')
test_df = pd.read_csv('../input/test.csv')
combine = [train_df, test_df]
导入相关库,导入数据。把训练集和测试集连接(感觉用pd.concat()会好一点)
通过描述数据进行分析
Pandas还有助于在我们的项目早期描述数据集回答以下问题。
数据集中有哪些特征?
print(train_df.columns.values)
['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch'
'Ticket' 'Fare' 'Cabin' 'Embarked']
哪些特征是类别型的?
这些值将样本分类为类似的集合。 在分类特征中,基于名字,序数,比率或区间的值? 除此之外,这有助于我们选择适当的可视化图。
- Categorical: Survived, Sex, and Embarked. Ordinal: Pclass.
哪些特征是数字型的?
哪些特征是数字的? 这些值随样本变化而变化。 在数值特征中,值是离散的,连续的还是基于时间序列的? 除此之外,这有助于我们选择适当的可视化图。
- Continous: Age, Fare. Discrete: SibSp, Parch.
# preview the data
train_df.head()
哪些特征是混合数据类型?
同一特征内含有数字,字母数字数据。 这些是纠正目标的候选。
- Ticket is a mix of numeric and alphanumeric data types. Cabin is alphanumeric.
哪些特征可能包含错误或拼写错误?
对于大型数据集来说,这很难检查,但是从较小的数据集中查看一些样本可能只是告诉我们,哪些特征可能需要更正。
name特征可能包含错误或拼写错误,因为有多种方法可用于描述name,包括标题,圆括号和用于替代或短名称的引号。
哪些特征包含空值?
These will require correcting.
- Cabin > Age > Embarked features contain a number of null values in that order for the training dataset.
- Cabin > Age are incomplete in case of test dataset.
各种特征的类型是什么?
Helping us during converting goal.
- Seven features are integer or floats. Six in case of test dataset.
- Five features are strings (object).
train_df.info()
print('_'*40)
test_df.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
________________________________________
<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
样本中数值特征值的分布是什么?
这有助于我们在早期见解中确定实际问题域的训练数据集的代表性。
1.总样本是泰坦尼克号(2,224)上实际乘客人数的891或40%。
2.Survived是一个具有0或1值的分类特征。
3.大约38%的样本存活,实际存活率为32%(所有的)。
4.大多数乘客(> 75%)没有与父母或孩子一起旅行。
5.近30%的乘客有兄弟姐妹和/或配偶。
6.票价差异很大,很少有乘客(<1%)支付高达512美元。
train_df.describe()
# Review survived rate using `percentiles=[.61, .62]` knowing our problem description mentions 38% survival rate.
# Review Parch distribution using `percentiles=[.75, .8]`
# SibSp distribution `[.68, .69]`
# Age and Fare `[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99]`
(这是describe()函数的使用,它的参数percentiles可以设定数值型数据显示范围,以上结论都是这样得出的)
分类特征的分布是什么?
1.名字在整个数据集中是唯一的(count = unique = 891)
2.性别为两个可能的值,男性为65%(top=male, freq=577/count=891)。
3.客舱值在样本中有几个重复值。 或者,几名乘客共用一间小屋。
4.Embarked有三个可能的值。 大多数乘客使用的S端口(top= S)
5.船票编号特征具有高重复率(22%)的重复值(unique= 681)。
train_df.describe(include=['O'])
基于数据分析的假设
我们基于迄今为止所做的数据分析得出以下假设。 我们可能会在采取适当行动之前进一步验证这些假设。
关联:我们想知道每个特征与生存的相关性。 我们希望在项目的早期阶段完成这项工作,并将这些快速关联与项目后期的建模关联相匹配。
完成:我们可能希望完成年龄特征,因为它与生存明确相关。
我们可能希望完成“登船舱口”功能,因为它还可能与生存或其他重要功能相关联。
正确:1.船票编号特征可能会从我们的分析中删除,因为它包含高比例的重复项(22%),并且和生存之间可能没有相关性。
2.客舱号可能会因为缺失太多而被删除,或者在训练和测试数据集中包含许多空值。
3.PassengerId可能会从训练数据集中删除,因为它对生存没有贡献。
4.名字特征相对不标准,可能无法直接促成生存,因此可能会删除。
创造:1.我们可能想要创建一个名为Family的基于Parch和SibSp的新特征,以获得船上家庭成员的总数。
2.我们可能希望抽取标题信息设计一个name的新特征。
3.我们可能想为Age划分层次。 这将连续的数字特征转换为序数分类特征。
4.如果它有助于我们的分析,我们可能还想创建一个票价范围功能。
分类:我们还可以根据前面提到的问题描述添加我们的假设。
1.女性(性别=女性)更有可能幸存下来。
2.儿童(年龄<?)更有可能幸存下来。
3.上层乘客(Pclass = 1)更有可能幸存下来。
通过旋转特征进行分析
为了确认我们的一些观察和假设,我们可以通过相互转动特征来快速分析我们的特征相关性。 我们只能在此阶段为没有任何空值的功能执行此操作。 对于分类(性别),序数(Pclass)或离散(SibSp,Parch)类型的特征,这样做也是有意义的。
1.Pclass我们观察到Pclass = 1和Survived(分类#3)之间的显着相关性(> 0.5)。 我们决定在我们的模型中包含此功能。
2.性别我们在问题定义中确认性别=女性的存活率非常高,为74%(分类#1)。
3.SibSp和Parch这些特征对于某些值具有零相关性。 最好从这些单独的特征中导出一个特征或一组特征(创建#1)。
train_df[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)
train_df[["Sex", "Survived"]].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)
train_df[["SibSp", "Survived"]].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)
train_df[["Parch", "Survived"]].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)
通过可视化分析数据
现在,我们可以继续使用可视化来确认我们的一些假设来分析数据。
关联数字特征
让我们首先了解数值特征与我们的解决方案目标(生存)之间的相关性。
直方图可用于分析像Age这样的连续数值变量,其中条带或范围将有助于识别有用的模式。 直方图可以使用自动定义的区间或等距离范围来指示样本的分布。 这有助于我们回答有关年龄层次的问题(婴儿的生存率是否更高?)
请注意,历史图表可视化中的x轴表示样本或乘客的数量。
观察:
1.婴儿(年龄<= 4)的存活率很高。
2.最年长的乘客(年龄= 80岁)幸免于难。
3.大量15-25岁的人没有活下来。
4.大多数乘客年龄在15-35岁之间。
结论:这种简单的分析证实了我们作为后续工作流程阶段决策的假设。
1.我们应该在模型训练中考虑Age(我们的假设分类#2)。
2.完成Age空值的填充(完成#1)。
3.我们应该给年龄划分层次(创建#3)。
g = sns.FacetGrid(train_df, col='Survived')
g.map(plt.hist, 'Age', bins=20)
关联数字和序数特征
我们可以使用单个图组合多个特征来识别相关性。 这可以通过具有数值的数字和分类特征来完成。
观察:
1.Pclass = 3有大多数乘客,但多数人没有幸存。 确认我们的分类假设#2。
2.Pclass = 2和Pclass = 3的婴儿乘客大多幸免于难。 进一步限定了我们的分类假设#2。
3.Pclass = 1的大多数乘客幸免于难。 确认我们的分类假设#3。
4.Pclass在乘客年龄分布方面有所不同。
结论:
在训练中考虑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();
关联分类特征 :现在我们可以将分类特征与我们的解决方案目标相关联。
观察:
1.女性乘客的生存率远高于男性。 确认分类(#1)。
2.在Embarked = C中的情况例外,其中男性的存活率更高。 这可能是Pclass和Embarked之间的相关性,反过来是Pclass和 Survived,不一定是Embarked和Survived之间的直接相关。
3.与Cc = Q的Pclass = 2相比,男性在Pclass = 3时的存活率更高。 完成(#2)。
4.登船口岸的Pclass = 3和男性乘客的生存率各不相同。 相关(#1)。
结论:
1.将性别特征加入到训练中。
2.将登船港口特征填充缺失值并加入到训练中。
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()
关联分类和数字特征
我们可能还想关联分类特征(使用非数字值)和数字特征。 我们可以考虑将登船港口(分类非数字),性别(分类非数字),票价(数字连续)与幸存(分类数字)相关联。
观察:
1.付费较高的乘客有更好的生存。 确认我们创建(#4)票价范围的假设。
2.登船港与生存率相关。 确认相关(#1)和完成(#2)。
结论:
考虑价格划分层次。
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()
Wrangle 数据
我们收集了有关数据集和解决方案要求的若干假设和决策。 到目前为止,我们没有必要更改单个特征或值来实现这些功能。 现在让我们执行我们的决策和假设,以纠正,创建和完成目标。
通过删除特征进行更正
这是一个很好的起始目标。 通过删除特征,我们处理的数据点更少。 加速我们的计算并简化分析。
根据我们的假设和决定,我们希望放弃Cabin(纠正#2)和Ticket(纠正#1)特征。
请注意,在适用的情况下,我们同时对训练和测试数据集执行操作以保持一致。
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]
"After", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape
Before (891, 12) (418, 11) (891, 12) (418, 11)
Out[17]:
('After', (891, 10), (418, 9), (891, 10), (418, 9))
从存在的特征中创建新特征
我们想要分析是否可以设计name特征来提取标题并测试标题和生存之间的相关性,然后再删除Name和PassengerId特征。
在下面的代码中,我们使用正则表达式提取标题特征。 RegEx模式(\ w + \。)匹配name特征中以点字符结尾前的第一个单词。 expand = False标志返回一个DataFrame。
观察:
当我们绘制Title,Age和Survived时,我们注意到以下观察结果。
1.大多数头衔准确地围绕年龄组 例如:Master标题的年龄均值为5年。
2.标题年龄段的生存率略有不同。
3.某些标题大多存活下来(Mme,Lady,Sir)或者没有(Don,Rev,Jonkheer)。
结论:
我们可以保留这个新的title特征。
for dataset in combine:
dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False)
pd.crosstab(train_df['Title'], train_df['Sex'])
我们可以用更常见的名称替换许多标题或将它们归类为Rare。
for dataset in combine:
dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',\
'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss')
dataset['Title'] = dataset['Title'].replace('Ms', 'Miss')
dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')
train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
我们可以将分类标题转换为序数。
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)
现在我们可以安全地从训练和测试数据集中删除Name特征。 我们也不需要训练数据集中的PassengerId特征。
train_df = train_df.drop(['Name', 'PassengerId'], axis=1)
test_df = test_df.drop(['Name'], axis=1)
combine = [train_df, test_df]
转换分类特征
现在我们可以将包含字符串的特征转换为数值。 这是大多数模型算法所必需的。 这样做也有助于我们实现功能完成目标。
让我们首先将性别特征转换为名为Gender的新特征,其中女性= 1,男性= 0。
for dataset in combine:
dataset['Sex'] = dataset['Sex'].map( {'female': 1, 'male': 0} ).astype(int)
填充数字连续特征
现在我们应该开始估计和完成缺少值或空值的特征。 我们将首先针对Age特征执行此操作。
我们可以考虑三种方法来完成数值连续特征。
1.一种简单的方法是在均值和标准差之间生成随机数。
2.猜测缺失值的更准确方法是使用其他相关特征。 在我们的例子中,我们注意到Age,Gender和Pclass之间的相关性。 使用 Pclass和Gender特征组合的集合使用Age的中值来猜测年龄值。 因此,Pclass的中位数年龄= 1且性别= 0,Pclass = 1且性别 = 1,依此类推......
3.结合方法1和2.不是基于中位数来猜测年龄值,而是根据Pclass和Gender组合的集合使用均值和标准差之间的随机数。
方法1和3将随机噪声引入我们的模型。 多次执行的结果可能会有所不同。 我们更喜欢方法2。
grid = sns.FacetGrid(train_df, row='Pclass', col='Sex', size=2.2, aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend()
让我们首先准备一个空数组,以包含基于Pclass x Gender组合的猜测Age值。
guess_ages = np.zeros((2,3))
guess_ages
Out[24]:
array([[ 0., 0., 0.],
[ 0., 0., 0.]])
现在我们迭代Sex(0或1)和Pclass(1,2,3)来计算六种组合的Age的猜测值。
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()
# Convert random age float to nearest .5 age
guess_ages[i,j] = int( age_guess/0.5 + 0.5 ) * 0.5
for 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)
让我们创建Age 层次并确定与Survived的相关性。
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)
让我们根据这些划分层次替换Age。
for dataset in combine:
dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0
dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1
dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2
dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3
dataset.loc[ dataset['Age'] > 64, 'Age']
train_df = train_df.drop(['AgeBand'], axis=1)
combine = [train_df, test_df]
train_df.head()
组合特征创造新特征
我们可以为FamilySize创建一个新特征,它结合了Parch和SibSp。 这将使我们能够从数据集中删除Parch和SibSp。
for dataset in combine:
dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False)
我们可以创建另一个名为IsAlone的特征。
for dataset in combine:
dataset['IsAlone'] = 0
dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1
train_df[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()
让我们放弃Parch,SibSp和FamilySize特征,转而使用IsAlone。
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]
我们还可以创建一个结合了Pclass和Age的人工特征。
for dataset in combine:
dataset['Age*Class'] = dataset.Age * dataset.Pclass
train_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)
补全分类特征
登船港口根据登船港口获取S,Q,C值。 我们的训练数据集有两个缺失值。 我们只是填写最常见的。
freq_port = train_df.Embarked.dropna().mode()[0]
freq_port
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特征。
for dataset in combine:
dataset['Embarked'] = dataset['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)
快速完成并转换数字特征
现在我们可以开始为测试集里的Fare特征补全数据。在补全时,我们可以使用最频繁出现的数据用于补全缺失值。
请注意,由于我们只替换单个值,因此我们不会创建中间新特征或进行任何进一步的相关分析以猜测缺失特征。 完成目标实现了模型算法对非空值进行操作的期望要求。(Fare只缺失了一个)
我们可能还希望将票价四舍五入到两位小数,因为它代表货币。
test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)
创建Fareband
train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)
train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)
根据FareBand将票价特征转换为序数值。
for dataset in combine:
dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0
dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare'] = 2
dataset.loc[ dataset['Fare'] > 31, 'Fare'] = 3
dataset['Fare'] = dataset['Fare'].astype(int)
train_df = train_df.drop(['FareBand'], axis=1)
combine = [train_df, test_df]
建模,预测和解决
现在我们准备训练模型并预测所需的解决方案。 有60多种预测建模算法可供选择。 我们必须了解问题的类型和解决方案要求,以缩小到我们可以评估的少数几个模型。 我们的问题是分类和回归问题。 我们想要确定输出(生存与否)与其他变量或特征(性别,年龄,港口......)之间的关系。 我们还开展了一种机器学习,称为监督学习,因为我们正在使用给定的数据集训练我们的模型。 有了这两个标准 - 监督学习加分类和回归,我们可以将我们选择的模型缩小到几个。 这些包括:
- Logistic Regression
- KNN or k-Nearest Neighbors
- Support Vector Machines
- Naive Bayes classifier
- Decision Tree
- Random Forrest
- Perceptron
- Artificial neural network
- RVM or Relevance Vector Machine
X_train = train_df.drop("Survived", 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
Out[40]:
((891, 8), (891,), (418, 8))
Logistic回归是在工作流程早期运行的有用模型。 Logistic回归通过使用逻辑函数(累积逻辑分布)估计概率来测量分类因变量(特征)与一个或多个自变量(特征)之间的关系。 参考维基百科。
请注意模型根据我们的训练数据集生成的置信度分数。
# 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
80.359999999999999
我们可以使用Logistic回归来验证我们对功能创建和完成目标的假设和决策。 这可以通过计算决策函数中的特征的系数来完成。
正系数增加响应的对数几率(从而增加概率),负系数减小响应的对数几率(从而降低概率)。
1.性别是最高的积极系数,暗示随着性别值的增加(男性:0到女性:1),存活率= 1的概率增加最多。
2.相反,随着Pclass的增加,Survived = 1的概率降低最多。
3.这样,Age * Class是一个很好的人工模型,因为它与Survived具有第二高的负相关。
4.标题是第二高的正相关。
coeff_df = pd.DataFrame(train_df.columns.delete(0))
coeff_df.columns = ['Feature']
coeff_df["Correlation"] = pd.Series(logreg.coef_[0])
coeff_df.sort_values(by='Correlation', ascending=False)
接下来,我们使用支持向量机进行建模,支持向量机是带有相关学习算法的监督学习模型,用于分析用于分类和回归分析 给定一组训练样本,每个训练样本被标记为属于两个类别中的一个或另一个,SVM训练算法构建将新测试样本分配给一个类别或另一个类别的模型,使其成为非概率二元线性分类器。 参考维基百科。
请注意,该模型生成的置信度得分高于“物流回归”模型。
svc = SVC()
svc.fit(X_train, Y_train)
Y_pred = svc.predict(X_test)
acc_svc = round(svc.score(X_train, Y_train) * 100, 2)
acc_svc
83.840000000000003
在模式识别中,k-Nearest Neighbors算法(或简称k-NN)是用于分类和回归的非参数方法。 样本按其邻居的多数票进行分类,样本被分配给其k个最近邻居中最常见的类(k是正整数,通常很小)。 如果k = 1,则简单地将对象分配给该单个最近邻居的类。 参考维基百科。
KNN置信度得分优于逻辑回归,但比SVM差。
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X_train, Y_train)
Y_pred = knn.predict(X_test)
acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
acc_knn
84.739999999999995
朴素贝叶斯分类器具有高度可扩展性,在学习问题中需要多个变量(特征)数量的线性参数。 参考维基百科。
到目前为止,模型生成的置信度得分是评估模型中最低的。
# Gaussian Naive Bayes
gaussian = GaussianNB()
gaussian.fit(X_train, Y_train)
Y_pred = gaussian.predict(X_test)
acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
acc_gaussian
Out[45]:
72.280000000000001
感知器是用于二元分类器的监督学习的算法(可以决定由数字向量表示的输入是否属于某个特定类的函数)。 它是一种线性分类器,即分类算法,其基于将一组权重与特征向量组合的线性预测器函数进行其预测。 该算法允许在线学习,因为它一次一个地处理训练集中的元素。 参考维基百科。
# Perceptron
perceptron = Perceptron()
perceptron.fit(X_train, Y_train)
Y_pred = perceptron.predict(X_test)
acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)
acc_perceptron
78.0
该模型使用决策树作为预测模型,将特征(树枝)映射到关于目标值(树叶)的结论。 目标变量可以采用有限值集的树模型称为分类树; 在这些树结构中,叶子表示类标签,分支表示导致这些类标签的特征的连接。 目标变量可以采用连续值(通常是实数)的决策树称为回归树。 参考维基百科。
到目前为止评估的模型中模型置信度得分最高。
# Decision Tree
decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train, Y_train)
Y_pred = decision_tree.predict(X_test)
acc_decision_tree = round(decision_tree.score(X_train, Y_train) * 100, 2)
acc_decision_tree
下一个模型随机森林是最受欢迎的模型之一。 随机森林或随机决策森林是用于分类,回归和其他任务的集成学习方法,其通过在训练时构建多个决策树(n_estimators = 100)并输出作为类的模式的类(分类)来操作。 或者表示各树的预测(回归)。 参考维基百科。
到目前为止评估的模型中模型置信度得分最高。 我们决定使用此模型的输出(Y_pred)来创建我们的竞赛结果提交
# Random Forest
random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_test)
random_forest.score(X_train, Y_train)
acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)
acc_random_forest
模型评估
我们现在可以对所有模型进行评估,以便为我们的问题选择最佳模型。 虽然决策树和随机森林得分相同,但我们选择使用随机森林来纠正决策树过度拟合其训练集的习惯。
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)
submission = pd.DataFrame({
"PassengerId": test_df["PassengerId"],
"Survived": Y_pred
})
我们向竞赛网站Kaggle提交的结果为6,082个参赛作品中的3,883个得分。 在比赛开始时,这个结果是指示性的。 此结果仅占提交数据集的一部分。 对我们的第一次尝试来说不错。 我们欢迎任何提高我们分数的建议。
########################################################################################################################################################################################################
这可能是我写的最多的一篇博客了,介绍了方案之后,我在反思我们可以从中学到什么,可以看出作者提出了一些富有创造力的特征,对年龄的缺失值的填充方式也十分有想法。包括分段的思想等等。
当然,作者的代码也存在一些问题,首先我们可以看到他的模型没有划分出验证集,这就导致了过拟合根本没有被考虑,线上线下的差距会很大。此外,对于很多类别型特征,应该进行哑编码好一点,在这篇文章的下面我看了很多评论,有的还指出分段的合理性与否。都是值得商榷的,总的来说我觉得最应该学会的是解决思路的建立。