python实现KNN,并用KNN实现手写数字识别
from numpy import*
import operator
from PIL import Image
from os import listdir
def knn (k,textdata,traindata,labels):
traindatasize = traindata.shape[0] #shapr[0]为矩阵的行数,shape[1]为矩阵的列数
sub_result = tile(textdata,(traindatasize,1))-traindata
#将测试集的格式扩展到与训练集的格式一致,然后在与训练集做差,在后面求欧氏距离时有用
#tile函数是一个扩展函数,第一个参数是要扩展的矩阵,第二个参数
# 可以是一个常数,表示在扩展列数的倍数,若为一个元组,
# 则元组第一个参数表示扩展行数的倍数,第个二参数表示扩展列数的倍数
sqrt_sub_result = sub_result**2 #对差直求方
sum_sqrt_sub_result = sqrt_sub_result.sum(axis = 1) #得到了测试集到各个训练集的距离的平方
#参数axis是用来指定求和的位置,=0时表示每一列上的各行求和(矩阵原本有多少列结果就多少列)
#=1时相反,是每一行上的各列求和(原本有多少行结果就有多少行)
distance = sum_sqrt_sub_result**0.5 #开方求出距离
distance_for_sort=distance.argsort() #对求出的距离进行排序,argsort的排序结果是输出元素的下标,默认为升序
count = {} #创建一个空字典,用于存放结果
for i in range(0,k): #找出排序后前k个结果中,某一标签最多的类别
vote = labels[distance_for_sort[i]] #当前第i个结果的类别
count[vote] = count.get(vote,0)+1 #把当前类别与类别出现的次数存到字典count中去
sort_count = sorted(count.items(),key = operator.itemgetter(1),reverse=True)
#第一个参数是排序的对象,第二个参数表示对谁排序,operator.itemgetter=0
#表示对类别来排序,operator.itemgetter=1,表示对类别出现的次数来排序
#reverse=True表示按降序排
#此时得到的count中第一个即为最可能的类别
return sort_count[0][0]
#将图片转化为文本的形式
def image_change_to_txt():
im = Image.open("E:/杂物/壁纸1") #用Image.open函数打开一张图片
fh = open("E:/杂物/壁纸","a") #创建一个文件的句柄
#im.save("E:/杂物/壁纸") #用来保存图片的函数
#k = im.getpixel((1,9)) #用来取出某一点的像素点
width = im.size(0) #size(0)表示图片的宽
height = im.size(1) #size(1)表示图片的高
for i in range(0,width):
for j in range(0,height):
p_pix = im.getpixel((i,j))
p_pix_sum = p_pix[0]+p_pix[1]+p_pix[2]
if(p_pix_sum==0):
fh.write("1")
else:
fh.write("0")
fh.close()
#加载数据(把每张图片的文本转到一维数组中保存)
def data_to_array(fname):
arr = [] #把数据加载 到一维数组arr里
fh = open(fname) #打开加载数据的文件夹
for i in range(0,32):
thisline = fh.readline() #每次读取一行
for j in range(0,32): #读取每一行的像素点的值,并存到arr数组中
arr.append(int(thisline[j]))
return arr
#arr1 = data_to_array("E:/py_application/shu_ju_fen_xi_yu_wa_jue/testdata//0_0.txt")
#print(arr1)
#建立一个取前缀的函数,因为前缀即为标签
def sep_label(fname):
filestr = fname.split(".")[0]
#将后面的.txt去掉,保留前面的数据,因此为[0]
lable = int(filestr.split("_")[0])
#同理,保留前面的数据,因此为[0]
return lable
#建立训练数据
def traindata ():
labels = []
train_file = listdir("E:/py_application/shu_ju_fen_xi_yu_wa_jue/testdata")
#listdir能够加载一个目录下的所有文件的名称
num = len(train_file) #求出有多少个文件
#生成一个二维数组,每一行存储一个arr,即每行存储一个训练数据
train_arr = zeros((num,1024)) #numpy中的方法,用与生成一个num行1024列的全0的二维数组
for i in range(0,num): #有多少个txt就循环多少次
this_fname = train_file[i] #取当前第i个元素的文件名
thislable = sep_label(this_fname) #从当前的文件名中读取出标签lable
labels.append(thislable)
train_arr[i,:] = data_to_array("traindata/"+this_fname) #把得到的一维数组arr赋到train__arr中
return train_arr,labels #返回训练集与标签
def test_knn ():
train_arr, labels = traindata() #获取训练集与标签
test_list = listdir("testdata") #获取测试集目录下的文件名
test_num = len(test_list) #获取文件名总数
for i in range(0,test_num): #测试每个测试数据
this_test_file = test_list[i] #取第i个元素的文件名
test_arr = data_to_array("testdata/"+this_test_file) #将第i个文本数据转成一维数组
result_knn = knn(3,test_arr,train_arr,labels) #测试第i个数据
print(result_knn) #打印测试结果
test_knn()