1. 程式人生 > >python實現kNN演算法識別手寫體數字

python實現kNN演算法識別手寫體數字

1。總體概要

kNN演算法已經在上一篇部落格中說明。對於要處理手寫體數字,需要處理的點主要包括:
(1)圖片的預處理:將png,jpg等格式的圖片轉換成文字資料,本部落格的思想是,利用圖片的rgb16進位制編碼(255,255,255)為白色,(0,0,0)為黑色,獲取圖片大小後,逐個畫素進行判斷分析,當此畫素為空白時,在文字資料中使用0來替換,反之使用1來替換。

from PIL import Image
'''將圖片轉換成文件,使用0,1分別替代空白和數字'''
pic = Image.open('/Users/wangxingfan/Desktop/1.png')
path = open('/Users/wangxingfan/Desktop/1.txt'
,'a') width = pic.size[0] height = pic.size[1] for i in range(0,width): for j in range(0,height): c_RGB = pic.getpixel((i,j))#獲取該畫素所對應的RGB值 if c_RGB[0]+c_RGB[1]+c_RGB[2]>0:#白色 path.write('0') elif c_RGB[0]+c_RGB[1]+c_RGB[2]==0:#黑色 path.write('1') else
: pass path.write('\n') path.close()

(2)訓練集的構建。首先想到的是將(1)中圖片處理後的文字資料構建成list形式,所以訓練集將是二維陣列,形如[[1,0,1,1,0,,,,,0,1],[0,1,1,1,10,,,,],[0,0,1,0,,,],,,,,]所以我們構建函式處理訓練集資料。

2。程式碼

簡單的總結這個演算法,就是將測試資料向量化,逐個和同樣向量化的訓練資料進行kNN運算,求的最短距離出現最多的分類就是我們要的分類。建立訓練集的過程就是將檔案資料向量化的過程。

#!/user/bin/env python
#-*- coding:utf-8 -*-
from os import listdir#獲取檔案目錄下所有檔案 ''' from PIL import Image #將圖片轉換成文件,使用0,1分別替代空白和數字 pic = Image.open('/Users/wangxingfan/Desktop/1.png') path = open('/Users/wangxingfan/Desktop/1.txt','a') width = pic.size[0] height = pic.size[1] for i in range(0,width): for j in range(0,height): c_RGB = pic.getpixel((i,j))#獲取該畫素所對應的RGB值 if c_RGB[0]+c_RGB[1]+c_RGB[2]>0:#白色 path.write('0') elif c_RGB[0]+c_RGB[1]+c_RGB[2]==0:#黑色 path.write('1') else: pass path.write('\n') path.close() ''' import numpy as np import operator as opt def kNN(dataSet, labels, testData, k): '''首先明確列表不能想加減,dataSet是陣列形式,而對於下面的test函式,testData只是一列,相當於列表,所以在進行加減時,需要將其轉換為陣列,我們使用np下的tile函式來實現''' testDatasize = dataSet.shape[0]#獲取dataSet的總行數 dataSet = dataSet.astype('float64')#不進行轉換則報錯 testData1 = np.tile(testData,(testDatasize,1))#使用tile函式返回多個重複構成的陣列 testData1 = testData1.astype('float64') distSquareMat = (dataSet - testData1) ** 2 # 計算差值的平方 distSquareSums = distSquareMat.sum(axis=1) # 求每一行的差值平方和,axis=0則按列計算 distances = distSquareSums ** 0.5 # 開根號,得出每個樣本到測試點的距離 sortedIndices = distances.argsort() # 排序,得到排序後的下標 indices = sortedIndices[:k] # 取最小的k個 labelCount = {} # 儲存每個label的出現次數,出現次數最多的就是我們要選擇的類別 for i in indices: label = labels[i] labelCount[label] = labelCount.get(label, 0) + 1 # 次數加一,使用字典的get方法,第一次出現時預設值是0 sortedCount = sorted(labelCount.items(), key=opt.itemgetter(1), reverse=True) # 對label出現的次數從大到小進行排序 return sortedCount[0][0] # 返回出現次數最大的label #定義函式讀取某個檔案,返回該檔案組成的陣列 def file_data(fname): arr = [] path = open(fname) for i in range(0,32): line = path.readline() for j in range(0,32): arr.append(line[j]) return arr #建立訓練資料集 def train_data(): lables = [] file_list = listdir('/學習/視訊課程/原始碼/第7周/testandtraindata/traindata/') trainarr = np.zeros((len(file_list),1024)) for i in range(0,len(file_list)): file = '/學習/視訊課程/原始碼/第7周/testandtraindata/traindata/'+file_list[i] lables.append(file_list[i].split('_')[0])#獲取對應的檔案類別 trainarr[i,:] = file_data(file)#取所有列的第一個資料 return trainarr,lables #測試函式 def test(): j = 0 k = 0 trainarr,lables = train_data() testdata_list = listdir('/學習/視訊課程/原始碼/第7周/testandtraindata/testdata/') for i in range(0,len(testdata_list)):#逐個去測試 testfile = '/學習/視訊課程/原始碼/第7周/testandtraindata/testdata/'+testdata_list[i] testdata1 = file_data(testfile) result = kNN(trainarr,lables,testdata1,k=3) print(result+',real_number:'+testdata_list[i].split('_')[0]) if result == testdata_list[i].split('_')[0]: j +=1 else: k +=1 print('辨識成功率:'+j/(k+j)) test()

輸出結果為:

這裡寫圖片描述

3。幾個知識點程式碼說明

(1)numpy.tile

p = np.array([0,0,0])
np.tile(p,(3,1))#表示columns方向重複三次,index方向不變
Out[12]: 
array([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])
np.tile(p,(1,3))#表示index方向重複三次,行還是一行
Out[13]: array([[0, 0, 0, 0, 0, 0, 0, 0, 0]])

(2)array[1,:]表示取所有列的第【索引1】個數據(也就是第二行資料)

a = np.array([[1,1,1],[2,2,2],[3,3,3],[4,4,4]])
a[1,:]
Out[21]: array([2, 2, 2])
a[:,1]#所有行的第二列資料
Out[22]: array([1, 2, 3, 4])

(3)list並不能進行加減計算,需要使用numpy將資料轉換為陣列形式,且在使用例如:arr1+arr2時,需要兩個陣列的維度相同,在某個緯度上的資料長度也相同。
(4)使用os模組下的listdir,可以顯示所有該資料夾下的檔案,以列表的形式返回。