二、Python實現
對於機器學習而已,Python需要額外安裝三件寶,分別是Numpy,scipy和Matplotlib。前兩者用於數值計算,后者用於畫圖。安裝很簡單,直接到各自的官網下載回來安裝即可。安裝程序會自動搜索我們的python版本和目錄,然后安裝到python支持的搜索路徑下。反正就python和這三個插件都默認安裝就沒問題了。
另外,如果我們需要添加我們的腳本目錄進Python的目錄(這樣Python的命令行就可以直接import),可以在系統環境變量中添加:PYTHONPATH環境變量,值為我們的路徑,例如:E:\Python\Machine Learning in Action
2.1、kNN基礎實踐
一般實現一個算法后,我們需要先用一個很小的數據庫來測試它的正確性,否則一下子給個大數據給它,它也很難消化,而且還不利於我們分析代碼的有效性。
首先,我們新建一個kNN.py腳本文件,文件里面包含兩個函數,一個用來生成小數據庫,一個實現kNN分類算法。代碼如下:
- #########################################
- # kNN: k Nearest Neighbors
- # Input: newInput: vector to compare to existing dataset (1xN)
- # dataSet: size m data set of known vectors (NxM)
- # labels: data set labels (1xM vector)
- # k: number of neighbors to use for comparison
- # Output: the most popular class label
- #########################################
- from numpy import *
- import operator
- # create a dataset which contains 4 samples with 2 classes
- def createDataSet():
- # create a matrix: each row as a sample
- group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]])
- labels = ['A', 'A', 'B', 'B'] # four samples and two classes
- return group, labels
- # classify using kNN
- def kNNClassify(newInput, dataSet, labels, k):
- numSamples = dataSet.shape[0] # shape[0] stands for the num of row
- ## step 1: calculate Euclidean distance
- # tile(A, reps): Construct an array by repeating A reps times
- # the following copy numSamples rows for dataSet
- diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise
- squaredDiff = diff ** 2 # squared for the subtract
- squaredDist = sum(squaredDiff, axis = 1) # sum is performed by row
- distance = squaredDist ** 0.5
- ## step 2: sort the distance
- # argsort() returns the indices that would sort an array in a ascending order
- sortedDistIndices = argsort(distance)
- classCount = {} # define a dictionary (can be append element)
- for i in xrange(k):
- ## step 3: choose the min k distance
- voteLabel = labels[sortedDistIndices[i]]
- ## step 4: count the times labels occur
- # when the key voteLabel is not in dictionary classCount, get()
- # will return 0
- classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
- ## step 5: the max voted class will return
- maxCount = 0
- for key, value in classCount.items():
- if value > maxCount:
- maxCount = value
- maxIndex = key
- return maxIndex
然后我們在命令行中這樣測試即可:
- import kNN
- from numpy import *
- dataSet, labels = kNN.createDataSet()
- testX = array([1.2, 1.0])
- k = 3
- outputLabel = kNN.kNNClassify(testX, dataSet, labels, 3)
- print "Your input is:", testX, "and classified to class: ", outputLabel
- testX = array([0.1, 0.3])
- outputLabel = kNN.kNNClassify(testX, dataSet, labels, 3)
- print "Your input is:", testX, "and classified to class: ", outputLabel
這時候會輸出:
- Your input is: [ 1.2 1.0] and classified to class: A
- Your input is: [ 0.1 0.3] and classified to class: B
2.2、kNN進階
這里我們用kNN來分類一個大點的數據庫,包括數據維度比較大和樣本數比較多的數據庫。這里我們用到一個手寫數字的數據庫,可以到這里下載。這個數據庫包括數字0-9的手寫體。每個數字大約有200個樣本。每個樣本保持在一個txt文件中。手寫體圖像本身的大小是32x32的二值圖,轉換到txt文件保存后,內容也是32x32個數字,0或者1,如下:
數據庫解壓后有兩個目錄:目錄trainingDigits存放的是大約2000個訓練數據,testDigits存放大約900個測試數據。
這里我們還是新建一個kNN.py腳本文件,文件里面包含四個函數,一個用來生成將每個樣本的txt文件轉換為對應的一個向量,一個用來加載整個數據庫,一個實現kNN分類算法。最后就是實現這個加載,測試的函數。
- #########################################
- # kNN: k Nearest Neighbors
- # Input: inX: vector to compare to existing dataset (1xN)
- # dataSet: size m data set of known vectors (NxM)
- # labels: data set labels (1xM vector)
- # k: number of neighbors to use for comparison
- # Output: the most popular class label
- #########################################
- from numpy import *
- import operator
- import os
- # classify using kNN
- def kNNClassify(newInput, dataSet, labels, k):
- numSamples = dataSet.shape[0] # shape[0] stands for the num of row
- ## step 1: calculate Euclidean distance
- # tile(A, reps): Construct an array by repeating A reps times
- # the following copy numSamples rows for dataSet
- diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise
- squaredDiff = diff ** 2 # squared for the subtract
- squaredDist = sum(squaredDiff, axis = 1) # sum is performed by row
- distance = squaredDist ** 0.5
- ## step 2: sort the distance
- # argsort() returns the indices that would sort an array in a ascending order
- sortedDistIndices = argsort(distance)
- classCount = {} # define a dictionary (can be append element)
- for i in xrange(k):
- ## step 3: choose the min k distance
- voteLabel = labels[sortedDistIndices[i]]
- ## step 4: count the times labels occur
- # when the key voteLabel is not in dictionary classCount, get()
- # will return 0
- classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
- ## step 5: the max voted class will return
- maxCount = 0
- for key, value in classCount.items():
- if value > maxCount:
- maxCount = value
- maxIndex = key
- return maxIndex
- # convert image to vector
- def img2vector(filename):
- rows = 32
- cols = 32
- imgVector = zeros((1, rows * cols))
- fileIn = open(filename)
- for row in xrange(rows):
- lineStr = fileIn.readline()
- for col in xrange(cols):
- imgVector[0, row * 32 + col] = int(lineStr[col])
- return imgVector
- # load dataSet
- def loadDataSet():
- ## step 1: Getting training set
- print "---Getting training set..."
- dataSetDir = 'E:/Python/Machine Learning in Action/'
- trainingFileList = os.listdir(dataSetDir + 'trainingDigits') # load the training set
- numSamples = len(trainingFileList)
- train_x = zeros((numSamples, 1024))
- train_y = []
- for i in xrange(numSamples):
- filename = trainingFileList[i]
- # get train_x
- train_x[i, :] = img2vector(dataSetDir + 'trainingDigits/%s' % filename)
- # get label from file name such as "1_18.txt"
- label = int(filename.split('_')[0]) # return 1
- train_y.append(label)
- ## step 2: Getting testing set
- print "---Getting testing set..."
- testingFileList = os.listdir(dataSetDir + 'testDigits') # load the testing set
- numSamples = len(testingFileList)
- test_x = zeros((numSamples, 1024))
- test_y = []
- for i in xrange(numSamples):
- filename = testingFileList[i]
- # get train_x
- test_x[i, :] = img2vector(dataSetDir + 'testDigits/%s' % filename)
- # get label from file name such as "1_18.txt"
- label = int(filename.split('_')[0]) # return 1
- test_y.append(label)
- return train_x, train_y, test_x, test_y
- # test hand writing class
- def testHandWritingClass():
- ## step 1: load data
- print "step 1: load data..."
- train_x, train_y, test_x, test_y = loadDataSet()
- ## step 2: training...
- print "step 2: training..."
- pass
- ## step 3: testing
- print "step 3: testing..."
- numTestSamples = test_x.shape[0]
- matchCount = 0
- for i in xrange(numTestSamples):
- predict = kNNClassify(test_x[i], train_x, train_y, 3)
- if predict == test_y[i]:
- matchCount += 1
- accuracy = float(matchCount) / numTestSamples
- ## step 4: show the result
- print "step 4: show the result..."
- print 'The classify accuracy is: %.2f%%' % (accuracy * 100)
測試非常簡單,只需要在命令行中輸入:
- import kNN
- kNN.testHandWritingClass()
輸出結果如下:
- step 1: load data...
- ---Getting training set...
- ---Getting testing set...
- step 2: training...
- step 3: testing...
- step 4: show the result...
- The classify accuracy is: 98.84%
個人修改一些注釋:
# -*- coding: utf-8 -*-
""" KNN: K Nearest Neighbors Input: newInput:vector to compare to existing dataset(1xN) dataSet:size m data set of known vectors(NxM) labels:data set labels(1xM vector) k:number of neighbors to use for comparison Output: the most popular class labels N為數據的維度 M為數據個數 """ from numpy import * import operator #create a dataset which contains 4 samples with 2 classes def createDataSet(): #create a matrix:each row as a sample group = array([[1.0,0.9],[1.0,1.0],[0.1,0.2],[0.0,0.1]]) #four samples and two classes labels = ['A','A','B','B'] return group,labels #classify using KNN def KNNClassify(newInput, dataSet, labels, k): numSamples = dataSet.shape[0] #shape[0] stands for the num of row 即是m ##step 1:calculate Euclidean distance #tile(A,reps):Construct an array by repeating A reps times #the following copy numSamples rows for dataSet diff = tile(newInput,(numSamples,1)) - dataSet #Subtract element-wise squaredDiff = diff ** 2 #squared for the subtract squaredDist = sum(squaredDiff, axis = 1) #sum is performed by row distance = squaredDist ** 0.5 ##step 2:sort the distance #argsort() return the indices that would sort an array in a ascending order sortedDistIndices = argsort(distance) classCount = {} #define a dictionary (can be append element) for i in xrange(k): ##step 3:choose the min k diatance voteLabel = labels[sortedDistIndices[i]] ##step 4:count the times labels occur #when the key voteLabel is not in dictionary classCount,get() #will return 0 #按classCount字典的第2個元素(即類別出現的次數)從大到小排序 #即classCount是一個字典,key是類型,value是該類型出現的次數,通過for循環遍歷來計算 classCount[voteLabel] = classCount.get(voteLabel,0) + 1 ##step 5:the max voted class will return #eg:假設classCount={'A':3,'B':2} maxCount = 0 for key,value in classCount.items(): if value > maxCount: maxCount = value maxIndex = key return maxIndex