机器学习(1)——从TItanic开始
Kaggle-Titanic
Step1-环境配置
- 从Kaggle上下载数据集"train.csv",“test.csv”
- 编程环境为jupyter notebook
Step2-数据概览
- 首先是训练集,Passenger代表编号,Survived代表生存状态,“1”代表最后得救。之后第一行的标签则代表一些个人的详细信息,例如“Pclass”代表船舱等级;“SibSp”代表兄弟姐妹和配偶;“Parch”代表父母子女;“Embarked”代表登船地点,可知有三个地点。
- 测试集与训练集基本相同,仅是缺失了“Survived”这一项,需要我们进行预测。
Step3-数据统计与预处理
3.1 数据统计代码与结果分析
import pandas
data = pandas.read_csv("train.csv")
#data.head() #此处可以打印出数据前5项具体信息
print(data.info())
print(data.describe())
以下为data.info()和data.describe()结果
- 由data.info()可以得到数据是否为空和数据类型等信息,如图可知Age,Cabin,Embarked等均有不同程度的数据缺失,而Cabin由于缺失较多(仅有204条数据),因此在分析时选择忽略Cabin对于结果的影响。
- 由data.describe()可以得到数据类型为float和int的数据信息。可发现信息中仅有Age一项为714,因此选择将其补全,这里选择将剩余用29岁(接*均值)补齐。
3.2 数据处理
- "Age"处理
#使用pandas.fillna()方法
data["Age"] = data["Age"].fillna(data["Age"].median())
- "Sex"处理
由于"Sex"在原数据列表中是float型,因此将其转化为int型以便于后续分析。
#unique()函数找到"Sex"列中有多少个不同的元素,并将其打印出来
print(data["Sex"].unique())
#loc()函数用来定位,有两个参数,先行后列
data.loc[data["Sex"] == "male","Sex"] = 0
data.loc[data["Sex"] == "female","Sex"] = 1
- "Embarked"处理
from collections import Counter
#利用Counter可以得到"Embarked"列中不同元素的数量,统计得出有三个地点
print(Counter(data["Embarked"]))
#'S': 644, 'C': 168, 'Q': 77,在这里简单处理,直接将缺失的数据填为'S'
data["Embarked"] = data["Embarked"].fillna("S")
#剩余操作同"Age"
data.loc[data["Embarked"] == "S","Embarked"] = 0
data.loc[data["Embarked"] == "C","Embarked"] = 1
data.loc[data["Embarked"] == "Q","Embarked"] = 2
Step4-线性回归分析
- 在数据处理结束之后,我们需要筛选出我们所需要的标签来进行分析。训练集中我们发现"Ticket"和"Fare"这两项每个人的数据基本都不一样,不便于统计与预测,因此弃用。而之前提到的"Cabin"由于样本数量少也弃用。
#线性回归+kfold交叉检验
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
#选择预测特征
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
- 选定了参数之后,开始用线性回归方法进行拟合。
alg = LinearRegression() #算法初始化
kf = KFold(n_splits=3, random_state=1).split(data) #这里采用KFold进行交叉验证
#n_split()功能:将训练/测试数据集划分n_splits个互斥子集,
#每次用其中一个子集当作验证集,剩下的n_splits-1个作为训练集,
#进行n_splits次训练和测试,得到n_splits个结果
predictions = []
for train,test in kf:
#初始化预测特征,采用iloc()
train_predictors = data[predictors].iloc[train,:]
#初始化目标值
train_target = data["Survived"].iloc[train]
#用训练集训练算法,即使用线性回归进行拟合,这里使用fit()函数
alg.fit(train_predictors, train_target)
#用算法对测试集进行测试,这里使用predict()函数,参数为测试集中的数据
test_predictions = alg.predict(data[predictors].iloc[test,:])
#由于进行了3次交叉验证,因此将结果用append连接起来
predictions.append(test_predictions)
- 对结果进行处理,由于线性回归是用线性函数拟合,其结果在[0,1]之间连续分布,因此将靠近0的设置为0,靠近1的设置为1,这时再计算结果的精确度。
import numpy as np
# numpy.concatenate()函数能一次完成多个数组的拼接
predictions = np.concatenate(predictions, axis=0)
print(predictions)
predictions[predictions > .5] = 1
predictions[predictions < .5] = 0
#统计预测值和实际值相符的个数占总数的比例即是最终的精确度
accuracy = len(predictions[predictions == data["Survived"]]) / len(predictions)
print(accuracy)
- 最后精确度大约为78.3%
Step5-Logistic回归分析
#逻辑回归,采用交叉验证
from sklearn import cross_validation
from sklearn.linear_model import LogisticRegression
# 初始化算法
alg = LogisticRegression(random_state=1)
#得出分数
scores = cross_validation.cross_val_score(alg, data[predictors], data["Survived"], cv=3)
#cv:几折交叉验证
print(scores.mean())
- 最后精确度大约为78.8%。
- 结果与线性回归相比没有显著提升,可见采用常见的拟合方法效果并不显著,因此可以采用随机森林进行预测。
Step6-随机森林预测
#随机森林
from sklearn import model_selection
from sklearn.ensemble import RandomForestClassifier
#选则预测特征
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
#初始化算法
#n_estimators:决策树的个数
#min_samples_split:指定了分裂一个内部节点(非叶子节点)需要的最小样本数。
#min_samples_leaf:指定了每个叶子节点包含的最少样本数。
#后两个参数是决策树停止递归的判断依据,可以有效防止过拟合。
alg = RandomForestClassifier(random_state=1, n_estimators=50, min_samples_split=5, min_samples_leaf=3)
#依旧是KFold-交叉检验
kf = model_selection.KFold(n_splits=3, random_state=1).split(data)
scores = model_selection.cross_val_score(alg, data[predictors], data["Survived"], cv=3)
print(scores.mean())
- 采用随机森林算法后精确度达到82.0%
Step7-生成新数据&数据关联性判断
7.1 头衔(‘Title’)的影响
import re
def get_title(name):
#正则匹配,任意长字符+字符“.”
title_search = re.search('([A-Za-z]+)\.',name)
if title_search:
#只返回第一个相匹配的字符串
return title_search.group(1)
return
#统计出现频率,apply()函数将get_title()作用到data["Name"]列,以统计头衔出现频率
titles = data["Name"].apply(get_title)
print(pandas.value_counts(titles))
头衔统计结果如下:
- 可以发现以"Mr",“Miss"为代表的性别属性的头衔占据绝大多数,同时也有像"Master”,"Dr"这一类象征教育水平的头衔,可以直观感觉这些头衔会与是否获救具有一定关系。
- “Col”,“Major"以及"Capt”,"Mme"等头衔出现频率较低,因此将这些头衔合并为两组,分别映射到量个标签中
#由于"Mlle","Col"等出现频率较低,因此将他们合并为一个
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Dr": 5,"Rev": 6, "Major": 7, "Col": 7, "Mlle": 7, "Lady": 8, "Don": 8, "Sir": 8, "Mme": 8, "Jonkheer": 8, "Capt": 8, "Countess": 8, "Ms": 8}
for k,v in title_mapping.items():
titles[titles == k] = v
#生成新标签
data["Title"] = titles
7.2 名字长度与家庭成员数量
ps:觉得这俩有点玄学。。
#合并构造新参数
#apply()函数,通过lambda(计算字符串长度)
data["NameLength"] = data["Name"].apply(lambda x:len(x))
data["FamilySize"] = data["SibSp"] + data["Parch"]
print(data["FamilySize"])
7.3 对数据进行关联性分析
import numpy as np
from sklearn.feature_selection import SelectKBest, f_classif
import matplotlib.pyplot as plt
#现在共有10个数据
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked', 'FamilySize', 'Title', 'NameLength']
#进行交叉验证
#方差分析,p参数
selector = SelectKBest(f_classif, k=5)
selector.fit(data[predictors], data['Survived'])
#得到每个特征对应的p值,将其转化为分数
scores = -np.log10(selector.pvalues_)
#绘图
plt.bar(range(len(predictors)), scores)
plt.xticks(range(len(predictors)), predictors, rotation='vertical')
plt.show()
#选择了其中关联性最大的五个变量
predictors = ["Pclass", "Sex", "Fare", "Title", "NameLength"]
- 结果如下
- 由柱状图可知,“Pclass”,“Sex”,“Fare”,“Title”,"NameLength"等五个参数对结果影响最大,因此在之后的预测中选择忽略其他因素的影响。
Step8-算法集成
这里采用梯度提升与Logistic回归分析
#梯度提升决策树分类+逻辑回归集成
from sklearn.ensemble import GradientBoostingClassifier
import numpy as np
#算法集成,设置算法剪枝条件、参数,算法预测标签
algorithms = [
[GradientBoostingClassifier(random_state=1, n_estimators=25, max_depth=3), ['Pclass', 'Sex', 'Age', 'Fare', 'Embarked', 'FamilySize', 'Title', 'Age', 'Embarked']],
[LogisticRegression(random_state=1, solver='liblinear'), ['Pclass', 'Sex', 'Age', 'Fare', 'Embarked', 'FamilySize', 'Title', 'Age', 'Embarked']]
]
kf = KFold(n_splits=3, random_state=1).split(data)
predictions = []
for train, test in kf:
train_target = data['Survived'].iloc[train]
full_test_predictions = []
#对于algorithms中的2个算法分别进行训练并取均值,但是比例可以调整
for alg, predictors in algorithms:
alg.fit(data[predictors].iloc[train, :], train_target)
test_predictions = alg.predict_proba(data[predictors].iloc[test, :].astype(float))[:, 1]
full_test_predictions.append(test_predictions)
test_predictions = (full_test_predictions[0] + full_test_predictions[1]) / 2
test_predictions[test_predictions <= .5] = 0
test_predictions[test_predictions > .5] = 1
predictions.append(test_predictions)
predictions = np.concatenate(predictions, axis=0)
accuracy = len(predictions[predictions == data['Survived']]) / len(predictions)
print(accuracy)
GradientBoostingClassifier:LogisticRegression | Accuracy |
---|---|
1:1 | 82.27% |
2:1 | 82.04% |
3:1 | 82.49% |
4:1 | 82.71% |
5:1 | 82.72% |
6:1 | 82.72% |
- 最终精确度在82%-83%之间浮动。
Step9-预测结果
import csv
#导入数据及补全空缺值
data_test = pandas.read_csv('test.csv')
data_test['Age'] = data_test['Age'].fillna(data_test['Age'].median())
data_test['Fare'] = data_test['Fare'].fillna(data_test['Fare'].median())
data_test.loc[data_test['Sex'] == 'male', 'Sex'] = 0
data_test.loc[data_test['Sex'] == 'female', 'Sex'] = 1
data_test['Embarked'] = data_test['Embarked'].fillna('S')
data_test.loc[data_test['Embarked'] == 'S', 'Embarked'] = 0
data_test.loc[data_test['Embarked'] == 'C', 'Embarked'] = 1
data_test.loc[data_test['Embarked'] == 'Q', 'Embarked'] = 2
#产生新的数据列
data_test["NameLength"] = data_test["Name"].apply(lambda x:len(x))
data_test["FamilySize"] = data_test["SibSp"] + data_test["Parch"]
# print(data_test.describe())
def get_title(name):
#正则匹配,任意长字符+字符“.”
title_search = re.search('([A-Za-z]+)\.',name)
if title_search:
#只返回第一个相匹配的字符串
return title_search.group(1)
return
#统计出现频率
titles = data_test["Name"].apply(get_title)
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Col": 5,"Rev": 5, "Dona": 6, "Ms": 6, "Dr": 6}
for k,v in title_mapping.items():
titles[titles == k] = v
data_test["Title"] = titles
predictors = ["Pclass", "Sex", "Fare", "Title", "NameLength"]
for alg,predictors in algorithms:
test_value = alg.predict(data_test[predictors])
print(test_value)
print(data_test["PassengerId"])
#生成结果并保存
result = pandas.DataFrame({'PassengerId':data_test["PassengerId"].values, "Survived":test_value.astype(np.int32)})
result.to_csv("predictions.csv", index=False)
- 这里是一张卑微的提交结果
参考资料
ps:后两个好像都是唐宇迪老师的,但来源没找到,抱歉抱歉(好吧其实是懒)
pps:第五个视频年代久远,似乎用的还是python2,我这里用的是python3.8,所以会与视频中部分代码有出入。