Kaggle数据分析/挖掘实战:二分类问题 Titanic 数据集,四种模型 Train AUC均分84 kaggle Score 均分77 ,从泰坦尼克号的灾难中学习机器学习
Kaggle 数据分析挖掘实战,二分类问题,四种模型 Kaggle Score 均分77。本专栏内容如果有新的更好的方法会不断更新,如果有友友有更好的处理方式得到更高的分,也同样欢迎评论。
本专栏内容如果有新的更好的方法会不断更新,如果有友友有更好的处理方式得到更高的分,也同样欢迎评论
本节是 Kaggle 实战系列的第一节,在本系列中,我们都会使用 Jupyter notebook 作为我们书写代码的工具。而本对于本系列的开篇,我们将从有监督学习分类任务中,最简单的二分类任务,即要么是 A 要么是 B,进行正式的入门数据分析与挖掘的世界。
一. 序言
比赛链接:Kaggle Titanic 比赛网站
比赛描述(简化):你的工作是预测一名乘客是否在泰坦尼克号沉没后幸存下来,对于测试集中的每个变量,您必须预测其值为 0 或 1,其中:1 表示存活,0 表示死亡。预测的结果应该提交一个包含 418 个条目和一个标题行的 csv 文件。如果您有额外的列(超出 PassengerId 和 Survived )或行,您的提交将显示错误。最终,你的分数是你正确预测的乘客百分比。这被称为准确性。
拿到任务后,对于大部分的这类比赛,首先一点就是要明确我们的目的是干什么?对于这个任务而言,我们的任务就是去预测哪些人能够活下来。
明确了任务后,下一步就是看有哪些可以利用来判断的数据呢?这点,我们可以在比赛中的 Data 处找到对应的特征,一共 11 个数据值,如下:
特征 | 含义 |
---|---|
Name | 姓名 |
survival | 是否存活 |
Pclass | 门票等级 |
Sex | 性别 |
Age | 年龄 |
SibSp | 泰坦尼克号上的兄弟姐妹/配偶数量 |
Parch | 泰坦尼克号上的父母/孩子数量 |
Ticket | 票号 |
Fare | 乘客票价 |
Cabin | 房间号 |
Embarked | 登船港口 |
有了这些基础信息过后,我们就可以开始第一步了。
二. 交叉验证–分层 k 折交叉检验
首先我们先读取我们的数据
# 导入数据集合
import pandas as pd
df_train = pd.read_csv('train.csv')
df_test = pd.read_csv('test.csv')
在建立模型之前,我们需要先确定交叉验证的方法,即划分数据集的方法以及如何最佳的划分。一个好的交叉验证可以帮助我们确保模型准确拟合数据,同时确保我们不会过拟合。交叉检验有许多不同的方法,选择正确的交叉检验取决于所处理的数据集,当然在一个数据集上适用的交叉检验也可能不适用于其他数据集。常用的方法如下:
- K 折交叉验证
- 分层 K 折交叉验证
- 分组 k 折交叉检验
- 暂留交叉检验
- 留一交叉检验
其中,K 折交叉验证是最为常用使用范围最广的,我们在此也会使用,具体的交叉检验方法的讨论不在此章节。
但对于分类问题而言,使用 K 折交叉验证需要先确认我们的分类目标数目是否均等或近似均等,如果并非均等的,出现某一类型特别多或某一类型特别少,就可能会导致最后划分出来的不同数据集中,某个数据集全是数量多的那一类型,或者全是少的,这并不是我们训练时所希望看到的,我们希望最终用于批次训练的数据集中,各个分类目标的数量都是均等的或者比例相等的。
因此,我们需要根据分类目标 'Survived'
画出其对应的柱状图,来进行判断。
# 观察二分类分布情况,决定使用什么交叉验证
import matplotlib.pyplot as plt
# 统计该列的二分类别数目
TRcategory_counts = df_train['Survived'].value_counts()
print(TRcategory_counts)
# 使用 matplotlib 绘制柱状图
plt.figure(figsize=(6,4))
TRcategory_counts.plot(kind='bar', color=['skyblue', 'lightgreen'])
plt.title('Category Counts')
plt.xlabel('Category')
plt.ylabel('Count')
plt.xticks(rotation=0)
plt.show()
print输出:
Survived
0 549
1 342
Name: count, dtype: int64
根据图片,我们可以很明显的发现这是一个偏斜的二元分类数据集,即两个分类目标的数量在数据集中的分布是不等的。因此,我们选择使用分层 K 折交叉验证。分层 K 折交叉验证会保证划分出来的每一个用于批次训练的小数据集中,分类目标的数量比例是相同的。其对应细节不在此处展开,在这里,我们可以直接使用下列库进行调用。
from sklearn.model_selection import StratifiedKFold
# n_splits=5,表示划分为 5 个部分
# shuffle,表示每次划分之间随机打乱数据
# random_state,明确 shuffle 随机打乱的种子,方便后续复现
skf = StratifiedKFold(n_splits=5,shuffle=True,random_state=42)
第一步,完成。
三. 评估指标–AUC
评估指标的选择既可以在最后决定,也可以在这里顺便决定完成。评估的指标非常多,甚至在特别领域也有其对应的特殊评估指标。对于分类任务而言,常用的分类指标为:
- 准确率(Accuracy)
- 精确率(P)
- 召回率(R)
- F1 分数(F1)
- AUC(AUC)
- 对数损失(Log loss)
其中,F1 分数与召回率,准确率和精确率往往是一起出现的。
对于评估指标而言,我们除了需要了解上述指标的工作原理,也需要了解何时去使用他们,这取决于我们有什么样的数据和什么样的目标。在第一步中我们知道,我们拥有的是偏斜的二元分类数据集。使用 F1 等指标去评估是不合适的,因此我们使用AUC 作为评估指标。AUC 的值范围是:0~1.0 之间。
- 当 AUC 非常接近 1 时:往往说明,你拥有一个很好的模型
- 当 AUC 非常接近 0 时:这说明你的模型可能非常糟糕,也可能非常好,这个时候你需要尝试反转预测概率再进行更进一步的验证。
- 当 AUC 在 0.5 附近时,对于任何二元分类问题而言,模型的预测就是随机的。对于 n 元分类,则 1/n 的值,对应这个情况。
- 对于 AUC 在 0~0.5 之间的值,你模型效果可能不好,也可能是好的,同样可以尝试反转概率。
使用 AUC 进行评估,有两种方法可以使用:
# 第一种:
from sklearn import metrics
auc = metrics.roc_auc_score(Y_test, Y_pred)
# 第二种:
from sklearn.model_selection import cross_val_score
auc_scores = cross_val_score(model, X_train, Y_train, cv=skf, scoring='roc_auc')
第二步,搞定。
四. 特征工程–简单的缺失值处理,分类变量处理
分类变量,即离散特征,它的的处理是非常麻烦的,也是大多数数据分析与挖掘从事者头疼的一点。对于这道题而言,通过数据集信息可以清楚,分类变量主要有以下几个组成:‘Name’,‘Sex’,‘Ticket’,‘Cabin’,‘Embarked’。
同时,在这里我们必须知道,计算机是无法理解文本数据,因此我们需要将这些类别转换为数字。常用的方式包含:标签编码,独热编码,二值表示法。但需要注意一点,对于标签编码这种编码方式并不能用于线性模型、支持向量机或神经网络,因为它们希望数据是标准化的。但我们却可以在许多树模型中直接使用它,列如:决策树,随机森林,提升树, XGBoost,GBM,LightGBM。
在这一步,我们会选择使用独热编码对上诉分类变量进行处理,但在此之前,我们需要先判断有无缺失值,一方面缺失值的存在会影响到后续模型的训练,另外一方面使用 scikit-learn 提供的编码函数,无法处理 NaN 值。如果有缺失值,我们需要进行补或者删。
使用下列代码:
df_train.info()
我们可以从图中发现,蓝色框部分中的就是有缺失的值,因此我们对其进行简单的填补。
需要注意,删除有时是不建议的,因为有时候缺失值也含有其内在的信息,删掉缺失值在某些时候等于删除一些特定信息
使用下列代码,对 df_train
训练集和 df_test
最终的测试集进行填补。
# 做出分类变量,和数值变量列表
str_features = [
f for f in df_train.columns if f not in ('Survived','Pclass','Age','SibSp','Parch','Fare','PassengerId')
]
numeric_features = ['Pclass','Age','SibSp','Parch','Fare']
# 对训练集和测试集中的 NAN 值进行处理
for col in str_features:
# 将空值置为"NONE"
df_train.loc[:, col] = df_train[col].astype(str).fillna("NONE")
df_test.loc[:,col] = df_test[col].astype(str).fillna("NONE")
for col in numeric_features:
# 将空值设置为"0"
df_train.loc[:,col] = df_train[col].astype(np.float64).fillna(0)
df_test.loc[:,col] = df_test[col].astype(np.float64).fillna(0)
再次使用 df_train.info()
检查是否填补完成:
处理完了缺失值,下面就是对分类变量进行独热编码了,当然,在这里我们可以顺便将数据集和训练集划分出来:
# 读取训练集与测试集,去掉不需要的列,同时进行独热编码
X_train = df_train.drop(columns=['Survived','PassengerId'])
X_test = df_test.drop(columns=['PassengerId'])
Y_train = df_train['Survived']
# 进行独热编码
combined = pd.concat([X_train, X_test], keys=['train', 'test'])
features = ["Name", "Sex", "Ticket", "Cabin","Embarked"]
combined = pd.get_dummies(combined[features])
# 分开训练集和测试集
X_train = combined.xs('train')
X_test = combined.xs('test')
在这里,如果不进行合并,由于独热编码的问题,在后面的模型预测中会出现特征错误的问题。当然,这样看起来似乎就不那么客观,但也仅仅将无穷的 ‘Name’ 分类变量转化了成了有限的。
第三步,完成
五. 模型搭建-- 机器学习模型的选择
机器学习的模型很多,当然,我们可以尝试从最简单的模型–逻辑回归进行尝试。
注意,很多友友在学习树模型系列后,如决策树,随机森林等,就不再使用逻辑回归,但其实有时候逻辑回归的效果或许比树模型系列会好的多。这里我们从最简单的逻辑回归开始测试。
使用如下代码:
# 使用逻辑回归+交叉验证
from sklearn import linear_model
# skf 和 cross_val_score 在前面几步已经定义了
# max_iter=1000 参数是调整最大迭代次数,如果没有调整会弹出一个警告,告诉你没有收敛。
model = linear_model.LogisticRegression(max_iter=1000)
auc_scores = cross_val_score(model, X_train, Y_train, cv=skf, scoring='roc_auc')
print(auc_scores.mean())
输出:
0.8601579427869345
我们可以看到在训练集上,AUC 得分为 86 分。那其他的模型表现如何呢?我们使用下列代码可以进行验证:
# 使用其他模型
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
import xgboost as xgb
# 定义常用的几种分类模型
models = {
'随机森林': RandomForestClassifier(),
'决策树': DecisionTreeClassifier(),
'Xgb': xgb.XGBClassifier(eval_metric='logloss')
}
for model_name in models:
scores = cross_val_score(models[model_name], X_train, Y_train, cv=skf, scoring='roc_auc')
print(f'{model_name}:',scores.mean())
输出:
随机森林: 0.8665252515772238
决策树: 0.7978308330258164
Xgb: 0.8397922501487812
六. 结果展示
我们对四个模型使用下列代码进行数据保存,并前往 Kaggle 网站进行提交:
# 保存逻辑回归训练结果
model.fit(X_train,Y_train)
Y_pred = model.predict(X_test)
result = pd.DataFrame({
'PassengerId': df_test['PassengerId'],
'Survived': Y_pred
})
result.to_csv('Logistic_result.csv',index=False)
print("OK!")
最终的结果如下图所示:
更多推荐
所有评论(0)