由於KNN的計算量太大,還沒有使用KD-tree進行優化,所以對於60000訓練集,10000測試集的數據計算比較慢。這里只是想測試觀察一下KNN的效果而已,不調參。
K選擇之前看過貌似最好不要超過20,因此,此處選擇了K=10,距離為歐式距離。如果需要改進,可以再調整K來選擇最好的成績。
先跑了一遍不經過scale的,也就是直接使用像素灰度值來計算歐式距離進行比較。發現開始基本穩定在95%的正確率上,嚇了一跳。因為本來覺得KNN算是沒有怎么“學習”的機器學習算法了,猜測它的特點可能會是在任何情況下都可以用,但都表現的不是最好。所以估計在60%~80%都可以接受。沒想到能基本穩定在95%上,確定算法和代碼沒什么問題后,突然覺得是不是這個數據集比較沒挑戰性。。。
去MNIST官網(http://yann.lecun.com/exdb/mnist/),上面掛了以該數據集為數據的算法的結果比較。查看了一下KNN,發現有好多,而且錯誤率基本都在5%以內,甚至能做到1%以內。唔。
跑的結果是,正確率:96.687%。也就是說,錯誤率error rate為3.31%左右。
再跑一下經過scale的數據,即對灰度數據歸一化到[0,1]范圍內。看看效果是否有所提升。
經過scale,最終跑的結果是,正確率:竟然也是96.687%! 也就是說,對於該數據集下,對KNN的數據是否進行歸一化並無效果!
在跑scale之前,個人猜測:由於一般對數據進行處理之前都進行歸一化,防止高維詛咒(在784維空間中很容易受到高維詛咒)。因此,預測scale后會比前者要好一些的。但是,現在看來二者結果相同。也就是說,對於K=10的KNN算法中,對MNIST的預測一樣的。
對scale前后的正確率相同的猜測:由於在訓練集合中有60000個數據點,因此0-9每個分類平均都有6000個數據點,在這樣的情況下,對於測試數據集中的數據點,相臨近的10個點中大部分都是其他分類而導致分類錯誤的概率會比較地(畢竟10相對與6000來說很小),所以,此時,KNN不僅可以取得較好的分類效果,而且對於是否scale並不敏感,效果相同。
代碼如下:
- #KNN for MNIST
- from numpy import *
- import operator
- def line2Mat(line):
- line = line.strip().split(' ')
- label = line[0]
- mat = []
- for pixel in line[1:]:
- pixel = pixel.split(':')[1]
- mat.append(float(pixel))
- return mat, label
- #matrix should be type: array. Or classify() will get error.
- def file2Mat(fileName):
- f = open(fileName)
- lines = f.readlines()
- matrix = []
- labels = []
- for line in lines:
- mat, label = line2Mat(line)
- matrix.append(mat)
- labels.append(label)
- print 'Read file '+str(fileName) + ' to matrix done!'
- return array(matrix), labels
- #classify mat with trained data: matrix and labels. With KNN's K set.
- def classify(mat, matrix, labels, k):
- diffMat = tile(mat, (shape(matrix)[0], 1)) - matrix
- #diffMat = array(diffMat)
- sqDiffMat = diffMat ** 2
- sqDistances = sqDiffMat.sum(axis=1)
- distances = sqDistances ** 0.5
- sortedDistanceIndex = distances.argsort()
- classCount = {}
- for i in range(k):
- voteLabel = labels[sortedDistanceIndex[i]]
- classCount[voteLabel] = classCount.get(voteLabel,0) + 1
- sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1),reverse=True)
- return sortedClassCount[0][0]
- def classifyFiles(trainMatrix, trainLabels, testMatrix, testLabels, K):
- rightCnt = 0
- for i in range(len(testMatrix)):
- if i % 100 == 0:
- print 'num '+str(i)+'. ratio: '+ str(float(rightCnt)/(i+1))
- label = testLabels[i]
- predictLabel = classify(testMatrix[i], trainMatrix, trainLabels, K)
- if label == predictLabel:
- rightCnt += 1
- return float(rightCnt)/len(testMatrix)
- trainFile = 'train_60k.txt'
- testFile = 'test_10k.txt'
- trainMatrix, trainLabels = file2Mat(trainFile)
- testMatrix, testLabels = file2Mat(testFile)
- K = 10
- rightRatio = classifyFiles(trainMatrix, trainLabels, testMatrix, testLabels, K)
- print 'classify right ratio:' +str(right)