題目
連接:https://www.kaggle.com/c/titanic
前言
和前幾篇差不多,這次換成svm,實際上,對于svm的理解是有了,知道是怎么做的了,但具體公式如何推導,還是不會,但是,這不影響寫代碼,使用現成的庫函數就可以搞定,有時候,這些工具大大簡化了我們的學習成本,知道基本原理,然后去使用就行了,具體的細節也不用去關心。用陶淵明的話來說,就是,“好讀書,不求甚解;每有會意,便欣然忘食.”。在機器學習方面,對于我這種人來說,更多的精力應該放在如何應用,如何處理數據與訓練好模型上,去理解底層原理然后對學習方法進行一些改進之類的,只能說現在還達不到那種境界。
SVM
這里簡單介紹一下svm的基本原理就結束了~ 我認為這個大概理解一下就行了,后面真的遇到瓶頸的時候再仔細研究一下吧~
SVM全稱是支持向量機(Support Vector Machine),聽起來還是挺高端的,讓我想起來搞acm的時候第一次聽到AC自動機的時候,哈哈。
SVM的分類方法其實和邏輯回歸類似,想象我們有一堆數據,這個數據有兩個屬性,也就是x是一個二維向量,那么就是類似這樣一個圖:
在實際數據中,紅色的是一類,藍色的是一類,我們想要用一個線性函數來將這些數據分類,即在函數圖像將平面分成兩個區域,一個區域內的點認為是紅色類,另一個區域內的點認為是藍色的點。仔細思考一下就可以發現,這是不可能實現的,無論如何在平面中畫線,都不能比較完美的分割。所以svm算法提出了一個思路:把n維空間無法解決的問題,放在n+1或者更高維度的空間中去分類。想象我們把上圖中的二維平面的點放到三維空間中,我們就可以構造出這樣的集合:
在三維空間中,二維平面中的點由于多了一維,有了“高度”,我們就可以用一個平面將這兩個集合完美劃分:
在svm算法體系中,這個平面就叫做“超平面”,這是一個抽象的面的概念,在一維空間里就是一個點,二維空間里就是一條直線,三維空間里就是一個平面,以此類推。
那么如何把一個n維空間中的點映射到更高維上去呢?這就要用核函數(Kernel)去構造,常見的核函數有:線性核函數、多項式核函數、徑向核函數、高斯核函數等等……至于這些函數如何運作的,這里就不介紹了(其實是因為不會,嘻嘻)。值得一提的是,核函數并沒有增加更多的維度,也就是你原來的輸入是二維向量,那升維以后的輸入也是相同的,核函數的其他維度表示的只是一個變量代換的關系。
擬合問題與奧卡姆剃刀原則
上面提到svm可以把n維空間中的點映射到更高維,那么,升維操作是不是做越多越好呢?比如把上圖中的二維空間中的點映射到十維空間中,是不是比映射到三維空間中結果更好?
下面用一個多項式函數擬合模型來解釋這個問題:
在上面這個圖中,用了3個模型擬合一個訓練樣本,圖左是用一個線性函數來進行擬合,最終的模型會欠擬合,它無法捕捉數據中的曲率信息,圖中是用一個二次函數來擬合,從效果上看,它的泛化能力比較好,沒有明顯的過擬合或者欠擬合現象。圖右是一個9階的多項式函數擬合結果,這會導致過擬合,雖然在訓練數據上表現的很好,但是沒有一個比較有效的結構信息,在真實數據中,并沒有想前兩個點之間中有一個“深谷”。
那么,如何選擇擬合模型的維度,或者說容量?能夠比較好的擬合數據,有良好的泛化能力,不至于有明顯的欠擬合或過擬合的現象發生呢?許多早期的學者提出一個簡約原則,現代廣泛被稱為奧卡姆剃刀。該原則說,“如無必要,勿增實體”,在同樣能夠解釋已知觀測現象的假設中,我們應該挑選“最簡單”的那一個。所以,不只是針對svm,對于所有機器學習模型來說,都有各種各樣的過擬合與欠擬合問題,其實都可以遵循這個簡單的原則,模型的能力如果可以對數據進行解釋,那么就不要去增加它的復雜度。
代碼與結果
下面上代碼,嘗試了各種核函數,其中,線性核函數(linear)表現的最好,準確率有0.76555。
import csv
import os
from sklearn import svm
def readData(fileName):
result = {}
with open(fileName,'rb') as f:
rows = csv.reader(f)
for row in rows:
if result.has_key('attr_list'):
for i in range(len(result['attr_list'])):
key = result['attr_list'][i]
if not result.has_key(key):
result[key] = []
result[key].append(row[i])
else:
result['attr_list'] = row
return result
def writeData(fileName, data):
csvFile = open(fileName, 'w')
writer = csv.writer(csvFile)
n = len(data)
for i in range(n):
writer.writerow(data[i])
csvFile.close()
def convertData(dataList):
hashTable = {}
count = 0.0
for i in range(len(dataList)):
if not hashTable.has_key(dataList[i]):
hashTable[dataList[i]] = count
count += 1
dataList[i] = hashTable[dataList[i]]
def convertValueData(dataList):
sumValue = 0.0
count = 0
for i in range(len(dataList)):
if dataList[i] == "":
continue
sumValue += float(dataList[i])
count += 1
dataList[i] = float(dataList[i])
avg = sumValue / count
for i in range(len(dataList)):
if dataList[i] == "":
dataList[i] = avg
def dataPredeal(data):
convertValueData(data["Age"])
convertData(data["Fare"])
convertData(data["Pclass"])
convertData(data["Sex"])
convertData(data["SibSp"])
convertData(data["Parch"])
convertData(data["Embarked"])
def getX(data):
x = []
ignores = {"PassengerId":1, "Survived":1, "Name":1,"Ticket":1, "Cabin":1, "Fare":1, "Embarked":1}
for i in range(len(data["PassengerId"])):
x.append([])
for j in range(len(data["attr_list"])):
key = data["attr_list"][j]
if not ignores.has_key(key):
x[i].append(data[key][i])
return x
def getLabel(data):
label = []
for i in range(len(data["PassengerId"])):
label.append(int(data["Survived"][i]))
return label
def calResult(x,label, input_x):
svmcal = svm.SVC(kernel='linear').fit(x, label)
return svmcal.predict(input_x)
def run():
dataRoot = '../../kaggledata/titanic/'
data = readData(dataRoot + 'train.csv')
test_data = readData(dataRoot + 'test.csv')
dataPredeal(data)
dataPredeal(test_data)
x = getX(data)
label = getLabel(data)
input_x = getX(test_data)
x_result = calResult(x, label,input_x)
res = [[test_data["PassengerId"][i], x_result[i]] for i in range(len(x_result))]
res.insert(0, ["PassengerId", "Survived"])
writeData(dataRoot + 'result.csv', res)
run()