讀古今文學網 > 機器學習實戰 > 2.3 示例:手寫識別系統 >

2.3 示例:手寫識別系統

本節我們一步步地構造使用k近鄰分類器的手寫識別系統。為了簡單起見,這裡構造的系統只能識別數字0到9,參見圖2-6。需要識別的數字已經使用圖形處理軟件,處理成具有相同的色彩和大小 1:寬高是32像素x32像素的黑白圖像。儘管採用文本格式存儲圖像不能有效地利用內存空間,但是為了方便理解,我們還是將圖像轉換為文本格式。

示例:使用k-近鄰算法的手寫識別系統

  1. 收集數據:提供文本文件。
  2. 準備數據:編寫函數classify0,將圖像格式轉換為分類器使用的list格式。
  3. 分析數據:在Python命令提示符中檢查數據,確保它符合要求。
  4. 訓練算法:此步驟不適用於k近鄰算法。
  5. 測試算法:編寫函數使用提供的部分數據集作為測試樣本,測試樣本與非測試樣本的區別在於測試樣本是已經完成分類的數據,如果預測分類與實際類別不同,則標記為一個錯誤。
  6. 使用算法:本例沒有完成此步驟,若你感興趣可以構建完整的應用程序,從圖像中提取數字,並完成數字識別,美國的郵件分揀系統就是一個實際運行的類似系統。

1.該數據集合修改自\"手寫數字數據集的光學識別\"一文中的數據集合,該文登載於2010年10月3日的UCI機器學習資料庫中http://archive.ics.uci.edu/ml。作者是土耳其伊斯坦布爾海峽大學計算機工程系的E. Alpaydin與C. Kaynak。

2.3.1 準備數據:將圖像轉換為測試向量

實際圖像存儲在第2章源代碼的兩個子目錄內:目錄trainingDigits中包含了大約2000個例子,每個例子的內容如圖2-6所示,每個數字大約有200個樣本;目錄testDigits中包含了大約900個測試數據。我們使用目錄trainingDigits中的數據訓練分類器,使用目錄testDigits中的數據測試分類器的效果。兩組數據沒有重疊,你可以檢查一下這些文件夾的文件是否符合要求。

圖2-6 手寫數字數據集的例子

為了使用前面兩個例子的分類器,我們必須將圖像格式化處理為一個向量。我們將把一個32x32的二進製圖像矩陣轉換為1x1024的向量,這樣前兩節使用的分類器就可以處理數字圖像信息了。

我們首先編寫一段函數img2vector,將圖像轉換為向量:該函數創建1x1024的NumPy數組,然後打開給定的文件,循環讀出文件的前32行,並將每行的頭32個字符值存儲在NumPy數組中,最後返回數組。

def img2vector(filename):
    returnVect = zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect
  

將上述代碼輸入到kNN.py文件中,在Python命令行中輸入下列命令測試img2vector函數,然後與文本編輯器打開的文件進行比較:

>>> testVector = kNN.img2vector(\'testDigits/0_13.txt\')
>>> testVector[0,0:31]
array([ 0.,    0.,    0.,    0.,    0., 0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
        0.,    1.,    1.,    1.,    1., 0.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
        0.,    0.,    0.,    0.,    0.])                            
>>> testVector[0,32:63]

array([ 0.,    0.,    0.,    0.,    0., 0.,    0.,    0.,    0.,    0.,    0.,    0.,    1.,
        1.,    1.,    1.,    1.,    1., 1.,    0.,    0.,    0.,    0.,    0.,    0.,    0.,
        0.,    0.,    0.,    0.,    0.])    
  

2.3.2 測試算法:使用k近鄰算法識別手寫數字

上節我們已經將數據處理成分類器可以識別的格式,本節我們將這些數據輸入到分類器,檢測分類器的執行效果。程序清單2-6所示的自包含函數handwritingClassTest是測試分類器的代碼,將其寫入kNN.py文件中。在寫入這些代碼之前,我們必須確保將from os import listdir寫入文件的起始部分,這段代碼的主要功能是從os模塊中導入函數listdir,它可以列出給定目錄的文件名。

程序清單2-6 手寫數字識別系統的測試代碼

def handwritingClassTest:
    hwLabels = 
    trainingFileList = listdir(\'trainingDigits\')           #❶ 獲取目錄內容
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))
    for i in range(m):
          #❷ (以下三行)從文件名解析分類數字
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split(\'.\')[0]               
        classNumStr = int(fileStr.split(\'_\')[0])
        hwLabels.append(classNumStr)
        trainingMat[i,:] = img2vector(\'trainingDigits/%s\' % fileNameStr)
    testFileList = listdir(\'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(\'testDigits/%s\' % fileNameStr)
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print \"the classifier came back with: %d, the real 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))
 

在程序清單2-6中,將trainingDigits目錄中的文件內容存儲在列表中❶,然後可以得到目錄中有多少文件,並將其存儲在變量m中。接著,代碼創建一個m行1024列的訓練矩陣,該矩陣的每行數據存儲一個圖像。我們可以從文件名中解析出分類數字❷。該目錄下的文件按照規則命名,如文件9_45.txt的分類是9,它是數字9的第45個實例。然後我們可以將類代碼存儲在hwLabels向量中,使用前面討論的img2vector函數載入圖像。在下一步中,我們對testDigits目錄中的文件執行相似的操作,不同之處是我們並不將這個目錄下的文件載入矩陣中,而是使用classify0函數測試該目錄下的每個文件。由於文件中的值已經在0和1之間,本節並不需要使用2.2節的autoNorm函數。

在Python命令提示符中輸入kNN.handwritingClassTest,測試該函數的輸出結果。依賴於機器速度,加載數據集可能需要花費很長時間,然後函數開始依次測試每個文件,輸出結果如下所示:

>>> kNN.handwritingClassTest
the classifier came back with: 0, the real answer is: 0 
the classifier came back with: 0, the real answer is: 0
.
.
the classifier came back with: 7, the real answer is: 7 
the classifier came back with: 7, the real answer is: 7 
the classifier came back with: 8, the real answer is: 8 
the classifier came back with: 8, the real answer is: 8 
the classifier came back with: 8, the real answer is: 8 
the classifier came back with: 6, the real answer is: 8
.
.
the classifier came back with: 9, the real answer is: 9 
the total number of errors is: 11
the total error rate is: 0.011628
  

k近鄰算法識別手寫數字數據集,錯誤率為1.2%。改變變量k的值、修改函數handwritingClassTest隨機選取訓練樣本、改變訓練樣本的數目,都會對k近鄰算法的錯誤率產生影響,感興趣的話可以改變這些變量值,觀察錯誤率的變化。

實際使用這個算法時,算法的執行效率並不高。因為算法需要為每個測試向量做2000次距離計算,每個距離計算包括了1024個維度浮點運算,總計要執行900次,此外,我們還需要為測試向量準備2MB的存儲空間。是否存在一種算法減少存儲空間和計算時間的開銷呢?k決策樹就是k近鄰算法的優化版,可以節省大量的計算開銷。