专栏名称: 大数据实验室
宽客俱乐部旗下美国大数据实验室,大数据研究应用。
目录
相关文章推荐
51好读  ›  专栏  ›  大数据实验室

监督学习:KNN(K-近邻)算法实现手写数字识别的三种方法

大数据实验室  · 公众号  · 大数据  · 2018-05-13 08:30

正文

请到「今天看啥」查看全文


献丑再贴出自己的代码:
说明如下:大的优化没有,由于我有自己习惯的名称定义,修改了源代码中大部分的名称,小的修改也未注明

注意:代码中需要的训练集、测试集数据均来源于上链接里,为了让大家更清楚下面代码的原理,强烈建议看完上面前辈的作品。

(网络直播课程)房博士手把手教你做程序化交易


本课程赠送适用于二十多个期货品种的两套模型组合,总价值九千元。

开课时间:5月13日——5月17日晚上7:00——9:00

报名电话/微信:18516600808




# -*- coding:utf-8 -*-

# -*- author:zzZ_CMing

# -*- 2017/12/28

# -*- python3.5

from os import listdir

from numpy import *

import numpy as np

import operator

import datetime

def KNN (test_data,train_data,train_label,k) :

#已知分类的数据集(训练集)的行数

dataSetSize = train_data.shape[ 0 ]

#求所有距离:先tile函数将输入点拓展成与训练集相同维数的矩阵,计算测试样本与每一个训练样本的距离

all_distances = np.sqrt(np.sum(np.square(tile(test_data,(dataSetSize, 1 ))-train_data),axis= 1 ))

#print("所有距离:",all_distances)

#按all_distances中元素进行升序排序后得到其对应索引的列表

sort_distance_index = all_distances.argsort()

#print("文件索引排序:",sort_distance_index)

#选择距离最小的k个点

classCount = {}

for i in range(k):

#返回最小距离的训练集的索引(预测值)

voteIlabel = train_label[sort_distance_index[i]]

#print('第',i+1,'次预测值',voteIlabel)

classCount[voteIlabel] = classCount.get(voteIlabel, 0 )+ 1

#求众数:按classCount字典的第2个元素(即类别出现的次数)从大到小排序

sortedClassCount = sorted(classCount.items(), key = operator.itemgetter( 1 ), reverse = True )

return sortedClassCount[ 0 ][ 0 ]

#文本向量化 32x32 -> 1x1024

def img2vector (filename) :

returnVect = []

fr = open(filename)

for i in range( 32 ):

lineStr = fr.readline()

for j in range( 32 ):

returnVect.append(int(lineStr[j]))

return returnVect

#从文件名中解析分类数字

def classnumCut (fileName) :

#参考文件名格式为:0_3.txt

fileStr = fileName.split( '.' )[ 0 ]

classNumStr = int(fileStr.split( '_' )[ 0 ])

return classNumStr

#构建训练集数据向量,及对应分类标签向量

def trainingDataSet () :

train_label = []

trainingFileList = listdir( 'trainingDigits' )

m = len(trainingFileList)

train_data = zeros((m, 1024 ))

#获取训练集的标签

for i in range(m):

# fileNameStr:所有训练集文件名

fileNameStr = trainingFileList[i]

# 得到训练集索引

train_label.append(classnumCut(fileNameStr))

train_data[i,:] = img2vector( 'trainingDigits/%s' % fileNameStr)

return train_label,train_data

#测试函数

def main () :

t1 = datetime.datetime.now() # 计时开始

Nearest_Neighbor_number = int(input( '选取最邻近的K个值,K=' ))

train_label,train_data = trainingDataSet()

testFileList = listdir( 'testDigits' )

error_sum = 0

test_number = len(testFileList)

for i in range(test_number):

#测试集文件名

fileNameStr = testFileList[i]

#切片后得到测试集索引

classNumStr = classnumCut(fileNameStr)

test_data = img2vector( 'testDigits/%s' % fileNameStr)

#调用knn算法进行测试

classifierResult = KNN(test_data, train_data, train_label, Nearest_Neighbor_number)

print ( "第" ,i+ 1 , "组:" , "预测值:" ,classifierResult, "真实值:" ,classNumStr)

if (classifierResult != classNumStr):

error_sum += 1.0

print ( "\n测试集总数为:" ,test_number)

print ( "测试出错总数:" ,error_sum)

print ( "\n错误率:" ,error_sum/float(test_number)* 100 , '%' )

t2 = datetime.datetime.now()

print( '耗 时 = ' , t2 - t1)

if __name__ == "__main__" :

main()

评价:这位前辈一所使用的训练集、测试集数据虽然是0-1矩阵,但是是可以通过代码生成打印出来。除此之外,前辈一代码的识别错误率比较低,测试时候946个测试数据只出错11个,出错率是1.16%,也就是成功率达98.84%。有图有真相:

大神一的方法也让我检验了最合适的K值选定是3 (大家自己动手试试,选取不同的K值,就能得到不同的错误率)

于是用大神一的方法,再结合米度教育Pierre老师的代码,写了第一份自己的手写数字识别的代码(这里就没有贴出了)。问题总是在实际实现的过程中被发现——每个人用画板写出来的数字各不相同,形状有大有小,笔画有粗有细,就连同一个数字的结构比例都千差万别。这对识别的成功率影响很大。恰当这时,无意中发现了大神二的方法——统一不同人写出来的数字,也就是 添加图片预处理,加入切割、拉伸函数。

第三个:图片预处理——切割、拉伸函数

大神二的原贴链接地址附上: http://m.blog.csdn.net/Hanpu_Liang/article/details/78237913
大神二的思路如下:

  1. 将读取的图片先转换成0-1矩阵形式

  2. 再根据灰度阈值,计算有效图片的边界索引,切割返回有效图片的索引尺寸

  3. 切割后的有效图片尺寸各不相同,运用拉伸函数将各不相同的有效图片转换成尺寸相同的有效图片







请到「今天看啥」查看全文