手写识别系统
我们一步一步地构造使用k-近邻分类器的手写识别系统。为了简单起见,这里对数据有以下要求
- 数字只能识别0到9
- 需要识别的数字已经使用图形处理软件处理过,处理成具有相同的色彩和大小,宽和高是32像素×32像素的黑白图像
- 将图像转换成文本进行处理
机器学习的六大步骤
- 收集数据:
- 准备数据:编程将图像格式转化为分类器使用的向量格式
- 分析数据:
- 训练算法:(此步骤不适用于k-近邻算法)
- 测试算法:编程将提供的部分数据集作为测试样本,测试样本与非测试样本的区别就在于测试样本是已经完成分类的数据。
- 使用算法:
准备数据:将图像转化为测试向量
- 在文件trainingDigits中包含了大约2000个例子,每个数字大概有200个样本
- 在文件testDigits中包含了大约900个测试用例。
然后我们使用trainingDigits中的数据进行训练,使用testDigits中的数据进行测试。
我们将继续使用前面约会网站所使用的分类器,所以我们要将图像格式处理为一个向量,将32×32的矩阵转化为1×1024的向量。
# 将32*32的数据转化为1*1024的数据
def imgToVector(filaname):
returnVector = zeros((1,1024))
fr = open(filaname)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVector[32*i+j] = int(lineStr[j]) # 保留疑问
return returnVector
测试算法:使用k-近邻算法识别手写数字
def handWritingClassTest():
hwLables = []
trainingFileList = listdir('trainingDigits') # os模块下的listdir可以取出该目录下的所有文件名,返回一个list
m = len(trainingFileList) # 该目录下的文件的数量
trainingMat = zeros((m, 1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0] # 去掉后缀
classNumStr = int(fileStr.split('_')[0]) # 提取标签
hwLables.append(classNumStr)
trainingMat[i, :] = imgToVector('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 = imgToVector('testDigits/%s' % fileNameStr)
classFierResult = classify(vectorUnderTest, trainingMat, hwLables, 3)
print("分类器返回的结果为:%d, 实际情况为:%d" % (classFierResult, classNumStr))
if (classFierResult != classNumStr): errorCount += 1.0
print("错误率为:%f" % (errorCount / float(mTest)))
完整代码和数据见:https://github.com/IBITM/Machine-Learning-in-Action/tree/master/kNN
kNN算法小结
- k-近邻算法是分类数据最简单最有效的算法,它是基于实例的学习,当我们使用算法时必须有接近实际数据的训练样本数据。
- k-近邻算法必须保存全部的数据集,如果训练的数据集很大,我们必须耗费大量的存储空间。此外,因为必须对数据集中的每个数据计算距离值,使用时可能非常耗费时间。
- k-邻近算法的另一个很重大的缺陷是无法给出任何数据的基础结构信息,我们无法知道我们所操作的样本具有什么样子的特征。接下来我们要学习使用概率测量的方法处理分类问题,听说这个算法可以解决这个问题。