1. 程式人生 > >機器學習之K-近鄰演算法(二)

機器學習之K-近鄰演算法(二)

本章內容:

  • K-近鄰分類演算法
  • 從文字檔案中解析和匯入資料
  • 使用matplotlib建立擴散圖
  • 歸一化數值

2-1 K-近鄰演算法概述

簡單的說,K-近鄰演算法採用測量不同特徵值之間的距離方法進行分類。

K-近鄰演算法

  • 優點:精度高、對異常值不敏感、無資料輸入假定
  • 缺點:計算複雜度高、空間複雜度高
  • 適用資料範圍:數值型和標稱型

K-近鄰演算法(KNN),工作原理:
存在一個樣本資料集合,稱之為訓練樣本集,並且樣本集中的每個資料都存在標籤,即我們知道集中每一資料與所屬分類的對應關係。
輸入沒有標籤的新資料後,將新資料的每個特徵與樣本集中資料對應的特徵進行比較,然後演算法提取樣本集中特徵最相似資料(最近鄰)的分類標籤。
一般來說,我們只選擇樣本資料集中前K個最相似的資料,這就是K-近鄰演算法中K的出處,通常K是不大於20的整數。
最後,選擇K個最相似資料出現次數最多的分類,作為新資料的分類。

數學公式
這裡寫圖片描述

K-近鄰演算法的一般流程

  • 收集資料:可以使用任何方法
  • 準備資料:距離計算所需要的數值,最好是結構化的資料格式
  • 分析資料:可以使用任何方法
  • 訓練演算法:此步驟不適用於K-近鄰演算法
  • 測試演算法:計算錯誤率
  • 使用演算法:首先需要輸入樣本資料和結構化的輸出結果,然後執行K-近鄰演算法判定輸入資料分別屬於哪個分類,最後應用對計算出的分類執行後續的處理。

2.1.1 準備:使用python匯入資料
首先,我們建立名為kNN.py的python模組。

from numpy import *
import operator

def createDataSet()
:
group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) labels=['A','A','B','B'] return group,labels

上面的程式碼中,我們匯入了兩個模組,第一個是科學計算包NumPy,第二個是運算子模組,k-近鄰演算法執行排序操作使用這個模組提供的函式。
我們現在要引入自定義的kNN模組。
要在python shell中呼叫這個函式,進入python互動開發環境
我們先使用os模組,
檢視當前路徑 os.getcwd()
更改當前路徑 os.chdir()

In [16]: import os
In [17]: os.getcwd() Out[17]: 'C:\\Users\\Administrator' In [18]: os.chdir("H:\ML") In [19]: import kNN In [20]: group,labels=kNN.createDataSet() In [21]: group Out[21]: array([[ 1. , 1.1], [ 1. , 1. ], [ 0. , 0. ], [ 0. , 0.1]]) In [24]: labels Out[24]: ['A', 'A', 'B', 'B']

2.1.2 從文字檔案中解析資料

我們給出k-近鄰演算法的虛擬碼和實際的python程式碼。
虛擬碼如下:
對未知類別屬性的資料集中的每個點依次執行以下操作:

(1)計算已知類別資料集中的點與當前點之間的距離;
(2)按照距離遞增次序排序;
(3)選取與當前點距離最小的k和點;
(4)確定前k個點所在類別的出現頻率;
(5)返回當前k和點出現頻率最高類別作為當前點的預測分類;

python函式classify0() 程式碼如下:

def classify0(inX,dataSet,labels,k):
    """應用KNN方法對測試點進行分類,返回一個結果型別 

    Keyword argument: 
    testData: 待測試點,格式為陣列 
    dataSet: 訓練樣本集合,格式為矩陣 
    labels: 訓練樣本型別集合,格式為陣列 
    k: 近鄰點數 
    """  
    dataSetSize=dataSet.shape[0]
    #距離計算,新的資料與樣本的距離進行減法
    diffMat = tile(inX, (dataSetSize,1)) - dataSet 
    sqDiffMat=diffMat**2  #對陣列的每一項進行平方
    sqDistances=sqDiffMat.sum(axis=1)  #陣列每個特徵值進行求和
    distances=sqDistances**0.5   #每個值開方
    sortedDistIndicies = distances.argsort() 索引值排序
    #選取距離最小的前k個值進行索引,從k箇中選取分類最多的一個作為新資料的分類     
    classCount={}   
    for i in range(k):   #統計前k個點所屬類別
        voteIlabel=labels[sortedDistIndicies[i]]
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
    sortedClassCount=sorted(classCount.iteritems(),
                                   key=operator.itemgetter(1),reverse=True)
    return sortedClassCount[0][0]   #返回前k個點鐘頻率最高的類別

程式中使用了歐氏距離公式,計算兩個向量點xA和xB之間的距離
這裡寫圖片描述

kNN演算法核心:
(1)計算當前點和訓練集中的每個點的歐式距離
(2)從小到大排列訓練集中前k個點
(3)返回這些點中出現頻率最高的

python函式classify0() 程式碼 語法解析:

(1).用於分類的向量是inX,輸入訓練樣本集為dataSet,標籤向量為labels,引數k表示用於選擇最近鄰居的數目,其中標籤向量的元素數目和矩陣dataSet的行數相同。

(2).shape返回array的大小,shape[0] 為第一維大小(訓練集資料量)

(3).tile(inX,(dataSetSize,1)-dataSet) :把inX按照(dataSetSize,1的形式複製,即:(dataSetSize,1是一個矩陣,把矩陣的每個元素用inX替代的就是最後結果。
例如:

In [31]: import numpy as np

In [32]: a=np.array([0,1,2])

In [34]: np.tile(a,2)
Out[34]: array([0, 1, 2, 0, 1, 2])

In [35]: np.tile(a,(2,2))
Out[35]:
array([[0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2]])

In [36]: b=np.array=[[4,5],[6,7]]

In [37]: np.tile(b,2)
Out[37]:
array([[4, 5, 4, 5],
[6, 7, 6, 7]])

(4). argsort() ,返回排序後的下標array

(5). 字典dict.get(key,x)查詢鍵為key的value,如果不存在返回x

(6).operator.itemgetter(1)返回的物件是第i+1個元素,相當於匿名函式

測試演算法:

In [85]: import kNN

In [86]: reload(kNN)
Out[86]: <module 'kNN' from 'kNN.pyc'>

In [87]: group,labels=kNN.createDataSet()

In [88]: group,labels
Out[88]:
(array([[ 1. , 1.1],
[ 1. , 1. ],
[ 0. , 0. ],
[ 0. , 0.1]]), ['A', 'A', 'B', 'B'])

In [89]: kNN.classify0([0,0],group,labels,3)
Out[89]: 'B'

測試結果,[0,0]屬於分類B 。

2.2 使用K-近鄰演算法來改進約會網站
示例:在約會網站上使用K-近鄰演算法

收集資料:提供文字檔案 準備資料:使用python解析文字檔案 分析資料:使用matplotlib畫二維擴散圖
訓練演算法:此步驟不適應於K-近鄰演算法
測試演算法:使用海倫提供的部分資料作為測試樣本
測試樣本和非測試樣本的區別在於:測試樣本是已經完成分類的資料,如果預測分類與實際類別不同,則標記為一個錯誤。
使用演算法:產生簡單的命令列程式,然後海倫可以輸入一些特徵資料以判斷對方是否為自己喜歡的型別

2.2.1 準備資料:從文字檔案中解析資料
海倫的資料樣本特徵:
每年獲得的飛行常客里程數
玩視訊遊戲所耗時間百分比
每週消費的冰淇淋公升數

我們將海倫提供的樣本特徵資料輸入到分類器之前,必須將待處理的資料格式轉換為分類器可以接受的格式。
我們在kNN.py中建立名為file2matrix的函式,用來處理輸入格式問題。
該函式的輸入為文字名字串,輸出為訓練樣本矩陣的和類標籤向量。

將下列的程式碼增加到kNN.py中:

def file2matrix(filename):
    fr=open(filename)
    arrayOLines=fr.readlines()
    #得到文字行數
    numberOfLines=len(arrayOLines)

    #建立返回的numpy矩陣
    returnMat=zeros((numberOfLines,3))
    classLabelVector = []
    index=0

    #解析文字資料到列表
    for line in arrayOLines:
        line=line.strip()    #擷取掉所有回車字元
        listFromLine=line.split('\t') #以指定字元為分割符分割字串,不指定則為空格 
        returnMat[index,:]=listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index +=1
    return returnMat,classLabelVector

我們需要重新載入kNN模組,否則python還是使用之前的載入的模組。

In [46]: import kNN

In [47]: reload(kNN)
Out[47]: <module 'kNN' from 'kNN.pyc'>

In [48]: datingDataMat,datingLabels =kNN.file2matrix('datingTestSet2.txt')

In [49]: datingDataMat
Out[49]:
array([[  4.09200000e+04,   8.32697600e+00,   9.53952000e-01],
       [  1.44880000e+04,   7.15346900e+00,   1.67390400e+00],
       [  2.60520000e+04,   1.44187100e+00,   8.05124000e-01],
       ..., 
       [  2.65750000e+04,   1.06501020e+01,   8.66627000e-01],
       [  4.81110000e+04,   9.13452800e+00,   7.28045000e-01],
       [  4.37570000e+04,   7.88260100e+00,   1.33244600e+00]])

In [50]: datingLabels[0:20]
Out[50]: [3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3]

我們已經匯入資料,接下來需要了解資料的真實含義,一般來說,除了瀏覽資料,我們會採用圖形化的方式直觀的去展示資料。

2.2. 分析資料:使用matplotlib建立散點圖

In [60]: import numpy as np
    ...: import matplotlib
    ...: import matplotlib.pyplot as plt

In [64]: fig = plt.figure()  #建立一張新的影象 
    ...: ax=fig.add_subplot(111)  #表示把影象分割為1行1列,當前子影象畫在第1塊 
         #scatter(X,Y) 以X為x座標,Y為y座標繪製散點圖
    ...: ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
    ...: ax.axis([-2,25,-0.2,2.0])
    ...: plt.ylabel('Kilogram of ice cream per week')
    ...: plt.xlabel('Percentage of time spent playing games')
    ...: plt.show()

這裡寫圖片描述

沒有使用樣本分類的特徵值,很難從上圖中看到任何有用的資料模式資訊。為此,我們重新採用彩色的來標記不同樣本。

In [65]: fig = plt.figure()
    ...: ax=fig.add_subplot(111)
    ...: ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*np.array(datingLabels),                           15.0*np.array(datingLabels))
    ...: ax.axis([-2,25,-0.2,2.0])
    ...: plt.ylabel('Kilogram of ice cream per week')
    ...: plt.xlabel('Percentage of time spent playing games')
    ...: plt.show()

這裡寫圖片描述

In [66]: fig = plt.figure()
    ...: ax=fig.add_subplot(111)
    ...: ax.scatter(datingDataMat[:,0], datingDataMat[:,1],                                  15.0*array(datingLabels),15.0*array(datingLabels))
    ...: plt.ylabel('Percentage of time spent playing games')
    ...: plt.xlabel('The number of frequent flier miles per year')
    ...: plt.show()

這裡寫圖片描述

散點圖使用datingDataMat矩陣的第一、第二列資料,分別表示特徵值“每年獲得的飛行常客里程數”和“玩視訊遊戲所耗時間百分比”。

圖中給出了不同樣本分類區域。

2.2.3 準備資料:歸一化資料

不同的特徵值將會有不同的取值和範圍,如果直接使用特徵值來計算距離,取值範圍較大的特徵值將會對結果產生較大的影響,取值範圍小的值將會對結果產生很小的影響。這使得較小的特徵值沒有起到作用。

如兩組特徵:{0, 20000, 1.1}和{67, 32000, 0.1},計算距離的算式為:
這裡寫圖片描述

那我們看到上面的計算式裡面,只有第二個特徵會產生很大的影響,第一個,第三個特徵則影響很小,甚至可以忽略掉。

但是三個特徵是同等重要的,因此三個等權重的特徵之一,飛行時間不能去這麼嚴重的影響結果。

處理上面的問題,我們採用的方式是將資料值歸一化:

如將取值範圍處理為0到1 的或者-1到1之間。給出一個公式,可以將任意取值範圍的特徵值轉換為0到1區間的值:

newValue=(oldValue-min)/(max-min)

其中,max和min分別是資料集中最大和最小的特徵值。
雖然我們改變取值範圍增加了分類器的複雜度,但是可以得到準確的結果。

接下來我們在kNN.py中增加一個函式autoNorm(),這個函式可以自動將數字特徵值轉化為0到1之間的區間。

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))
    return normDataSet,ranges,minVals

檢測函式執行結果:

In [99]: import kNN

In [100]: reload(kNN)
Out[100]: <module 'kNN' from 'kNN.py'>

In [101]: normMat,ranges,minVals=kNN.autoNorm(datingDataMat)

In [102]: normMat
Out[102]:
array([[ 0.44832535, 0.39805139, 0.56233353],
[ 0.15873259, 0.34195467, 0.98724416],
[ 0.28542943, 0.06892523, 0.47449629],
...,
[ 0.29115949, 0.50910294, 0.51079493],
[ 0.52711097, 0.43665451, 0.4290048 ],
[ 0.47940793, 0.3768091 , 0.78571804]])

In [103]: ranges
Out[103]: array([ 9.12730000e+04, 2.09193490e+01, 1.69436100e+00])

In [104]: minVals
Out[104]: array([ 0. , 0. , 0.001156])

2.2.4 測試演算法:作為完整程式驗證分類器

我們將測試分類器的效果,如果分類器的正確率滿足要求,海倫就可以使用這個名單來處理約會網站這個事情了。

機器學習演算法重要的一個工作就是:評估演算法的正確率為多少。

通常我們給出樣本資料的90%作為訓練樣本來訓練分類器,剩下的10%的資料去測試分類器,檢測分類器的正確率。值得注意的是:10%的資料應該是隨機選擇的。

對於分類器:錯誤率就是分類器給出錯誤結果的次數除以測試資料的總數。完美分類器的錯誤率為0,錯誤率為1的分類就不會有正確結果。

下面給出分類器針對約會網站的測試程式碼

def datingClassTest():
    hoRatio=0.10
    datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
    normMat,ranges,minVals=autoNorm(datingDataMat)
    m=normMat.shape[0]
    numTestVecs=int(m*hoRatio)
    errorCount=0.0
    for i in range(numTestVecs):
        classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],\
                                   datingLabels[numTestVecs:m],3)
        print "the classifier came back with:%d,the real anwer is:%d"\
                                   % (classifierResult,datingLabels[i])
        if (classifierResult!=datingLabels[i]):errorCount +=1.0
    print "the total error rate is: %f" % (errorCount/float(numTestVecs))

接下來我們執行分類器的測試程式:

In [112]: import kNN

In [113]: reload(kNN)
Out[113]: <module 'kNN' from 'kNN.py'>

In [114]: kNN.datingClassTest()
the classifier came back with:3,the real anwer is:3
the classifier came back with:2,the real anwer is:2
the classifier came back with:1,the real anwer is:1
...
...

the classifier came back with:2,the real anwer is:2
the classifier came back with:1,the real anwer is:1
the classifier came back with:3,the real anwer is:1
the total error rate is: 0.050000

我們看到最終分類器的處理約會資料集的錯誤率為5%,這是相對不錯的結果。
我們可以改變函式datingClassTest內變數hoRatio和變數k的值,檢測錯誤率是否隨著變化量值的變化而增加。

這個例子表明我們可以正確地預測分類,錯誤率僅僅是2.4%。海倫完全可以輸入未知物件的屬性資訊,由分類軟體來幫助她判定某一物件的可交往程度:討厭、一般喜歡、非常喜歡。

2.2.5使用演算法:構建完整的可用系統

上面我們已經在資料上對分類器進行測試,現在就額可以去使用這個分類器來對人們進行分類。
我們給出下面的程式碼,海倫只需要在約會網站上找到某個人輸入資訊,程式碼就可以給出她的喜歡程度的預測值。

我們將程式碼新增到kNN.py中:(約會網站預函式)

def classifyPerson():
    resultList=['not at all','in small dose','in large doses']
    percentTats=float(raw_input(\
                          "percentage of time spent playing video games?"))    
    ffMiles=float(raw_input("frequent flier miles earned per year?"))
    iceCream=float(raw_input("liters of ice cream consumed per year?"))
    datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
    normMat,ranges,minVals=autoNorm(datingDataMat)
    inArr=array([ffMiles,percentTats,iceCream])
    classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
    print "You will probably like this person:",\
                                  resultList[classifierResult -1]

增加了一個執行三個月文字行輸入命令的函式raw_input()
我們來檢驗實際執行效果:

In [127]: import kNN

In [128]: reload(kNN)
Out[128]: <module 'kNN' from 'kNN.py'>

In [129]: kNN.classifyPerson()

percentage of time spent playing video games?9

frequent flier miles earned per year?9000

liters of ice cream consumed per year?0.4
You will probably like this person: in small dose

我們看過最後輸入資料之後,程式預測出這個人一點也不喜歡,這樣海倫也許就沒有必要進行這次約會,可以篩選下一個目標了。

2.3 手寫識別系統
為了簡單起見,構造的識別系統只能識別數字0到9 。

2.3.1準備資料:將影象轉換為測試向量。
我們把一個32*32的二進位制影象矩陣轉換為1*1024的向量,這樣使用前面的分類器就可以處理影象資訊了。
給kNN.py新增下列程式碼:

def img2vector(filename):
    #該函式建立1*1024的NumPy陣列
    returnVect = zeros((1,1024))
    fr = open(filename)
    #迴圈出文件的前32行,並將每行的頭32行儲存在NumPy陣列熵,最後返回陣列
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect

測試一下程式碼:

In [19]: import kNN

In [20]: reload(kNN)
Out[20]: <module 'kNN' from 'kNN.pyc'>

In [21]: testVector = kNN.img2vector(r'E:\ML\ML_source_code\machinelearninginaction\Ch02\digits\testDigits\0_13.txt')

In [22]: testVector[0,0:31]
Out[22]: array([ 0., 0., 0., ..., 0., 0., 0.])

In [23]: testVector[0,32:63]
Out[23]: array([ 0., 0., 0., ..., 0., 0., 0.])

2.3.2 測試演算法:使用k-近鄰演算法識別手寫數字
os模組有函式listdir,可以列出給定目錄的檔名,我們確保指令碼檔案有
from os import listdir

手寫數字識別系統的測試程式碼如下,

def handwritingClassTest():
    hwLabels = []
    #獲取目錄內容
    trainingFileList = listdir(r'E:\ML\ML_source_code\mlia\Ch02\digits\trainingDigits')
    #trainingFileList下面有1934個檔案
    m = len(trainingFileList)
    #形成了一個1934*1024的0矩陣
    trainingMat = zeros((m,1024))
    #從檔名解分類數字
    for i in range(m):
        #構造要開啟的檔名
        fileNameStr = trainingFileList[i]
        #按照"."分開取第一個數
        fileStr = fileNameStr.split('.')[0]
        #按照"_"來分開來取第一數值並強制轉換為int型別
        classNumstr = int(fileStr.split('_')[0])
        hwLabels.append(classNumstr)
        trainingMat[i,:] = img2vector(r'E:\ML\ML_source_code\mlia\Ch02\digits\trainingDigits/%s' %fileNameStr)
    testFileList = listdir(r'E:\ML\ML_source_code\mlia\Ch02\digits\testDigits')
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumstr = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector(r'E:\ML\ML_source_code\mlia\Ch02\digits\trainingDigits/%s' %fileNameStr)
        classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3)
        print "the classifier came back with: %d, the read answer is:%d" %(classifierResult,classNumstr) 
        #計算錯誤率
        if (classifierResult !=classNumstr):
            errorCount += 1.0
    print "\nthe total number of errors is %d" % errorCount
    print "\nthe total error rate is: %f" % (errorCount/float(mTest))

測試函式的結果,輸出結果:

In [102]: reload(kNN)
Out[102]: <module 'kNN' from 'kNN.pyc'>

In [103]: kNN.handwritingClassTest()
the classifier came back with: 0, the read answer is:0
the classifier came back with: 0, the read answer is:0
the classifier came back with: 0, the read answer is:0
..
the classifier came back with: 4, the read answer is:4
the classifier came back with: 4, the read answer is:4
the classifier came back with: 9, the read answer is:5
the classifier came back with: 5, the read answer is:5
...
the classifier came back with: 9, the read answer is:9
the classifier came back with: 9, the read answer is:9
the classifier came back with: 9, the read answer is:9

the total number of errors is 15

the total error rate is: 0.015856

k-近鄰演算法手寫數字資料集,錯誤率為1.58% 。
執行效率並不高。K-決策樹其實就是k-近鄰演算法的優化版。可以節省計算開銷。

下面給出所有程式碼:


# -*- coding: utf-8 -*-
"""
Created on Sat Jun 01 22:46:19 2017

@author: yi
"""

from numpy import *
from os import listdir
import operator

def createDataSet():
    group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels=['A','A','B','B']
    return group,labels


#==============================================================================
# K近鄰演算法
def classify0(inX,dataSet,labels,k):
    """應用KNN方法對測試點進行分類,返回一個結果型別 

    Keyword argument: 
    testData: 待測試點,格式為陣列 
    dataSet: 訓練樣本集合,格式為矩陣 
    labels: 訓練樣本型別集合,格式為陣列 
    k: 近鄰點數 
    """  
    #讀取矩陣長度
    dataSetSize=dataSet.shape[0]
    #距離計算
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    sqDiffMat=diffMat**2
    sqDistances=sqDiffMat.sum(axis=1)
    distances=sqDistances**0.5
    #argsort函式返回的是陣列值從小到大的索引值
    sortedDistIndicies = distances.argsort() 
    classCount={}
    for i in range(k):
        voteIlabel=labels[sortedDistIndicies[i]]
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
    sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
    return sortedClassCount[0][0]

#==============================================================================
# 將文字記錄轉換為NumPy的解析程式
def file2matrix(filename):
    fr=open(filename)
    arrayOLines=fr.readlines()
    #得到文字行數
    numberOfLines=len(arrayOLines)

    #建立返回的numpy矩陣
    returnMat=zeros((numberOfLines,3))
    classLabelVector = []
    index=0

    #解析文字資料到列表
    for line in arrayOLines:
        line=line.strip()    #擷取掉所有回車字元
        listFromLine=line.split('\t')
        returnMat[index,:]=listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index +=1
    return returnMat,classLabelVector

#==============================================================================
#歸一化特徵值        
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))
    return normDataSet,ranges,minVals

#==============================================================================
# 分類器針對約會網站的測試程式碼
def datingClassTest():
    hoRatio=0.10
    datingDataMat,datingLabels=file2matrix(r'E:\ML\ML_source_code\mlia\Ch02\datingTestSet2.txt')
    normMat,ranges,minVals=autoNorm(datingDataMat)
    m=normMat.shape[0]
    numTestVecs=int(m*hoRatio)
    errorCount=0.0
    for i in range(numTestVecs):
        classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],\
                                   datingLabels[numTestVecs:m],3)
        print "the classifier came back with:%d,the real anwer is:%d"\
                                   % (classifierResult,datingLabels[i])
        if (classifierResult!=datingLabels[i]):errorCount +=1.0
    print "the total error rate is: %f" % (errorCount/float(numTestVecs))

#==============================================================================
# 約會網站預測函式
def classifyPerson():
    resultList=['not at all','in small dose','in large doses']
    percentTats=float(raw_input("percentage of time spent playing video games?"))    
    ffMiles=float(raw_input("frequent flier miles earned per year?"))
    iceCream=float(raw_input("liters of ice cream consumed per year?"))
    datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
    normMat,ranges,minVals=autoNorm(datingDataMat)
    inArr=array([ffMiles,percentTats,iceCream])
    classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
    print "You will probably like this person:",resultList[classifierResult -1]

#==============================================================================
#準備資料:將影象轉換為測試向量
def img2vector(filename):
    #該函式建立1*1024的NumPy陣列
    returnVect = zeros((1,1024))
    fr = open(filename)
    #迴圈出文件的前32行,並將每行的頭32行儲存在NumPy陣列熵,最後返回陣列
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect

#==============================================================================
#手寫數字識別系統的測試程式碼   
def handwritingClassTest():
    hwLabels = []
    #獲取目錄內容
    trainingFileList = listdir(r'E:\ML\ML_source_code\mlia\Ch02\digits\trainingDigits')
    #trainingFileList下面有1934個檔案
    m = len(trainingFileList)
    #形成了一個1934*1024的0矩陣
    trainingMat = zeros((m,1024))
    #從檔名解分類數字
    for i in range(m):
        #構造要開啟的檔名
        fileNameStr = trainingFileList[i]
        #按照"."分開取第一個數
        fileStr = fileNameStr.split('.')[0]
        #按照"_"來分開來取第一數值並強制轉換為int型別
        classNumstr = int(fileStr.split('_')[0])
        hwLabels.append(classNumstr)
        trainingMat[i,:] = img2vector(r'E:\ML\ML_source_code\mlia\Ch02\digits\trainingDigits/%s' %fileNameStr)
    testFileList = listdir(r'E:\ML\ML_source_code\mlia\Ch02\digits\testDigits')
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumstr = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector(r'E:\ML\ML_source_code\mlia\Ch02\digits\trainingDigits/%s' %fileNameStr)
        classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3)
        print "the classifier came back with: %d, the read answer is:%d" %(classifierResult,classNumstr) 
        #計算錯誤率
        if (classifierResult !=classNumstr):
            errorCount += 1.0
    print "\nthe total number of errors is %d" % errorCount
    print "\nthe total error rate is: %f" % (errorCount/float(mTest))

本文參考:

《機器學習實戰》,作者: Peter Harrington ,出版社: 人民郵電出版社
原作名: Machine Learning in Action,譯者: 曲亞東 / 李銳 / 王斌 / 李鵬

《機器學習實戰》實踐心得 kNN篇

機器學習演算法( 二、K - 推酷)