初窥Kaggle竞赛
原文地址: https://www.dataquest.io/mission/74/getting-started-with-kaggle
1: Kaggle竞赛
我们接下来将要学习如果在Kaggle竞赛上进行一次提交。Kaggle是一个创造算法,与来自全世界的机器学习练习者竞赛的平台。你的算法在给定的数据集中准确率越高你就赢了。Kaggle是一个有趣的途径去联系机器学习技能。
Kaggle网站上有不同的竞赛。有一个是预测哪个成哥在泰坦尼克号上存活下来。在接下去的任务中,我们会探索数据,训练我们第一个模型,然后准备我们第一个提交。无论如何,如果你想要得到提交到Kaggle.com的网站上进行测评,你要在你的电脑上做这些。
我们的数据用的是.csv
格式。你可以从这里下载数据。
每行表示一个泰坦尼克号上的一个乘客和他们的信息。让我们先看看这些列。
-
PassengerId
-- 一个分配给每个乘客的id号 -
Survived
-- 乘客是否存活(1表示存活,0表示不存活)。我们将要对这一列进行预测 -
Pclass
-- 乘客所在的等级 -
Name
-- 乘客名 -
Sex
-- 乘客的性别(male或者female) -
Age
-- 乘客的年龄 -
SibSp
-- 乘客在船上兄弟和配偶的数量 -
Parch
-- 乘客在船上父母和儿女的数量 -
Ticket
-- 乘客票的数量 -
Fare
-- 乘客为票付了多少钱 -
Cabin
-- 乘客位于哪个船舱 -
Embarked
-- 乘客所在位置
一个好的步骤是去思考这些列的含义和我们将预测的。那些变量在逻辑上会影响存活的输出。
我们知道女人和儿童更有可能存活。因此,Age
和Sex
都是很好的预测因素。Pclass
也是影响输出的一个因素,因为first class距离船的甲板更近。Fare
和乘客的class相关联,也会增加一些信息。兄弟和父母/子女的数量也会换脸到存活与否,这关系到是否有更多人帮助或者尝试救你。
同时,存活率和Embarked
, Ticket
和Name
都有一些联系。
这一步通常称为获取领域知识,而且它对大多数机器学习任务相当重要。我们最大化信息去预测。
2: 观察数据
我们会用python3
,pandas
库和scikit-learn
来分析我们的数据并创建一个提交。我们会交互的在code boxes中敲代码,就像你在下看到的一样。如果你对Python
不熟悉,你可以先看看我们的课程。
第二步就是从观察数据的高层的描述。在这个例子中,我们用pandas
库中的.describe()
方法观察每个数字列的不同的特征。
# We can use the pandas library in python to read in the csv file.
# This creates a pandas dataframe and assigns it to the titanic variable.
titanic = pandas.read_csv("titanic_train.csv")
# Print the first 5 rows of the dataframe.
print(titanic.describe())
3: 缺失的数据
当用到数据titanic
数据集上的.describe()
时,你可能注意到Age
列的数量是714,而其他的列的数量是891。这暗示在Age
列上有缺失的数据。
这意味着数据集不干净,而且我们将清理数据。我们不希望直接移除缺失数据的行,因为更多的数据能帮助我们更好的训练算法。我们也不想除去这一列,因为这一列对我们的分析很重要。
有很多策略处理缺失的数据,一种简单的方法就是用中位数填充缺失的数据。
我们像dictionary
一样选择数据集的一列。
titanic["Age"]
我们可以用.fillna
方法替换所有缺失的数据。.fillna
需要一个参数,就是需要替换的值。
在我们的案例中,我们用中位数填充:
.fillna(titanic("Age").median())
我们接着把结果返回
titanic["Age"] = titanic["Age"].fillna(titanic["Age"].median())
4: 非数据列
当我们用.describe()
时,我们可能注意到不是所有列都显示了。只有数字列显示了。我们不能讲非数字列传入机器学习方法并指望他们去理解。
我们必须在训练算法的时候排除这些非数字列(如Name
,Sex
,Cabin
,Embarked
和Ticket
),或者把他们转变为数字。
我们将忽略Ticket
,Cabin
和Name
列。他们没有太多的信息值得我们提取。大多数值Cabin
的值都丢失了(891个乘客中只有204个值),而且这也不是一个关键信息。Ticket
和Name
列在缺少领域知识的情况下都不能告诉我们票数量意味着什么,还有名字关联了家族是否大或富裕。
5: 转换Sex
列
Sex
列是非数字列,但是我们将保留他--他包含了一些信息。我们可以用一个数字去编码性别列的值。一个机器学习算法将会利用这些进行预测。
为了实现这个,我们首先找到性别列中所有独一无二的性别。我们用0
表示male
,用1
表示female
。
我们可以选择Sex
类中的所有male
值:
titanic.loc[titanic["Sex"]=="male","Sex"]
我们可以用0代替这些:
titanic.loc[titanic["Sex"] == "male", "Sex"] = 0
# Find all the unique genders -- the column appears to contain only male and female.
print(titanic["Sex"].unique())
# Replace all the occurences of male with the number 0.
titanic.loc[titanic["Sex"] == "male", "Sex"] = 0
titanic.loc[titanic["Sex"] == "female", "Sex"] = 1
print(titanic["Sex"].unique())
6: 转换Embarked
列
我们想转换Sex
列一样转换Embarked
列。
# Find all the unique values for "Embarked".
print(titanic["Embarked"].unique())
titanic["Embarked"]=titanic["Embarked"].fillna('S')
titanic.loc[titanic["Embarked"] == "S", "Embarked"] = 0
titanic.loc[titanic["Embarked"] == "C", "Embarked"] = 1
titanic.loc[titanic["Embarked"] == "Q", "Embarked"] = 2
7: 开始机器学习!
现在我们清理了我们的数据,我们开始探索机器学习。我们看下面的例子:
Age Sex Survived
10 0 0
5 1 1
30 0 0
如果我们从Age
列中预测是否存活,我们可以用线性回归。
线性回归遵循以下等式: y=mx+b
,y
表示我们要预测的值,m
是一个系数叫斜率,x
是一列的值,b
是一个常数。
我们可以令m=-2,b=20。那么我们就得到了:
Age Sex Survived Predictions
10 0 0 -2 * 10 + 20 = 0
5 1 1 -2 * 5 + 20 = 10
30 0 0 -2 * 30 + 20 = -40
如果我们将所有大于0的预测转换为1,小于等于0的转为0,那么我们会得到以下结果:
Age Sex Survived Predictions
10 0 0 0
5 1 1 1
30 0 0 0
这个简单的模型很好的预测了存活结果。线性回归是一个非常有力的算法,但是有一些缺陷:
- 如果一列和一个输出不是线性相关,那么它不会表现很好。比如,如果有一个老妇人没有存过,假设她的年龄是80,线性回归就不能得到正确结果。
- 它不能给你存活概率,只有标识是否存活的结果
我们之后讨论如何处理这些问题。现在,我们学习如何自动计算线性回归系数以及如何利用多列得到输出结果。
8: 交叉验证
我们可以利用线性回归预测我们的训练集。
我们想要在不同数据训练算法。我们必须防止过拟合。过拟合是一种模型拟合噪声而不是信号的现象。每个数据集都有他的不存在于所有样品的特点。举个例子,如果我让你用马力和其他特征预测一辆车的最高时速,随机给你有很高时速的车的数据集,你将会创造一个夸大了时速的模型。如果你的模型必须用没有用于预测的数据来评价它的表现。
每一个机器学习算法都有可能过拟合,尽管有些算法会更少地过拟合。如果用训练的数据集评价一个算法,那么是不可能知道这个算法是否表现的好因为它对于噪声过拟合,或者它确实是一个好算法。
幸运的是,交叉验证是一个简单的方法去避免过拟合。我们把数据集分割成几个部分。用3
作为例子:
- 用第一部分和第二部分训练模型,第三部分进行预测
- 用第一部分和第三部分训练模型,第二部分进行预测
- 用的二部分和第三部分训练模型,第一部分进行预测
通过这种方法,我们不用训练模型时用到的数据集来评价算法的预测准确率。
9: 进行预测
我们用scikit-learn
库进行预测。我们用sklearn的方法将数据分为交叉验证的部分。对每个部分训练一个算法,然后进行预测。最后,我们有一列的预测结果,每个列表项包含了对于相关部分的预测。
# Import the linear regression class
from sklearn.linear_model import LinearRegression
# Sklearn also has a helper that makes it easy to do cross validation
from sklearn.cross_validation import KFold
# The columns we'll use to predict the target
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
# Initialize our algorithm class
alg = LinearRegression()
# Generate cross validation folds for the titanic dataset. It return the row indices corresponding to train and test.
# We set random_state to ensure we get the same splits every time we run this.
kf = KFold(titanic.shape[0], n_folds=3, random_state=1)
predictions = []
for train, test in kf:
# The predictors we're using the train the algorithm. Note how we only take the rows in the train folds.
train_predictors = (titanic[predictors].iloc[train,:])
# The target we're using to train the algorithm.
train_target = titanic["Survived"].iloc[train]
# Training the algorithm using the predictors and target.
alg.fit(train_predictors, train_target)
# We can now make predictions on the test fold
test_predictions = alg.predict(titanic[predictors].iloc[test,:])
predictions.append(test_predictions)
10: 评价误差
现在我们有了预测结果,我们可以评价误差。
我们首先需要定义一个误差指标,所以我们计算模型的正确率。从Kaggle竞赛的描述中得到,误差指标是正确预测的百分比。我们用相同的指标来评价算法的表现。
这个指标用predictions
表示,由titanic["Survived"]/total number of passengers
得到。
在我们做这个之前,我们需要将三个集合的预测结果连城一列。因为每个预测结果是numpy
数组,所以我们可以用numpy
函数去把他们串联在一起。
import numpy as np
# The predictions are in three separate numpy arrays. Concatenate them into one.
# We concatenate them on axis 0, as they only have one axis.
predictions = np.concatenate(predictions, axis=0)
# Map predictions to outcomes (only possible outcomes are 1 and 0)
predictions[predictions > .5] = 1
predictions[predictions <=.5] = 0
cnt=0
for i in range(len(predictions)):
if(predictions[i]==titanic["Survived"][i]):
cnt+=1
accuracy=cnt/len(predictions)
11: 逻辑回归
我们有了第一个预测。他不是很好,只有78.3%的正确率。我们可以用逻辑回归去表示[0,1]之间的输出。
逻辑回归可以接收线性回归的输出,然后将他们映射到[0,1]之间。这个映射由logit function
完成。
Sklearn
有一个逻辑回归的类供我们使用。我们也可以调用sklearn的辅助函数去帮助我们完成交叉验证和评价。
from sklearn import cross_validation
# Initialize our algorithm
alg = LogisticRegression(random_state=1)
# Compute the accuracy score for all the cross validation folds. (much simpler than what we did before!)
scores = cross_validation.cross_val_score(alg, titanic[predictors], titanic["Survived"], cv=3)
# Take the mean of the scores (because we have one for each fold)
print(scores.mean())
12: 处理测试集
我们的正确率有点像样了,但是还是不够好。我们仍然可以尝试其他方法使他更好,我们会在后面提到。
但是,我们需要再竞赛中进行提交。为了完成这个,我们需要在测试集上做和在训练集上同样的步骤。如果我们不做这些相同的操作,我们不会有有效的预测结果。
这些操作根据我们之前对于列做过的操作进行改变。
titanic_test = pandas.read_csv("titanic_test.csv")
titanic_test["Age"] = titanic_test["Age"].fillna(titanic["Age"].median())
titanic_test.loc[titanic_test["Sex"] == "male", "Sex"] = 0
titanic_test.loc[titanic_test["Sex"] == "female", "Sex"] = 1
titanic_test["Embarked"] = titanic_test["Embarked"].fillna('S')
titanic_test.loc[titanic_test["Embarked"] == "S", "Embarked"] = 0
titanic_test.loc[titanic_test["Embarked"] == "C", "Embarked"] = 1
titanic_test.loc[titanic_test["Embarked"] == "Q", "Embarked"] = 2
titanic_test["Fare"] = titanic_test["Fare"].fillna(titanic_test["Fare"].median())
13: 生成一个提交文件
现在我们有了所有我们需要生成提交的东西。
首先,我们需要在训练数据上训练一个算法。然后我们在测试集上进行预测。最后,我们用预测结果和乘客id生成一个csv
文件。
你可以通过调用submission.to_csv("kaggle.csv", index=False)
生成输出文件。他会提供你第一次提交所需要的一切东西--它不会给你很好的正确率(大概75%)
# Initialize the algorithm class
alg = LogisticRegression(random_state=1)
# Train the algorithm using all the training data
alg.fit(titanic[predictors], titanic["Survived"])
# Make predictions using the test set.
predictions = alg.predict(titanic_test[predictors])
# Create a new dataframe with only the columns Kaggle wants from the dataset.
submission = pandas.DataFrame({
"PassengerId": titanic_test["PassengerId"],
"Survived": predictions
})
submission.to_csv("kaggle.csv", index=False)
14: 下一步
我们刚刚生成了一个提交文件,但是正确率在提交时不会很高(大概75%)。在测试集上的成绩会比交叉验证集上得到的成绩要低,因为我们在预测不同的数据。在下一个任务中,我们会学习如果产生更好的特征并用更好的模型来提高我们的分数。