問題
為了提高我的B格,我打算買瓶紅酒嘗嘗。但是我對紅酒一竅不通,不知道該如何鑒別紅酒質量的好壞。于是突發奇想,能否使用K-近鄰算法來幫助我選擇呢?
數據準備
我在網上下載了紅酒的數據集。該數據集收集了紅酒的11種特征及專家對每種酒的評價。這是數據的下載地址,下面列舉了部分樣本數據:
|序號|非揮發性酸度|揮發性酸度|檸檬酸|殘留糖|氯化物|游離二氧化硫|總二氧化硫|濃度|PH值|硫酸鹽|酒精度|質量|
|---|
|1|7|0.27|0.36|20.7|0.045|45|170|1.001|3|0.45|8.8|6|
|2|6.3|0.3|0.34|1.6|0.049|14|132|0.994|3.3|0.49|9.5|6|
|3|7.9|0.18|0.37|1.2|0.04|16|75|0.992|3.18|0.63|10.8|5|
數據的處理一共包括兩個步驟:讀取數據及數據預處理。讀取數據部分將數據分成紅酒數據部分及標簽部分。數據預處理主要是對數據部分進行歸一化處理。代碼如下:
import numpy as np
import csv
def generateData(filename):
data = []
with open(filename, 'r') as csvfile:
reader = csv.reader(csvfile, delimiter=';')
data = [row for row in reader]
data = data[1:]
data = np.array(data)
label = data[:,-1]
data = data[:,:-1]
data = data.astype(np.float)
label = label.astype(np.float)
#normlize to [0,1]
minVals = data.min(0)
maxVals = data.max(0)
data = (data-minVals)/(maxVals-minVals)
return data, label
K-近鄰算法
下面來寫K-近鄰算法的核心算法,算法的流程大概如下:首先計算測試樣本與所有訓練樣本之間的距離,選出距離最近的K個樣本,將這K個樣本中標簽最多的標簽作為這個測試樣本的標簽,代碼如下:
import numpy as np
import csv
import operator
def knnclassify(testData, trainData, trainLabel,k):
diff = trainData - testData
diffSq = diff**2
distances = np.sqrt(np.sum(diffSq, axis = 1))
sortedDistdicies = distances.argsort()
classCount = {}
for i in range(k):
votedLabel = trainLabel[sortedDistdicies[i]]
classCount[votedLabel] = classCount.get(votedLabel, 0) + 1
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
現在我買了一瓶紅酒,并記錄下它的各種特征,判斷它等級的代碼如下:
testData = np.array([0.2,0.3,0.11,0.13,0.16,0.2,0.3,0.11,0.13,0.16,0.27])
data, label = generateData('winequality-red.csv')
k=9
level = knnclassify(testData, data, label, k)
print level
從結果可以看出,我們這瓶酒的等級為5,屬于中等紅酒。對于我這種level來說,應該足夠了。
評價分類器 在我品嘗這瓶紅酒的時候,我心中不禁有點不安:如果這個K-近鄰分類方法“騙了”我怎么辦? 作為一個嚴謹的人,我一定得找到一個方法來檢驗這個分類器的效度。
驗證分類器的方法非常簡單,首先我們將之前的數據一分為二,一份作為訓練集,一份作為測試集。用訓練集來預測測試集,因為我們已經知道測試集的真正等級,所以,我們可以通過比較我們的預測結果與真實結果之間的差異來評估分類器的效度。測試的代碼為:
data, label = generateData('winequality-red.csv')
ratio = 0.1
k = 9
m = int(ratio*data.shape[0])
testData = data[:m];
trainData = data[m:]
testLabel = label[:m]
trainLabel = label[m:]
errorCount = 0
for i in range(testData.shape[0]):
predict = knnclassify(testData[i], trainData, trainLabel, k)
print "the predict is %f, the ground truth is %f" %(predict, testLabel[i])
if(predict != testLabel[i]):
errorCount = errorCount + 1;
print "the total error rate is %f" %(errorCount/float(testData.shape[0]))
最后輸出的錯誤率為0.39。還是比較可信,至少是個及格的分數。在后面的文章中,我會嘗試使用其他的分類方法,以期達到更好的結果。
關于K的選擇
在前面的代碼中,K的值我都取做9,但是在實踐中,我們需要通過實驗得出K的最佳取值。操作也非常簡單,就是K取不同的值,然后得出不同的錯誤率,取錯誤率最低的那個K值作為我們最終的選擇。
本篇文章的完整代碼可以在這里下載