您好,登錄后才能下訂單哦!
本文實例講述了Python實現基于KNN算法的筆跡識別功能。分享給大家供大家參考,具體如下:
需要用到:
數據說明:
數據共有785列,第一列為label,剩下的784列數據存儲的是灰度圖像(0~255)的像素值 28*28=784
KNN(K近鄰算法):
從訓練集中找到和新數據最接近的K條記錄,根據他們的主要分類來決定新數據的類型。
這里的主要分類,可以有不同的判別依據,比如“最多”,“最近鄰”,或者是“距離加權”。
整個程序的幾個部分:
1.數據的歸一化處理(normalization)
2.(重要)找出與test數據最接近的train數據的編號,根據編號查找到對應的label,將label賦給test數據的預測值
3.統計知道的test的label值與test的預測label值得正確率
Step 1
導入Numpy與Pandas庫
import numpy as np import pandas as pd
Step 2
對數據進行歸一化
對數據歸一化的方法很多,比如:
一、max-Min標準化
max - Min標準化方法是對原始數據進行線性變換。設minA和maxA分別為屬性A的最小值和最大值,將A的一個原始值x通過max-Min標準化映射成在區間[0,1]中的值x',其公式為:
新數據=(原數據-極小值)/(極大值-極小值)
二、
新數據=原數據/(原數據的平方和開根號)
三、
y = ( x - min )/( max - min ) 其中min為x的最小值,max為x的最大值,輸入向量為x,歸一化后的輸出向量為y 。上式將數據歸一化到 [ 0 , 1 ]區間,當激活函數采用S形函數時(值域為(0,1))時這條式子適用
在這里采用方法二
def normalize(x): norms = np.apply_along_axis(np.linalg.norm,1,x) return x / np.expand_dims(norms,-1)
調用np中的linalg.norm(x)
和 apply_along_axis(func, axis, x)
函數
linalg.norm(x)
函數的作用是 return sum(abs(xi)**2)**0.5
,
apply_along_axis(func, axis, x)
函數的作用是將x按axis方向執行func函數,axis=0表示做列方向上的運算,axis=1表示做行方向上的運算
step 3
找出與test數據最接近的train數據,這步是最關鍵的一步。
在這里,test數據與train數據就是空間的兩個向量,問題就變成了如何計算這兩個向量的相似程度。
我們可以把它們想象成空間中的兩條線段,都是從原點([0, 0, ...])出發,指向不同的方向。兩條線段之間形成一個夾角,如果夾角為0度,意味著方向相同、線段重合;如果夾角為90度,意味著形成直角,方向完全不相似;如果夾角為180度,意味著方向正好相反。因此,我們可以通過夾角的大小,來判斷向量的相似程度。夾角越小,就代表越相似。
假定a向量是[x1, y1],b向量是[x2, y2],那么可以將余弦定理改寫成下面的形式:
余玄定理
def nearest_neighbor(norm_func,train_data,train_label,test_data): train_data = norm_func(train_data) test_data = norm_func(test_data) cos = np.dot(train_data,np.transpose(test_data))#np.transpose為求轉置,dot為矩陣的乘積,結果為cos的一列值為test與train的相似度 max_cos = np.argmax(cos,axis=0)#np.argmax為cos中一列上方的最大值 test_pred = train_label[max_cos]#train_label為一列,max_cos為一個數組,train_label[max_cos]會讀出train_label中max_cos數組編號的元素 return test_pred#返回test的預測值
step 4
統計預測值的正確率
def validate(test_pred,test_label): c=len(test_pred)#在數組里面套數組的時候,len得到的是大數組里數組的個數,在只有一層數組的時候,得到的是數組中元素的個數 correct=(test_pred == test_label).sum()#統計兩個數組中有多少個元素相同 return float(correct)/c#必須轉變成浮點數再做除法,之前使用correct/c得到0
測試代碼:
if __name__ == '__main__': train_num = 200 test_num = 300#測試數據起始是test_num-train_num x = pd.read_csv('train.csv') x_train = x.values[0:train_num,1:]#讀取pandas中讀取出來的數據,需要用data.values[] x_train_label = x.values[0:train_num,0]#第一列是label,每幅圖的數據是一行 x_test = x.values[train_num:test_num,1:] x_test_label = x.values[train_num:test_num,0] test_pred=nearest_neighbor(normalize,x_train,x_train_label,x_test) prec=validate(test_pred,x_test_label) print u"正確率為%.2f"%(prec)#浮點數是%f
完整代碼點擊此處本站下載。
注解:
上面部分主要是講解KNN算法,運用到的是現成的28*28的數據,而在實際做筆跡分析的時候,首先需要將圖像轉化成矩陣數據。
現在介紹一下,圖像轉化成矩陣與矩陣轉化成圖像的方法
矩陣轉化成圖像
需要用到的庫是圖像處理庫Python Imaging Library (PIL)
在Windows下使用pip install PIL安裝失敗,采取了下載PIL.exe雙擊安裝的方法
下載地址:
PIL官方下載地址
import pandas as pd import numpy as np from PIL import Image # load data train = pd.read_csv('train.csv') # now draw the numbers for ind, row in train.iloc[0:3].iterrows():#iloc方法(介紹見后)來獲得前3行數據 i = row[0]#[0]為標簽項 arr = np.array(row[1:], dtype=np.uint8)#1-784列組成一幅圖,,uint8為8位無符號整數 #arr = np.array(255 - row[1:], dtype=np.uint8)#如果需要顏色取反,用255減去當前每個像素點的值 arr.resize((28, 28))#把它變成28*28的矩陣 #save to file im = Image.fromarray(arr) im.save("./train_pics/%s-%s.png" % (ind, i))#第一個%s(ind)表示它是第幾幅圖像,第二個%s表示這個圖像里面數字是幾 ,注意該語句不能產生文件夾,需要現在指定目錄建一個文件夾
.iloc()方法
iloc[行位置,列位置] df.iloc[1,1]#選取第二行,第二列的值,返回的為單個值 df.iloc[0,2],:]#選取第一行及第三行的數據
圖像轉化成矩陣
需要用到的庫是opencv(open source computer vision)
,下載安裝方式請參照附錄:python_OpenCV安裝
這里主要講它的幾個簡單功能
1.靜態圖像的輸入,輸出
cv2.imread('xxx.png')#輸入,#這里輸入image的維度image.shape = (w,h,3),w*h是圖片的長寬,3是BGR等三種顏色的channel值,每個值為0~255 cv2.imwrite('xxx.jpg', image)#輸出
2.將圖片轉化為灰度圖片
#灰度圖片的顏色channel只有一個,0~255表示灰度值 grayImage = cv2.imread('xxx.png',cv2.CV_LOAD_IMAGE_GRAYSCALE)
3.改變圖像的大小
print grayImage.shape#查看圖像的shape,shape為(137,301),如果查看的是圖像的size,則為42137(41237=137*301) res=cv2.resize(grayImage,(28,28),interpolation=cv2.INTER_CUBIC)#將圖片grayImage以cv2.INTER_CUBIC方式變化為(28,28)大小的圖片
變換的方法:
下面是有關輸入,輸出,改變成灰度圖,改變圖像大小,顯示的完整程序,注意圖像在窗口中的顯示
import cv2 image = cv2.imread('111.png')#讀 cv2.imwrite('111.jpg', image)#寫 grayImage = cv2.imread('111.png',cv2.CV_LOAD_IMAGE_GRAYSCALE) print grayImage.shape res=cv2.resize(grayImage,(28,28),interpolation=cv2.INTER_CUBIC) #顯示圖像 cv2.imshow('test',grayImage)#顯示灰度圖 cv2.imshow('change',res)#顯示改變了大小的圖 #捕獲鍵盤輸入 k=cv2.waitKey(0) if k==27:#27表示ESC鍵 cv2.destroyWindow()
cv2.imshow()
用于將圖片顯示在窗口中,后面必須跟個cv2.waitKey()
函數,才能讓顯示持續,不然顯示出來程序就中止了,窗口就會被關閉。cv2.waitKey()
函數是捕獲鍵盤的輸入,cv2.destroyWindow()
是釋放窗口。
在學習了如果讀取,輸出圖片后,我們就可以用寫好的KNN算法識別我們的筆跡了。
問題:
我使用了很多手寫的數據去驗證識別是否準確,發現準確率還不夠高。主要存在的問題是
1.圖片大小問題,大小的調節不應該把整張圖片變為28*28的圖,而應該識別出寫有數字的中心圖片,把旁邊的白邊去掉
2.手寫的數字照片,不能保證寫字的地方為黑(像素值為255)
解決方式:需要使用一個濾波器,把因紙張,拍攝問題出現的像素值降。再沒有使用濾波器的條件下,我把照片換成了在畫圖板上寫的數字。
3.寫字的粗細會影響判斷
解決辦法:這個可能是訓練樣本不夠多,整體訓練樣本的字跡偏粗,在輸入很細的筆跡時,不能識別出來。還有就是應該監測輸入字體的粗細,對輸入的很細的筆跡做膨脹處理,對很粗的筆跡做腐蝕處理
附:python_OpenCV安裝
看到網上好多教程的是在VS環境下OpenCV的安裝,而我一直都是在windows7,32位,sublime+cmd環境下,進行python的編程,所以琢磨了下這種條件下的OpenCV安裝
使用pip install numpy語句安裝numpy
(如果出現錯誤:Microsoft Visual C++ 9.0 is required <unable to find vcvarsall.bat>,使用管理員身份安裝 Microsoft Visual C++ 9.0,重新啟動計算機,再使用使用pip install numpy語句安裝numpy
opencv2.4.10下載
下載之后解壓(隨便解壓到哪里),將解壓目錄opencv文件夾中,build->python->2.7->x86下的文件cv2.pyd 復制到python2.7\Lib\site-packages 中
測試是否安裝成功,執行解壓目錄下的sources\samples\python\drawing.py或者進入python環境,使用
import cv2
更多關于Python相關內容感興趣的讀者可查看本站專題:《Python數學運算技巧總結》、《Python數據結構與算法教程》、《Python函數使用技巧總結》、《Python字符串操作技巧匯總》、《Python入門與進階經典教程》及《Python文件與目錄操作技巧匯總》
希望本文所述對大家Python程序設計有所幫助。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。