前段时间学习了吴恩达的机器学习课程,然后蠢蠢欲动想要有所体验机器学习,于是上kaggle来体验了一下入门比赛-泰坦尼克号生存预测。
在kaggle上下载了项目训练集,就可以开始动手啦
本文仅仅是新手入门,hhh
1.数据初探
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
data_train=pd.read_csv('train.csv')
data_train.head()
使用head快速返回前5条消息。可以发现结果如下:
在kaggle页面上,我们找到了每一列的说明:
Variable | Definition | Key |
survival | Survival | 0 = No, 1 = Yes |
pclass | Ticket class | 1 = 1st, 2 = 2nd, 3 = 3rd |
sex | Sex | |
Age | Age in years | |
sibsp | # of siblings / spouses aboard the Titanic | |
parch | # of parents / children aboard the Titanic | |
ticket | Ticket number | |
fare | Passenger fare | |
cabin | Cabin number | |
embarked | Port of Embarkation | C = Cherbourg, Q = Queenstown, S = Southampton |
大致就是:
- # passengerId 乘客编号
- # survived 是否存活 1是 0否
- # pclass 船舱等级 1=lst 2=2nd 3=3rd
- # name 姓名
- # sex 性别
- # age 年纪
- # sibsp ?上的兄弟姐妹/配偶个数
- # parch ?上的父母,孩子
- # ticket 船票号码
- # fare 船票价格
- # cabin 船仓号
- # embarked 登船港口 C = Cherbourg, Q = Queenstown, S = Southampto
对于样本,我们进行整体把握,查看一下总体的信息:
data_train.info()
# 可以发现age,和cabin有大量缺失
# embarked只有两条缺失
2.数据处理与特征选择
Cabin船舱号有大量空值,对于空值填充可能有较大误差,所以我们先不考虑cabin作为特征
passengerId是一个连续的序列,与结果无关,我们不选择这个作为特征
ticket是船票序列,我们不分析
age,由于age缺失很少,我们使用年龄的平均值进行填充
embarked中有两条缺失的,我们使用其中出现最多的来填充
我们发现embarked和sex这两个特征是字符串,进行处理。我们将sex中male=1,famle=0。将embarked中 c=1,q=2,s=3
2.1数据填充
填充age
data_train['Age']=data_train['Age'].fillna(data_train['Age'].median())
data_train.describe()
# 可以发现AGE现在全部被填充了
填充embarked
## 统计出Embarked列各个元素出现次数
ans=data_train['Embarked'].value_counts()
# 返回最大值索引
fillstr=ans.idxmax()
data_train['Embarked']=data_train['Embarked'].fillna(fillstr)
data_train.info()
#可以发现embarked无缺失了
修改embarked与sex
# data_train['Sex'][data_train['Sex']=='male']=1
# data_train['Sex'][data_train['Sex']=='female']=0
# 使用loc定位行列
# data_train["Sex"] == "male"定位行 sex是列
data_train.loc[data_train["Sex"] == "male","Sex"] = 0
data_train.loc[data_train["Sex"] == "female","Sex"] = 1
data_train.loc[data_train['Embarked']=='C','Embarked']=0
data_train.loc[data_train['Embarked']=='Q','Embarked']=1
data_train.loc[data_train['Embarked']=='S','Embarked']=2
2.2特征选择
由于还处于入门级别,我只是想先体验一下做项目的感觉,我们就不进行一些详细的分析,使用特征工程创造更多的特征等等了。
我们进行最简单的相关性系数分析,只需要观察每个特征与分类结果Survived的相关性系数即可
相关性系数是我们研究变量的线性相关程度的量,我们最常使用的就是皮尔逊相关系数:
其中,Cov(X,Y)为X与Y的协方差,Var[X]为X的方差,Var[Y]为Y的方差
使用python实现如下:
# 下面计算相关性系数
import math
# 函数:计算相关系数
def calc_corr(a, b):
a_avg = sum(a) / len(a)
b_avg = sum(b) / len(b)
# 计算分子,协方差————按照协方差公式,本来要除以n的,由于在相关系数中上下同时约去了n,于是可以不除以n
cov_ab = sum([(x - a_avg) * (y - b_avg) for x, y in zip(a, b)])
# 计算分母,方差乘积————方差本来也要除以n,在相关系数中上下同时约去了n,于是可以不除以n
sq = math.sqrt(sum([(x - a_avg) ** 2 for x in a]) * sum([(x - b_avg) ** 2 for x in b]))
corr_factor = cov_ab / sq
return corr_factor
当然,pandas中已经为我们封装的很好了,我们可以直接使用。
#相关性协方差表,corr()函数,返回结果接近0说明无相关性,大于0说明是正相关,小于0是负相关.
#去掉passengerId,这一项是连续的序列,对结果无影响
train_corr = data_train.drop('PassengerId',axis=1).corr()
train_corr
当然,我们可以画出更为清晰的热力图
#画出相关性热力图
a = plt.subplots(figsize=(15,9))#调整画布大小
a = sns.heatmap(train_corr, vmin=-1, vmax=1 , annot=True , square=True)#画热力图
虽然我们可以发现 Sibsp,age,Parch的相关性系数都很小,但是由于是入门,我们就还是将他们都考虑进去,作为特征吧。
3.使用线性回归
首先来介绍一下线性回归。
假设你有了喜欢的?,那么在追她之前,你需要对自己进行评估。那么你会对自己身上的一系列特征评估,来认识自己。比如身高x1,x2,帅气程度x3,有钱程度x4等等。那么我们预测自己的得分h(x):
那么上式中的Ɵ(theta)参数就是对于我们每一个特征的权重,我们系要做的就是找到一组合适的theta,使得我们的预测更加准确,这样我们才能正确的评估自己,避免盲目出击。比如我们认为有钱程度x4的权重theta4为100,身高x1的权重为20也许是比较合理的。如果我们认为有钱程度x4的权重theta4为-2,身高x1的权重为3,那么根据实际经验,这显然是错误的。
3.1代价函数
那么我们怎么知道我们的theta参数最好呢,我们此时需要定义一个代价函数,反应我们预测值与实际值的差距。最简单的就是误差平方和啦。我们现在给出cost function J(theta)的定义:
所以我们现在的目标是找到使得我们的代价函数最小theta。
3.2梯度下降
首先,梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)【from 百科】。直观的理解就是,比如我们面前一座山,我们沿着山坡最陡峭的下坡(梯度)往下跑,我们下来的最快。那么对于代价函数J(theta),我们沿着其梯度,我们会一直下降到函数的某一个极值点(局部最优解),或者是最小值点(全局最优解)。
如下图黑色的线,我们就是我们使用梯度下降就可以从起点不断向着最优解收敛。
我们对J求theta的偏导,就可以得到theta的偏导数
。然后迭代的将theta沿着梯度下降的方向更新。这样我们就可以不断向着最优解逼近
上面就是线性回归的基本知识,下面我们就用线性回归来进行预测吧。
首先我们先自己实现一下线性回归,然后进行预测。
代码如下:
# 实现多元线性回归
# 获取样本
x_data=data_train[["Pclass","Age","Parch","Fare","Sex","Embarked"]]
y_data=data_train['Survived']
x=x_data.values #将dataframe转为矩阵
y=y_data.values.reshape(-1,1) # 列向量 891X1
# 标准化数据
def norm(x_data):
mu = np.zeros(x_data.shape[1])
sigma = np.zeros(x_data.shape[1])
mu=np.mean(x_data)
sigma=np.std(x_data)
x_norm=(x_data-mu)/sigma
return x_norm
# 添加常数项x0 为1
x_norm=np.insert(x_norm,0,np.ones(x_norm.shape[0]),axis=1)
x_norm # 891X 7
# 使用梯度下降
# 定义学习率
alpha=0.1
# 定义迭代次数
iters=700
#初始化theta 7X1
theta=np.zeros(x_norm.shape[1]).reshape(-1,1)
# 样本数m
# (n,)是一个数组,既不是行向量也不是列向量
# 定义代价函数
def mutilCost(X,y,theta):
m=X.shape[0]
bais=np.dot(theta.T,x_norm.T)-y.T
cost=1/2/m*np.dot(bais,bais.T)
return cost
# 定义梯度下降函数
def gradientDescentMulti(X, y, theta, alpha, num_iters):
m=X.shape[0]
cost_history =np.zeros(num_iters)
for i in range(1,num_iters):
theta=theta-aplha/m*X.T@(X@theta-y)
cost_history[i] = mutilCost(X, y, theta)
return theta,cost_history
训练theta
[theta, cost_history] = gradientDescentMulti(x_norm, y, theta, alpha, iters)
获取结果
ans=x_norm@theta
# 这是一个2分类问题,结果为0,1.那么对于预测生存几率大于0.5的,我们认为其能存活,置为1,小于0.5的为0
ans[ans>=0.5]=1
ans[ans<0.5]=0
ans.shape
data_train["Survived"].shape
# 查看准确率
accuracy = sum(ans == data_train["Survived"].values.reshape(-1,1)) / len(predictions)
print("准确率为: ", accuracy)
最后我们发现准确率为0.67。不是很理想hhh,不过毕竟是自己简单实现的。
当然,我们可以直接使用sklearn中的linea regression来进行预测
from sklearn.linear_model import LinearRegression #导入线性回归
# 训练集进行交叉验证,得到均值
from sklearn.model_selection import KFold
# 选取简单的可用数字特征
predictors=["Pclass","Age","Parch","Fare","Sex","Embarked",'SibSp']
#初始化现行回归算法
alg = LinearRegression()
#样本平均分成3份,3折交叉验证
kf = KFold(n_splits=3,shuffle=False,random_state=1)
predictions = []
for train,test in kf.split(data_train):
train_predictors = (data_train[predictors].iloc[train,:])
train_target = data_train["Survived"].iloc[train]
alg.fit(train_predictors,train_target)
test_predictions = alg.predict(data_train[predictors].iloc[test,:])
predictions.append(test_predictions)
#测试准确率
import numpy as np
predictions = np.concatenate(predictions,axis=0)
predictions[predictions>.5] = 1
predictions[predictions<=.5] = 0
accuracy = sum(predictions == data_train["Survived"]) / len(predictions)
print ("准确率为: ", accuracy)
这下我们的准确率来到了0.79,这就非常不错了。完成了我们的算法,进行了训练,得到的预测结果也还可以,然后我们就可以提交了?
too young,我们还需要对测试集中的数据进行处理(类似我们对训练集数据的处理),然后生成符合kaggle提交要求的文件。代码如下:
#对data——test进行一样的预处理
data_test=pd.read_csv('test.csv')
ans=data_test['Embarked'].value_counts()
# 返回最大值索引
fillstr=ans.idxmax()
data_test['Embarked']=data_test['Embarked'].fillna(fillstr)
data_test.info()
data_test.loc[data_test["Sex"] == "male","Sex"] = 0
data_test.loc[data_test["Sex"] == "female","Sex"] = 1
data_test.loc[data_test['Embarked']=='C','Embarked']=0
data_test.loc[data_test['Embarked']=='Q','Embarked']=1
data_test.loc[data_test['Embarked']=='S','Embarked']=2
data_test['Age']=data_test['Age'].fillna(data_test['Age'].median())
mid=data_test['Fare'].median()
data_test['Fare']=data_test['Fare'].fillna(value=mid)
test_predictions = alg.predict(data_test[predictors])
test_predictions[test_predictions>=0.5]=1
test_predictions[test_predictions<0.5]=0
# 导出结果
result=pd.DataFrame({'PassengerId':data_test['PassengerId'].as_matrix(),'Survived':test_predictions.astype(np.int32)})
这下算是大功告成了,我们接下来可以愉快的提交了。
好了,至此我们就完成了首次的kaggle体验了。也希望自己在以后的学习中有更多的收获吧。