文章目录
前言数据集介绍gender_submision.csv:train.csv:test.csv数据清洗数据预处理缺失值填充数据优化训练过程SVM完整代码KNNK-MeansRF特征工程小总结学习参考前言
本篇将注重于机器学习预测模型的过程、数据分析以及项目实战落地。对于各种所使用的算法直接调用sklearn包,在这里不会说算法的原理和过程。有想看原理和过程的可以看我这两天整理的学习笔记。
KNN K邻近 学习笔记各种聚类 学习笔记决策树与随机森林 学习笔记SVM 支持向量机 学习笔记
关于kaggle:
就是一个面向机器学习和数据科学打比赛的地方,根据精度有排行榜,会有企业在里面,你要是解决了企业发布的项目,还会有奖金啥的。
我们没那么强,就利用他的数据集和项目来做练习吧。
先下载一下泰坦尼克号的数据集。
kaggle官网下载地址:/competitions/titanic/data
浏览kaggle网页不需要翻墙,不过要下载数据集或者提交代码需要登陆账号,登录账号要翻墙。
放一个迅雷的链接:点我下载
泰坦尼克号就不介绍了吧,大家应该都看过,没看过应该也听说过了。
本次小项目的目的就是预测哪些人可能在这次灾难中幸存下来。
这是一个分类问题,且是一个二分类问题,一个乘客的结果只有 幸存/未幸存 两种结果。
数据集介绍
下载完成之后就这三个文件:
gender_submision.csv:
Passenger: 乘客编号Survived:是否幸存, 1幸存 0 未幸存train.csv:
训练集数据
Passenger:乘客编号Survived:相当于label 是否幸存,1 = 幸存 0 = 未幸存Pclass:乘客所在船舱等级,跟一等座二等座商务座类似。Name:乘客名,(话说乘客获救与否和名字有啥关系,可能只是想完善数据集?)Sex:性别Age:年龄SibSp:兄弟姐妹的数量Parh:父母子女的数量Ticket:乘客购买的船票编号Fare:船票价Cabin:客舱号Embarked:上船地点(S=英国南安普顿,C=法国瑟堡,Q=爱尔兰昆士敦)
test.csv
测试集,里面的字段和验证集一致,就是少了Survived是否幸存字段,毕竟测试集嘛,不能告诉你答案呀~~~
数据清洗
数据预处理
刚才在Excel中大致看了一下数据发现有很多的空缺值,数据值缺失会带来许多问题,所以必须要处理,来统计一下看看缺失的数据量级。
这块其实应该用jupyter写更好点,不过我刚才在写Bug,用Pycharm调试来着,所以就懒得换了,直接在Pycharm里继续干了,反正结果都一样哈哈。
titanic = pandas.read_csv('../data/Titanic数据集/train.csv')s = titanic.info()print(s)
pandas.read_csv
:
读取CSV文件内容
info()
统计文件内容数据
输出:
RangeIndex: 891 entries, 0 to 890Data columns (total 12 columns):# Column Non-Null Count Dtype --- ------ -------------- ----- 0 PassengerId 891 non-null int64 1 Survived891 non-null int64 2 Pclass 891 non-null int64 3 Name 891 non-null object 4 Sex891 non-null object 5 Age714 non-null float646 SibSp 891 non-null int64 7 Parch 891 non-null int64 8 Ticket 891 non-null object 9 Fare 891 non-null float6410 Cabin 204 non-null object 11 Embarked889 non-null object dtypes: float64(2), int64(5), object(5)memory usage: 83.7+ KB
可以看到在训练集中,一共是891条数据,其中:
Age 缺失 177 条Cabin 缺失 687 条Embarked 缺失 2 条
这仅仅是train里的数据缺失,还没有算test里。小孩和老人有可能被优先营救,所以年龄是比较关键的数据,必须填充。
数据缺失的还是蛮多的,如果只少了一两条我一般都是直接删掉哈哈。
缺失值填充
数值填充用最简单的方法,取本字段的平均值进行填充。
titanic['Age'] = titanic['Age'].fillna(titanic['Age'].median())
fillna(titanic['Age'].median())
fillan() 函数主要用于填充,后面的参数就是用什么值填充,这里写的用‘Age’字段的均值填充。
填充之后看一下。
print(titanic.describe())
describe()
该函数主要就是统计数据 有最大值 最小值 均值等等
输出:
PassengerId SurvivedPclass Age SibSp Parch Farecount 891.000000 891.000000 891.000000 891.000000 891.000000 891.000000 891.000000mean 446.000000 0.383838 2.308642 29.361582 0.523008 0.381594 32.204208std257.353842 0.486592 0.836071 13.019697 1.102743 0.806057 49.693429min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.00000025%223.500000 0.000000 2.000000 22.000000 0.000000 0.000000 7.91040050%446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.4545%668.500000 1.000000 3.000000 35.000000 1.000000 0.000000 31.000000max891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
可以看到填充之后的Age字段一共891个 已经没有缺失值了。
这里使用的fillna()并非永久性填充。注释该语句后输出可以看到 ‘Age’字段变成了 714.000000 。
这里有个小技巧:
如果pycharm的输出有省略号导致看不到结果,可以添加下面几行代码让他显示完整:
pd.set_option('display.max_columns', 1000)pd.set_option('display.width', 1000)pd.set_option('display.max_colwidth', 1000)
顾名思义,增加输出个数的。
同样的来填充 ‘Embarked’ 字段值。
由于这是字符串,没办法求均值,也是使用最简单的方法,该字段中那个值最多,就用那个值填充缺失值。
统计一下 Embarked 字段每个值的个数总计,看看那个值最多。
print(titanic['Embarked'].value_counts())输出:S 644C 168Q77
使用‘S’ 填充其中的缺失值:
titanic['Embarked'] = titanic['Embarked'].fillna('S')
可以看一下填充前后变化:
这里可以用unique()函数看该字段的所有可能值,
print(titanic['Embarked'].unique())titanic['Embarked'] = titanic['Embarked'].fillna('S')print(titanic['Embarked'].unique())输出:['S' 'C' 'Q' nan]['S' 'C' 'Q']
Cabin 字段缺失值非常多,这里先用‘U’ (unknow)进行填充
titanic['Cabin'] = titanic['Cabin'].fillna( 'U' )
数据优化
因为后面要做二分类,所以如果有‘str’的类型尽量变成‘int’或者‘float’类型。
这里面有个关键的字段就是性别,很有可能女性优先获救嘛,数据所给的是 ‘male’ 和 ‘female’,将他映射成 0和1来表示。
将 male变成0 female变成1:
titanic.loc[titanic['Sex'] == 'male','Sex' ] = 0titanic.loc[titanic['Sex'] == 'female','Sex' ] = 1
loc()
该函数用于定位,接受两个参数,并以’,'分隔。第一个位置表示行,第二个位置表示列。
这里就是定位 ‘Sex’字段 当其等于 ‘male’ 时候改成等于0。
同样的,登船地点字段 Embarked 里面也是字符,将其改成数字, S = 0 , C = 1, Q = 2。
print(titanic['Embarked'].unique()) # ['S' 'C' 'Q' nan]titanic.loc[titanic['Embarked'] == 'S','Embarked'] = 0titanic.loc[titanic['Embarked'] == 'C','Embarked'] = 1titanic.loc[titanic['Embarked'] == 'Q','Embarked'] = 2
将字符串数据映射成数值型数据的过程,这块有一个 独热编码(One-Hot Encoding)的概念简单提一下吧:
独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。
可以这样理解,对于每一个特征,如果它有m个可能值,那么经过独热编码后,就变成了m个二元特征(如成绩这个特征有好,中,差变成one-hot就是100, 010, 001)。并且,这些特征互斥,每次只有一个激活。因此,数据会变成稀疏的。
毕竟对于分类器来说,数值比字符串更好分类嘛。
训练过程
看一下刚才处理过后的数据的样子.
一会使用Pclass、Sex 、 Age、 SibSp、 Parch 、 Fare 、Embarked 这七个特征进行训练。
使用支持向量机(SVM)、K均值、 K邻近(KNN)和 决策树-随机森林(RF)来算法实现本次模型训练。
先将用到的包全导进来
from sklearn.model_selection import KFoldfrom sklearn.metrics import confusion_matrix,accuracy_score# SVMfrom sklearn.svm import SVC# KNNfrom sklearn.neighbors import KNeighborsClassifier
SVM
直接定义出来SVM的计算模型:
alg = SVC(C=2,kernel='rbf',gamma=10,decision_function_shape='ovr')
没写错 就是SVC
SVM=Support Vector Machine 是支持向量
SVC=Support Vector Classification就是支持向量机用于分类
SVR=Support Vector Regression.就是支持向量机用于回归分析
这里面的参数解释一下:
C:错误术语的惩罚参数C。C越大,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。
kernel :核函数类型,
linear’:线性核函数‘poly’:多项式核函数‘rbf’:径像核函数/高斯核‘sigmod’:sigmod核函数‘precomputed’:核矩阵
gamma:核函数系数,float类型,可选参数,默认为auto。只对’rbf’ ,’poly’ ,’sigmod’有效。如果gamma为auto,代表其值为样本特征数的倒数,即1/n_features。
decision_function_shape : ‘ovo’,‘ovr’,默认=‘ovr’
决策函数类型,可选参数’ovo’和’ovr’,默认为’ovr’。’ovo’表示one vs one,’ovr’表示one vs rest。
将我们用到的特征字段定义出来:
predictors = ['Pclass','Sex' ,'Age','SibSp', 'Parch' , 'Fare' ,'Embarked']
然后采用K折交叉验证的方法。
kf = KFold(n_splits=3, shuffle=True, random_state=1)
我之前在复现ZFnet论文的时候有详细的说过K折交叉验证,这里就简单的说一下,就是随机划分训练集为N份,分别取不同的一部分作为训练一部分作为测试,最后加权合并结果。
开始循环训练:
先放整体代码 后面再解释。
for train, test in kf.split(titanic[predictors]): # 表示 划分这些个特征做交叉验证train_predictors = (titanic[predictors].iloc[train,:]) # 拿出来训练的数据 pandas.iloc方法提供了基于整数的索引方式train_target = titanic["Survived"].iloc[train] # 拿出来lable值# 模型计算alg.fit(train_predictors, train_target)test_predictions = (titanic[predictors].iloc[test,:]) # 拿出来测试的数据集test_target = titanic["Survived"].iloc[test] # 拿出来测试的lable值# 这里有个东西需要注意,训练集 测试集 验证集# predictions.extend(test_predictions) # 记录测试结果pre_train = alg.predict(train_predictors)pre_test = alg.predict(test_predictions)
train_predictors = (titanic[predictors].iloc[train,:])
拿出来训练集的数据 。
pandas.iloc方法提供了基于整数的索引方式
。
train_target = titanic["Survived"].iloc[train]
拿出来训练集lable值
同样的方法 拿出来测试集的数据和测试集的lable值。
test_predictions = (titanic[predictors].iloc[test,:]) # 拿出来测试的数据集
test_target = titanic["Survived"].iloc[test] # 拿出来测试的lable值
这里有个东西我一开始蒙圈了,想了一下。
我这里说的测试集和训练集别忘了都是从一开始的train.csv里划分出来的。其实在一开始的test.csv文件应该叫做验证集。
alg.fit(train_predictors, train_target)
把计算模型定义出来,这里的参数就是训练集的数据和训练集的lable。
pre_train = alg.predict(train_predictors)
pre_test = alg.predict(test_predictions)
计算用SVM预测出来的值
最后我们可以打印看一下预测出来的值是什么样的:
accuracy_score() 计算正确率的函数,参数就是 原始数据标签和 使用模型预测出来的结果。
print('train',accuracy_score(train_target,pre_train))print('test',accuracy_score(test_target,pre_test))输出: # 一共三轮train 0.9814814814814815test 0.6127946127946128train 0.9764309764309764test 0.6363636363636364train 0.9764309764309764test 0.67003367003367
训练集的数据基本接近百分之百。这也没什么看的,主要是看测试集。
将每一次的测试结果放到一起来求一个平均值作为最后的结果。
完整代码
import pandas as pdpd.set_option('display.max_columns', 1000)pd.set_option('display.width', 1000)pd.set_option('display.max_colwidth', 1000)# 数据读取titanic = pd.read_csv('../data/Titanic数据集/train.csv')# 平均值填充年龄缺失值titanic['Age'] = titanic['Age'].fillna(titanic['Age'].median())# 年龄的字符串改inttitanic.loc[titanic['Sex'] == 'male','Sex' ] = 0titanic.loc[titanic['Sex'] == 'female','Sex' ] = 1# S填充Embarked缺失值titanic['Embarked'] = titanic['Embarked'].fillna('S')# Embarked 字符串改inttitanic.loc[titanic['Embarked'] == 'S','Embarked'] = 0titanic.loc[titanic['Embarked'] == 'C','Embarked'] = 1titanic.loc[titanic['Embarked'] == 'Q','Embarked'] = 2# Cabin字段 用U填充titanic['Cabin'] = titanic['Cabin'].fillna( 'U' )print(titanic.head())from sklearn.model_selection import KFoldfrom sklearn.metrics import confusion_matrix,accuracy_score# SVMfrom sklearn.svm import SVC# KNNfrom sklearn.neighbors import KNeighborsClassifieralg = SVC(C=2,kernel='rbf',gamma=10,decision_function_shape='ovr')predictors = ['Pclass','Sex' ,'Age','SibSp', 'Parch' , 'Fare' ,'Embarked']kf = KFold(n_splits=3, shuffle=True, random_state=1)# kf K折交叉验证 这里是3折交叉验证test_accurate = []for train, test in kf.split(titanic[predictors]): # 表示 划分这些个特征做交叉验证train_predictors = (titanic[predictors].iloc[train,:]) # 拿出来训练的数据 pandas.iloc方法提供了基于整数的索引方式train_target = titanic["Survived"].iloc[train] # 拿出来lable值# 模型计算alg.fit(train_predictors, train_target)test_predictions = (titanic[predictors].iloc[test,:]) # 拿出来测试的数据集test_target = titanic["Survived"].iloc[test] # 拿出来测试的lable值# 这里有个东西需要注意,训练集 测试集 验证集pre_train = alg.predict(train_predictors)pre_test = alg.predict(test_predictions)test_accurate.append(accuracy_score(test_target,pre_test))test_accurate = sum(test_accurate)/3 # 取平均值print(test_accurate)
可以看到使用SVM模型的预测结果为:
0.6397306397306397 大概百分之63%左右。
KNN
上述代码不变,直接使用Sklearn提供的API。
将
alg = SVC(C=2,kernel='rbf',gamma=10,decision_function_shape='ovr')
改为
alg = KNeighborsClassifier(n_neighbors=10)
参数:
n_neighbors:取邻近点的个数k 。可以多试几个数 看看效果。
直接运行看最后结果:
0.7048260381593714 70%左右 比SVM高一点。
K-Means
在scikit-learn中,包括两个K-Means的算法,一个是传统的K-Means算法,对应的类是KMeans。
另一个是基于采样的Mini Batch K-Means算法,对应的类是MiniBatchKMeans。
from sklearn.cluster import KMeansalg = KMeans(n_clusters=2, random_state=0)
关于KMeans的使用,可以直接看官方文档
n_clusters:数据集将被划分成 n_clusters个‘簇’即k值以及(int, optional, default: 8)。一般需要选取多个k值进行运算,并用评估标准判断所选k值的好坏,以获得较好的聚类效果。
RF
同样只更换一条语句就行了,不得不说sklearn真香!
将
alg = KNeighborsClassifier(n_neighbors=10)
改为
alg = RandomForestClassifier(random_state=1,n_estimators=10,min_samples_leaf=1,min_samples_split=2)
’
简单说一下参数:
random_state 随机种子n_estimators 要构建的多少颗决策树min_samples_split : 最小样本划分的数目,就是样本的数目少于等于这个值,就不能继续划分当前节点了min_samples_leaf : 叶子节点最少样本数,如果某叶子节点数目小于这个值,就会和兄弟节点一起被剪枝。
结果也是非常的不错 0.7957351290684626 基本上在80%左右。
特征工程
数据和特征对于机器学习模型影响是巨大的,所以要查看一下现在用的这七个特征对于结果的影响,
直接放一个模板:
selector = SelectKBest(f_classif,k=5)selector.fit(titanic[predictors],titanic['Survived'])score = -np.log10(selector.pvalues_)plt.bar(range(len(predictors)),score)plt.xticks(range(len(predictors)),predictors,rotation='vertical')plt.show()
这里面的各种参数可以查看官方文档。
得到结果:
可以看到 年龄、孩子父母的个数、兄弟姐妹的个数、还有登船地点等的重要性较低。
小总结
在KNN算法中 将参数n_neighbors 改成5 正确率为71%左右,稍高点。
在RF随机森林中 将参数 n_estimators 改为500 得到0.811447 正确率大概81%左右,也高了一点,不过不能无脑增大,改到5000之后反而变成0.8了。而调参 min_samples_leaf=2,min_samples_split=4 之后 正确率能达到0.8282828282828283 大概 82%左右,又提高了一点。
如果在前面K折交叉验证的时候使用的是老版本的 cross_validation,这个老版本不能用了,现在是model_selection。
这个小项目中,其实还有一个重要的特征没有应用,就是名字,可以分析名字长度和名字里的代号作为特征继续训练,过程我没有写进去,但结果显示 名字是对结果有很大影响的一个特征。
还有一个问题就是年龄居然没有对结果产生较大影响,这,,我觉得老人和小孩不应该优先获救嘛~~~
之前在复现 GoogleNet的时候,作者为了ILSVRC 比赛,用到一个多模型融合,我觉得这里其实也可以将多模型一起用,查资料之后也发现有一个博主是这么做的,他将逻辑回归和随机森林进行融合,不过我看了一下他的代码,他融合的时候将这俩模型用列表存储,最后精度除以2。这里是不是有问题啊? 单个随机森林的精度很高,而逻辑回归的精度比较低,如果直接存列表里做模型融合,最后取平均值的话,岂不是会拉低总体的精度?还不如用单个的随机森林呀?这里没看懂,有明白的大佬,评论区指点我一下。
还有一点就是,其实在这个数据集里并不适合使用K-Means算法,因为这是聚类算法嘛,聚类是无监督算法,而在这个泰坦尼克号的数据集里是已经给了lable的,所以可能这个算法并不合适,不过也可以用。
学习参考
数据值缺失处理:/p/137175585
pandas输出值省略号问题:/XU_RAL_KO/article/details/109380846
pandas.loc()用法详解:/pandas/loc-iloc.html
pandas 统计列个数:https://waple./article/details/80514073
独热编码:/p/134495345
SVM 官方文档:https://scikit-/view/83.html
sklearn K折交叉验证:/weixin_40283816/article/details/83242083
SVC\SVM\SVR:/ccc_bioinfo/article/details/108975556
sklearn.svm.SVC()函数:/TeFuirnever/article/details/99646257
decision_function_shape():/weixin_38100489/article/details/78363326
SVM的各种核函数:/batuwuhanpei/article/details/52354822
视频教程:/video/BV1WD4y1U7og?p=1
KNN KNeighborsClassifier 算法实现:/ml_alg/sklearn-knn.html
随机森林RandomForestClassifier :/mdevelopment/p/9450909.html
特征影响程度的计算:/weixin_48266461/article/details/114108872
K-means:/p/208306755
K-means 官方文档:https://scikit-/stable/modules/generated/sklearn.cluster.KMeans.html#sklearn.cluster.KMeans