【機器學習演算法實現】kNN演算法 手寫識別——基於Python和NumPy函式庫
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
【機器學習演算法實現】系列文章將記錄個人閱讀機器學習論文、書籍過程中所碰到的演算法,每篇文章描述一個具體的演算法、演算法的程式設計實現、演算法的具體應用例項。爭取每個演算法都用多種語言程式設計實現。所有程式碼共享至github:
(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編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:
- 全新的介面設計 ,將會帶來全新的寫作體驗;
- 在創作中心設定你喜愛的程式碼高亮樣式,Markdown 將程式碼片顯示選擇的高亮樣式 進行展示;
- 增加了 圖片拖拽 功能,你可以將本地的圖片直接拖拽到編輯區域直接展示;
- 全新的 KaTeX數學公式 語法;
- 增加了支援甘特圖的mermaid語法1 功能;
- 增加了 多螢幕編輯 Markdown文章功能;
- 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位於編輯區域與預覽區域中間;
- 增加了 檢查列表 功能。
功能快捷鍵
撤銷: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.
圖片:
帶尺寸的圖片:
當然,我們為了讓使用者更加便捷,我們增加了圖片拖拽功能。
如何插入一段漂亮的程式碼片
去部落格設定頁面,選擇一款你喜歡的程式碼片高亮樣式,下面展示同樣高亮的 程式碼片
.
// An highlighted block var foo = 'bar';
生成一個適合你的列表
- 專案
- 專案
- 專案
- 專案
- 專案1
- 專案2
- 專案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公式展示 是通過尤拉積分
你可以找到更多關於的資訊 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檔案,在上方工具欄可以選擇匯入功能進行對應副檔名的檔案匯入,
繼續你的創作。
註腳的解釋 ↩︎