K近鄰演算法(三)--kaggle競賽之Titanic
小白好難得會用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 listdir import numpy as np dataset,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的序列就可以提交啦。
排名立馬上升150,這臉打得好疼。
六、優化方向
1、其實性別影響很大的,後期要加入 2、關於K值的選擇和過擬合問題,可以引入正則化和交叉驗證 3、資料探勘分類演算法辣麼多,都試試感覺靠譜的還有邏輯迴歸,決策樹和支援向量機(SVM)另外可以嘗試整合學習
另外可以Bagging :有放回的自助取樣,生成多棵決策樹訓練以及在此基礎上再引入隨機屬性,進一步增加“多樣性”(隨機森林)(據說這一串下來可以到81%)