1. 程式人生 > >【機器學習演算法實現】kNN演算法 手寫識別——基於Python和NumPy函式庫

【機器學習演算法實現】kNN演算法 手寫識別——基於Python和NumPy函式庫

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

【機器學習演算法實現】系列文章將記錄個人閱讀機器學習論文、書籍過程中所碰到的演算法,每篇文章描述一個具體的演算法、演算法的程式設計實現、演算法的具體應用例項。爭取每個演算法都用多種語言程式設計實現。所有程式碼共享至github:

https://github.com/wepe/MachineLearning-Demo     歡迎交流指正!


(1)kNN演算法_手寫識別例項——基於Python和NumPy函式庫

1、kNN演算法簡介

kNN演算法,即K最近鄰(k-NearestNeighbor)分類演算法,是最簡單的機器學習演算法之一,演算法思想很簡單:從訓練樣本集中選擇k個與測試樣本“距離”最近的樣本,這k個樣本中出現頻率最高的類別即作為測試樣本的類別。下面的簡介選自wiki百科:http://zh.wikipedia.org/wiki/%E6%9C%80%E8%BF%91%E9%84%B0%E5%B1%85%E6%B3%95

方法

  • 目標:分類未知類別案例。
  • 輸入:待分類未知類別案例專案。已知類別案例集合D ,其中包含 j個已知類別的案例。
  • 輸出:專案可能的類別。

步驟

如下圖
我們考慮樣本為二維的情況下,利用knn方法進行二分類的問題。圖中三角形和方形是已知類別的樣本點,這裡我們假設三角形為正類,方形為負類。圖中圓形點是未知類別的資料,我們要利用這些已知類別的樣本對它進行分類。

分類過程如下:


1 首先我們事先定下k值(就是指k近鄰方法的k的大小,代表對於一個待分類的資料點,我們要尋找幾個它的鄰居)。這邊為了說明問題,我們取兩個k值,分別為3和5;
2 根據事先確定的距離度量公式(如:歐氏距離),得出待分類資料點和所有已知類別的樣本點中,距離最近的k個樣本。
3 統計這k個樣本點中,各個類別的數量。如上圖,如果我們選定k值為3,則正類樣本(三角形)有2個,負類樣本(方形)有1個,那麼我們就把這個圓形資料點定為正類;而如果我們選擇k值為5,則正類樣本(三角形)有2個,負類樣本(方形)有3個,那麼我們這個資料點定為負類。即,根據k個樣本中,數量最多的樣本是什麼類別,我們就把這個資料點定為什麼類別。


補充:

優缺點:

(1)優點:

演算法簡單,易於實現,不需要引數估計,不需要事先訓練。

(2)缺點:

屬於懶惰演算法,“平時不好好學習,考試時才臨陣磨槍”,意思是kNN不用事先訓練,而是在輸入待分類樣本時才開始執行,這一特點導致kNN計算量特別大,而且訓練樣本必須儲存在本地,記憶體開銷也特別大。


K的取值:

引數k的取值一般通常不大於20。——《機器學習實戰》


2、手寫識別例項

kNN演算法主要被應用於文字分類、相似推薦。本文將描述一個分類的例子,是《機器學習實戰》一書中的例項,使用python語言以及數值計算庫NumPy。下面首先簡單介紹本例項程式設計開發過程中所用到的python、numpy中的函式。

2.1 python、numpy函式

NumPy庫總包含兩種基本的資料型別:矩陣和陣列,矩陣的使用類似Matlab,本例項用得多的是陣列array。


shape()

shape是numpy函式庫中的方法,用於檢視矩陣或者陣列的維素

>>>shape(array) 若矩陣有m行n列,則返回(m,n)

>>>array.shape[0] 返回矩陣的行數m,引數為1的話返回列數n

tile()

tile是numpy函式庫中的方法,用法如下:

>>>tile(A,(m,n))  將陣列A作為元素構造出m行n列的陣列

sum()

sum()是numpy函式庫中的方法

>>>array.sum(axis=1)按行累加,axis=0為按列累加

argsort()

argsort()是numpy中的方法,得到矩陣中每個元素的排序序號

>>>A=array.argsort()  A[0]表示排序後 排在第一個的那個數在原來陣列中的下標

dict.get(key,x)

python中字典的方法,get(key,x)從字典中獲取key對應的value,字典中沒有key的話返回0

sorted()

python中的方法

min()、max()

numpy中有min()、max()方法,用法如下

>>>array.min(0)  返回一個數組,陣列中每個數都是它所在列的所有數的最小值

>>>array.min(1)  返回一個數組,陣列中每個數都是它所在行的所有數的最小值

listdir('str')

python的operator中的方法

>>>strlist=listdir('str')  讀取目錄str下的所有檔名,返回一個字串列表

split()

python中的方法,切片函式

>>>string.split('str')以字元str為分隔符切片,返回list


關於更多的numpy中的函式,可以查閱官網:http://docs.scipy.org/doc/


2.2  程式設計實現“手寫識別”

手寫識別的概念:是指將在手寫裝置上書寫時產生的軌跡資訊轉化為具體字碼。 手寫識別系統是個很大的專案,識別漢字、英語、數字、其他字元。本文只是個小demo,重點不在手寫識別而在於理解kNN,因此只識別0~9單個數字。
輸入格式:每個手寫數字已經事先處理成32*32的二進位制文字,儲存為txt檔案。0~9每個數字都有10個訓練樣本,5個測試樣本。訓練樣本集如下圖:
開啟3_3.txt這個檔案看看:


上面的背景介紹完了,現在程式設計實現,大概分為 三個步驟 (1)將每個圖片(即txt文字,以下提到圖片都指txt文字)轉化為一個向量,即32*32的陣列轉化為1*1024的陣列,這個1*1024的陣列用機器學習的術語來說就是特徵向量。
(2)訓練樣本中有10*10個圖片,可以合併成一個100*1024的矩陣,每一行對應一個圖片。(這是為了方便計算,很多機器學習演算法在計算的時候採用矩陣運算,可以簡化程式碼,有時還可以減少計算複雜度)。
(3)測試樣本中有10*5個圖片,我們要讓程式自動判斷每個圖片所表示的數字。同樣的,對於測試圖片,將其轉化為1*1024的向量,然後計算它與訓練樣本中各個圖片的“距離”(這裡兩個向量的距離採用歐式距離),然後對距離排序,選出較小的前k個,因為這k個樣本來自訓練集,是已知其代表的數字的,所以被測試圖片所代表的數字就可以確定為這k箇中出現次數最多的那個數字。
第一步:轉化為1*1024的特徵向量。程式中的filename是檔名,比如3_3.txt
<span style="font-family:SimSun;font-size:18px;">#樣本是32*32的二值圖片,將其處理成1*1024的特徵向量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</span>


第二步、第三步:將訓練集圖片合併成100*1024的大矩陣,同時逐一對測試集中的樣本分類
<span style="font-family:SimSun;font-size:18px;">def handwritingClassTest():    #載入訓練集到大矩陣trainingMat    hwLabels = []    trainingFileList = listdir('trainingDigits')           #os模組中的listdir('str')可以讀取目錄str下的所有檔名,返回一個字串列表    m = len(trainingFileList)    trainingMat = zeros((m,1024))    for i in range(m):        fileNameStr = trainingFileList[i]                  #訓練樣本的命名格式:1_120.txt        fileStr = fileNameStr.split('.')[0]                #string.split('str')以字元str為分隔符切片,返回list,這裡去list[0],得到類似1_120這樣的        classNumStr = int(fileStr.split('_')[0])           #以_切片,得到1,即類別        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))</span>


這裡面的函式classify()為分類主體函式,計算歐式距離,並最終返回測試圖片類別:
<span style="font-family:SimSun;font-size:18px;">#分類主體程式,計算歐式距離,選擇距離最小的k個,返回k箇中出現頻率最高的類別#inX是所要測試的向量#dataSet是訓練樣本集,一行對應一個樣本。dataSet對應的標籤向量為labels#k是所選的最近鄰數目def classify0(inX, dataSet, labels, k):    dataSetSize = dataSet.shape[0]                       #shape[0]得出dataSet的行數,即樣本個數    diffMat = tile(inX, (dataSetSize,1)) - dataSet       #tile(A,(m,n))將陣列A作為元素構造m行n列的陣列    sqDiffMat = diffMat**2    sqDistances = sqDiffMat.sum(axis=1)                  #array.sum(axis=1)按行累加,axis=0為按列累加    distances = sqDistances**0.5    sortedDistIndicies = distances.argsort()             #array.argsort(),得到每個元素的排序序號    classCount={}                                        #sortedDistIndicies[0]表示排序後排在第一個的那個數在原來陣列中的下標    for i in range(k):        voteIlabel = labels[sortedDistIndicies[i]]        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #get(key,x)從字典中獲取key對應的value,沒有key的話返回0    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) #sorted()函式,按照第二個元素即value的次序逆向(reverse=True)排序    return sortedClassCount[0][0]</span>


3、工程檔案

整個工程檔案包括原始碼、訓練集、測試集,可到github獲取:github地址




進入use Python and NumPy目錄,開啟python開發環境,import kNN模組,呼叫手寫識別函式:



因為我用的訓練集和測試集都比較小,所以湊巧沒有識別錯誤的情況:






           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述 你好! 這是你第一次使用 **Markdown編輯器** 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章,瞭解一下Markdown的基本語法知識。

新的改變

我們對Markdown編輯器進行了一些功能拓展與語法支援,除了標準的Markdown編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:

  1. 全新的介面設計 ,將會帶來全新的寫作體驗;
  2. 在創作中心設定你喜愛的程式碼高亮樣式,Markdown 將程式碼片顯示選擇的高亮樣式 進行展示;
  3. 增加了 圖片拖拽 功能,你可以將本地的圖片直接拖拽到編輯區域直接展示;
  4. 全新的 KaTeX數學公式 語法;
  5. 增加了支援甘特圖的mermaid語法1 功能;
  6. 增加了 多螢幕編輯 Markdown文章功能;
  7. 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位於編輯區域與預覽區域中間;
  8. 增加了 檢查列表 功能。

功能快捷鍵

撤銷:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜體:Ctrl/Command + I
標題:Ctrl/Command + Shift + H
無序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
檢查列表:Ctrl/Command + Shift + C
插入程式碼:Ctrl/Command + Shift + K
插入連結:Ctrl/Command + Shift + L
插入圖片:Ctrl/Command + Shift + G

合理的建立標題,有助於目錄的生成

直接輸入1次#,並按下space後,將生成1級標題。
輸入2次#,並按下space後,將生成2級標題。
以此類推,我們支援6級標題。有助於使用TOC語法後生成一個完美的目錄。

如何改變文字的樣式

強調文字 強調文字

加粗文字 加粗文字

標記文字

刪除文字

引用文字

H2O is是液體。

210 運算結果是 1024.

插入連結與圖片

連結: link.

圖片: Alt

帶尺寸的圖片: Alt

當然,我們為了讓使用者更加便捷,我們增加了圖片拖拽功能。

如何插入一段漂亮的程式碼片

部落格設定頁面,選擇一款你喜歡的程式碼片高亮樣式,下面展示同樣高亮的 程式碼片.

// An highlighted block var foo = 'bar'; 

生成一個適合你的列表

  • 專案
    • 專案
      • 專案
  1. 專案1
  2. 專案2
  3. 專案3
  • 計劃任務
  • 完成任務

建立一個表格

一個簡單的表格是這麼建立的:

專案 Value
電腦 $1600
手機 $12
導管 $1

設定內容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列 第二列 第三列
第一列文字居中 第二列文字居右 第三列文字居左

SmartyPants

SmartyPants將ASCII標點字元轉換為“智慧”印刷標點HTML實體。例如:

TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash

建立一個自定義列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何建立一個註腳

一個具有註腳的文字。2

註釋也是必不可少的

Markdown將文字轉換為 HTML

KaTeX數學公式

您可以使用渲染LaTeX數學表示式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n 1 ) ! n N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N 是通過尤拉積分

Γ ( z ) = 0 t z 1 e t d t &ThinSpace; . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.

你可以找到更多關於的資訊 LaTeX 數學表示式here.

新的甘特圖功能,豐富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 現有任務
        已完成               :done,    des1, 2014-01-06,2014-01-08
        進行中               :active,  des2, 2014-01-09, 3d
        計劃一               :         des3, after des2, 5d
        計劃二               :         des4, after des3, 5d
  • 關於 甘特圖 語法,參考 這兒,

UML 圖表

可以使用UML圖表進行渲染。 Mermaid. 例如下面產生的一個序列圖::

這將產生一個流程圖。:

  • 關於 Mermaid 語法,參考 這兒,

FLowchart流程圖

我們依舊會支援flowchart的流程圖:

  • 關於 Flowchart流程圖 語法,參考 這兒.

匯出與匯入

匯出

如果你想嘗試使用此編輯器, 你可以在此篇文章任意編輯。當你完成了一篇文章的寫作, 在上方工具欄找到 文章匯出 ,生成一個.md檔案或者.html檔案進行本地儲存。

匯入

如果你想載入一篇你寫過的.md檔案或者.html檔案,在上方工具欄可以選擇匯入功能進行對應副檔名的檔案匯入,
繼續你的創作。


  1. mermaid語法說明 ↩︎

  2. 註腳的解釋 ↩︎