小白好难得会用python做第分类,实践一下用于kaggle入门赛之泰坦尼克生还预测
问题介绍:泰坦尼克电影大家都看过,大灾难过后有些人生还了,有些人却遭遇了不信,官方提供了1309名乘客的具体信息以及提供了其中891名乘客的最后的存活情况,让我们去预测另外418乘客的存活情况。是很基本的二分类问题。
官方所给的数据长这样:
Survived:是否存活(0代表否,1代表是)Pclass:社会阶级(1代表上层阶级,2代表中层阶级,3代表底层阶级)Name:船上乘客的名字Sex:船上乘客的性别Age: 船上乘客的年龄(可能存在 NaN)SibSp:乘客在船上的兄弟姐妹和配偶的数量Parch:乘客在船上的父母以及小孩的数量Ticket:乘客船票的编号Fare:乘客为船票支付的费用Cabin:乘客所在船舱的编号(可能存在 NaN)Embarked:乘客上船的港口( S 代表从 Southampton 登船, C 代表从 Cherbourg 登船,Q 代表从 Queenstown 登船,)
我们面临的主要问题有两个,一个是age和cabin存在很多缺失值(Age(年龄)属性只有714名乘客有记录,Cabin(客舱)只有204名乘客是已知的),很可能会影响模型性能;另一个是训练样本就891个,训练集过小,很容易造成过拟合。
由于影响因素太多,我们先对特征向量进行筛选。根据信息增益比进行排序画图(详细知识点见《机器学习》周志华)
故我们就保留前三个影响因子。推测性别女士优先,年龄小孩优先,票价可能影响到其所在船舱而影响其存活率。
def file2matrix(filename): fr = open(filename) numberOfLines = len(fr.readlines()) #get the number of lines in the file returnMat = zeros((numberOfLines,3)) #PRepare matrix to return classLabelVector = [] #prepare labels return fr = open(filename) index = 0#确保指针在最前面 for line in fr.readlines(): line = line.strip() listFromLine = line.split(',') if listFromLine[5]=='male': listFromLine[5]='0' else: listFromLine[5]='1' if listFromLine[6]=='': listFromLine[6]=29.7 if listFromLine[7]=='': listFromLine[7]=35.6 #年龄的缺省用平均年龄29.7代替 returnMat[index,:] = listFromLine[5:8] #对应着性别,年龄和船票价 classLabelVector.append(int(listFromLine[1])) index += 1 return returnMat,classLabelVector#return的位置一定要找对二、分析数据
import matplotlib import matplotlib.pyplot as plt from os import listdirimport numpy as npdataset,classlabel=file2matrix('train1.csv')classlabel=array(classlabel)id0=np.where(classlabel==0)id1=np.where(classlabel==1)fig=plt.figure() ax=fig.add_subplot(111) p1=ax.scatter(dataset[id1,1],dataset[id1,2],color = 'r',label='1',s=20)p0=ax.scatter(dataset[id0,1],dataset[id0,2],color ='b',label='0',s=10)plt.legend(loc = 'upper right')plt.show这是以年龄和船票价分的
这是以年龄和性别分的
这是以性别和船票价分的
其实这里应该继续探讨特征间的相互联系,做集成算法。不过初步先直接选定年龄和船票价为分类标准吧(谁叫小白还不会处理定性特征值呢
)
三、准备数据
归一化数值
def autoNorm(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = zeros(shape(dataSet))#建立框架 m = dataSet.shape[0]#行数 normDataSet = dataSet - tile(minVals, (m,1))#每个数减该列最小 normDataSet = normDataSet/tile(ranges, (m,1)) #差再除以max-min return normDataSet, ranges, minVals四、测试算法
导入核心算法
def classify0(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0]#4l diffMat = tile(inX, (dataSetSize,1)) - dataSet#inx按4*1重复排列再与sataset做差 sqDiffMat = diffMat**2#每个元素平方 sqDistances = sqDiffMat.sum(axis=1)#一个框里的都加起来 distances = sqDistances**0.5#加起来之后每个开根号 sortedDistIndicies = distances.argsort() #返回的是数组值从小到大的索引值,最小为0 classCount={} for i in range(k):#即0到k-1.最后得到的classCount是距离最近的k个点的类分布,即什么类出现几次如{'A': 1, 'B': 2} voteIlabel = labels[sortedDistIndicies[i]]#返回从近到远第i个点所对应的类 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#字典模式,记录类对应出现次数.这里.get(a,b)就是寻找字典classcount的a对应的值,如果没找到a,就显示b sortedClassCount = sorted(classCount.iteritems(),key=Operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]测试算法def surviorclasstest(): hoRatio = 0.10 #hold out 10% dataset,classlabel=file2matrix('train1.csv') #load data setfrom file normMat, ranges, minVals = autoNorm(dataset) m = normMat.shape[0] numTestVecs = int(m*hoRatio) errorCount = 0.0 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],classlabel[numTestVecs:m],3) print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classlabel[i]) if (classifierResult != classlabel[i]): errorCount += 1.0 print "the total error rate is: %f" % (errorCount/float(numTestVecs)) print errorCount得到
the total error rate is: 0.269这里要提到一点,此时我们可以调节K值来使错误率变小,实际上K值越大错误率越小(验证了一下k=10时0.235,k=15时0.224,k=100时0.191)如果选择较小的k值,就相当于用较小的领域中的训练实例学习,其近似误差会小,但估计误差会大,其泛化性能会比较差,因为预测结果会对近邻的实例点非常敏感,即k值越小就意味着整体模型越复杂,容易发生过拟合;相对地,用较大的k值,可以减小学习的估计误差,但是近似误差会增大,这时与输入实例较远的不相似的训练实例也会起预测作用,使预测发生错误,夸张一点k=N时无论输入什么实例都返回整个训练集占多数的类,k值越大意味着整体模型变得简单,有可能欠拟合。在应用中,通常采用交叉验证法来选取较优的k值。
(from《机器学习》周志华)
这里我比较懒加上之前提过这里样本少很容易过拟合,再加上能上75%的准确率我已经很满意啦。这里就选择k=10吧。
五、使用算法
def classifyperson(): dataset,classlabel=file2matrix('train1.csv') normMat, ranges, minVals = autoNorm(dataset) testset,whatever=file2matrix('test.csv') normMattest, rangestset, minValstest = autoNorm(testset) h=testset.shape[0] for i in range(h): classifierResult = classify0(normMattest[i,:],normMat[:,:],classlabel[:],10) print classifierResult得到一串01的序列就可以提交啦。果然还是太粗糙,换成k=3
排名立马上升150,这脸打得好疼。
六、优化方向
1、其实性别影响很大的,后期要加入2、关于K值的选择和过拟合问题,可以引入正则化和交叉验证3、数据挖掘分类算法辣么多,都试试
感觉靠谱的还有逻辑回归,决策树和支持向量机(SVM)另外可以尝试集成学习
另外可以Bagging :有放回的自助采样,生成多棵决策树训练以及在此基础上再引入随机属性,进一步增加“多样性”(随机森林)(据说这一串下来可以到81%)
新闻热点
疑难解答