Chapter2 K-近邻算法案例1

发布时间 2023-04-08 15:28:35作者: gao79138

案例2:使用K-近邻算法实现手写数字系统


1. 案例要求

    编写一个程序,应用K-近邻算法,实现手写数字系统。
    通过画图生成一个32*32的数字图像,再将图像转化为代表数字的0-1文本文件。之后往程序输入代表数字的0-1文本文件,程序便可以输出相应的数字。

2. 案例的执行流程

    示例:使用k-近邻算法的手写识别系统
        (1)收集数据:提供文本文件。
        (2)准备数据:编写函数img2vector(),将图像格式转换为分类器使用的向量格式。
        (3)分析数据:在Python命令提示符中检查数据,确保它符合要求。
        (4)训练算法:此步骤不适用于k-近邻算法。
        (5)测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
        (6)使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统。(本博客构建此应用程序,通过画图产生图像进行输入来完成识别)

3. 准备数据:将图像转换为测试向量

    我们可以将32*32的代表数字的0-1文本文件进行提取,转换为1*1024的array数组。换句话说,我们可以将文本文件中的每一个0/1都看成是该文本的特征。之后,就可以应用K-近邻算法了。需要注意的是:本例不需要进行归一化,因为每一个特征的取值范围都是0/1,不存在取值范围各不相同进而导致不同特征差值不同最终权重不同的情况。
    以下是代表数字0的文本文件的案例图:

img

# 本函数的作用就是将表示数字的0-1文本转换为1*1024的array数组
# 便于之后分类器采用K近邻算法进行分类
# 该函数位于kNN.py文件中

def img2vector(filename):
    returnVect = np.zeros((1, 1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32*i+j] = int(lineStr[j])
    return returnVect

4. 测试算法:使用K-近邻算法识别手写数字

# 该函数的用途是进行测试
# 该函数位于kNN.py文件中
def handwritingClassTest():
    hwLabels = []                                          # 记录训练集中的每个样本的标签
    # 列出训练集中的代表数字的所有文本文件
    trainingFileList = listdir('trainingDigits')           # listdir代表列出该目录下所有文件
    m = len(trainingFileList)                              # 求得训练样本的总数量
    trainingMat = np.zeros((m, 1024))                      # 构造训练集(矩阵)
    for i in range(m):
        fileNameStr = trainingFileList[i]                  # 取出文件名
        fileStr = fileNameStr.split('.')[0]                # 消除文件后缀
        classNumStr = int(fileStr.split('_')[0])           # 从文件名提取训练样本标签
        hwLabels.append(classNumStr)                       # 把标签添加进去
        trainingMat[i, :] = img2vector('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 = img2vector('testDigits/%s' % fileNameStr)
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
        if (classifierResult != classNumStr): errorCount += 1.0
    print("\nthe total number of errors is: %d" % errorCount)
    print("\nthe total error rate is: %f" % (errorCount/float(mTest)))
# 本代码的作用是运行上述的测试代码
# 该代码位于personalTest.py文件中
kNN.handwritingClassTest()
'''
the classifier came back with: 0, the real answer is: 0
the classifier came back with: 0, the real answer is: 0
the classifier came back with: 0, the real answer is: 0
the classifier came back with: 0, the real answer is: 0
the classifier came back with: 0, the real answer is: 0
the classifier came back with: 0, the real answer is: 0
...
the total number of errors is: 10

the total error rate is: 0.010571
'''
'''
错误率仅在1%左右,非常好!
'''

4. 构造程序:将画图中的图像转换为0-1文本文件

# 在kNN.py中添加如下函数
# 该函数的作用就是将图像转换为0-1txt文件
def jpgToText(jpgname):
    img = Image.open(jpgname)
    img2 = img.convert("1")      # 转换为1模式,用于处理二值(非黑即白)图像
    width,height = img2.size
    with open("./number.txt",'w') as f:
        for i in range(height):
            for j in range(width):
                if(int(img2.getpixel((j,i))) == 0): # getpixel函数用于获取图像中该点(坐标为像素点坐标与平面直角坐标相反)的颜色,0为黑,255为白(在1模式下)
                    f.write(str(1))
                else:
                    f.write(str(0))
                if(j == width-1):
                    f.write('\n')      #写下回车
    f.close()                          # 关闭文件

5. 构造程序:投入使用

# 以下代码位于kNN.py文件中
# 该代码的作用是通过画图进行手写数字识别
# 值得注意的是:用户的手写习惯、k值的选取、画笔的大小均可能影响识别效果。
# 当然,用官方选定的测试集,错误率不到1%
def handwriting():
    jpgToText('number.jpg')
    hwLabels = []
    trainingTextList = listdir('trainingDigits')
    m = len(trainingTextList)
    trainingMatrix = np.zeros((m,1024))
    for i in range(m):
        filename = trainingTextList[i]
        realfilename = filename.split('.')[0]
        label = realfilename.split('_')[0]
        hwLabels.append(label)
        trainingMatrix[i,:] = img2vector('trainingDigits/%s' % filename)
    testingMatrix = img2vector('number.txt')
    #以下代码用于测试
    for i in range(1024):
        if(i!=0 and i % 32 == 0):
            print()
        print(int(testingMatrix[0,i]),end="")
    print()
    print(classify0(testingMatrix,trainingMatrix,hwLabels,3))       # 输出分类结果
# 该代码的作用是用于测试
kNN.handwriting()
'''
00000000000000000000000000000000
00000000000000000000000000000000
00000000011111111111111111100000
00000000111111111111111111110000
00000001111111111111111111110000
00000001111111111111111111110000
00000001111111111111111111100000
00000001111100000000000000000000
00000001111100000000000000000000
00000001111100000000000000000000
00000001111100000000000000000000
00000001111100000000000000000000
00000011111111111110000000000000
00000011111111111111000000000000
00000011111111111111100000000000
00000011111111111111100000000000
00000001111111111111110000000000
00000000000000001111110000000000
00000000000000000111110000000000
00000000000000000111110000000000
00000000000000000111110000000000
00000000000000000111110000000000
00000000000000000111110000000000
00001110000000001111110000000000
00011111110011111111110000000000
00011111111111111111110000000000
00011111111111111111100000000000
00011111111111111111000000000000
00001111111111111100000000000000
00000001111111000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
5
00000000000000000000000000000000
00000000000000110000000000000000
00000110000001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100000000000000
00001111100001111100011110000000
00001111111001111111111111000000
00001111111111111111111111000000
00001111111111111111111111000000
00000111111111111111111110000000
00000011111111111111111000000000
00000000011111111111000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000001111100000000000000
00000000000000111000000000000000
00000000000000000000000000000000
4

Process finished with exit code 0

'''

img
img