机器学习实战——kNN

最近在学习机器学习算法,感觉有本书写得很不错——《机器学习实战》,如果有一点基础去看这本书,然后在结合书中实例进行实践,还是很有收获的。


之后,可能会不定时的更新此书的相关内容,主要内容参考此书,夹杂一些我自己实践中的经验。

KNN算法

本书介绍的第一个机器学习算法是kNN算法,这个算法比较基础,简单易懂。

实现的主要步骤:
(1)收集数据。

(2)准备数据:使用python解析文本文件

(3)分析数据:主要是可视化数据

(4)训练算法:kNN不用训练

(5)测试算法:计算错误率,或者准确率

(6)使用算法:

A. 输入训练样本向量或者特征向量;

B. 计算待分类样本(或特征向量)到这些训练样本的距离,一般是欧式距离

C. 选取最小的k个距离,统计对应的类别,哪个类别多,待分类的样本就属于哪一类

kNN的简单实现

注:代码主要参考自书本,有一些改动。

kNN的简单实现
#-*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2 as cv

# kNN算法的流程
# 收集、整理数据->计算数据到分类点的距离
# 选取k个距离最近的数据,看这几个数据属于哪一类,
# 所属类别多的,待分类的点就属于此类
# kNN算法不需要训练!!

def createDataSet():
    # 构造简单的数据集
    group = np.array([[1.0, 1.1],[1.0,1.0],[0,0.2],[0,0.1]])
    # 这里对数据集进行修改,感觉书上的有点不合理 [0,0]->[0,0.2]
    labels = ['A','A','B','B']
    return group, labels
数据的可视化
def visualData(group,labels):
    # 数据可视化
    x1 = group[:2,0]
    y1 = group[:2,1]
    x2 = group[2:,0]
    y2 = group[2:,1]
    p1= plt.scatter(x1,y1)
    p2= plt.scatter(x2,y2)
    plt.legend([p1,p2],["A","B"])
    plt.show()
def classify0(inX, dataSet, labels, k):
    """
    :param inX: 待分类的向量 - [x0,y0]
    :param dataSet: 数据集 - [[x1,y1],[x2,y2],...] - numpy类型
    :param labels: 标签数据 - 'A'或者'B'
    :param k: 设置的k值,选取前k个结果 - int
    :return:分类的结果,'A'或者'B'
    """
    d = inX-dataSet
    sqMat = d**2
    sqDis = sqMat.sum(axis=1)
    distances = sqDis**0.5
    sortedDis = distances.argsort()  # 将距离排序,并提取其对应的序号
    assert len(sortedDis)>=k  # 检查k的设置是否有问题
    classCount = {}
    for i in range(k):
        votelabel = labels[sortedDis[i]]
        classCount[votelabel] = classCount.get(votelabel, 0)+1
    keylist = list(classCount.keys())
    s = 0
    for l in keylist:
        if classCount[l] > s:
            s = classCount[l]
            res = l
    return res

运行结果:

可视化的结果:

最终分类结果:

kNN的手写数字识别

思路:将手写数字图像转为向量,然后计算测试图像向量和训练图像向量的距离,然后选k个所属类别最多的作为最终类别。
# 以下为手写数字识别的内容
def img2vector(filename):
    # 将二进制图转换为向量,方便进行距离的计算
    resVec = np.zeros((1, 1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            resVec[0, 32*i+j] = int(lineStr[j])
    return resVec
def visualImg(filename):
    # 可视化手写数字图像
    resimg = np.zeros((32, 32))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            resimg[i][j] = int(lineStr[j])
    cv.imshow("numberImg", resimg)
    cv.waitKey(0)
    return resimg
def handwritingClassTest():
    hwLabels = []
    trainingFileList = os.listdir('./digits/trainingDigits/')
    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('./digits/trainingDigits/%s' % fileNameStr)
    testFileList = os.listdir('./digits/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('./digits/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)))
手写数字图像的可视化:

最终的分类结果:




单看这个结果,好像比用CNN的结果还要好,https://blog.csdn.net/louishao/article/details/60867339

但是,kNN分类的这个数据集是比较小的,而且算法较为简单,比较复杂的情况分类的效果就会变得差一些,而且kNN无法表示图像/数据的特征。

完整代码

#-*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import os
import cv2 as cv

# kNN算法的流程
# 收集、整理数据->计算数据到分类点的距离
# 选取k个距离最近的数据,看这几个数据属于哪一类,
# 所属类别多的,待分类的点就属于此类
# kNN算法不需要训练!!

def createDataSet():
    # 构造简单的数据集
    group = np.array([[1.0, 1.1],[1.0,1.0],[0,0.2],[0,0.1]])
    # 这里对数据集进行修改,感觉书上的有点不合理 [0,0]->[0,0.2]
    labels = ['A','A','B','B']
    return group, labels
def visualData(group,labels):
    # 数据可视化
    x1 = group[:2,0]
    y1 = group[:2,1]
    x2 = group[2:,0]
    y2 = group[2:,1]
    p1= plt.scatter(x1,y1)
    p2= plt.scatter(x2,y2)
    plt.legend([p1,p2],["A","B"])
    plt.show()

def classify0(inX, dataSet, labels, k):
    """
    :param inX: 待分类的向量 - [x0,y0]
    :param dataSet: 数据集 - [[x1,y1],[x2,y2],...] - numpy类型
    :param labels: 标签数据 - 'A'或者'B'
    :param k: 设置的k值,选取前k个结果 - int
    :return:分类的结果,'A'或者'B'
    """
    d = inX-dataSet
    sqMat = d**2
    sqDis = sqMat.sum(axis=1)
    distances = sqDis**0.5
    sortedDis = distances.argsort()  # 将距离排序,并提取其对应的序号
    assert len(sortedDis)>=k  # 检查k的设置是否有问题
    classCount = {}
    for i in range(k):
        votelabel = labels[sortedDis[i]]
        classCount[votelabel] = classCount.get(votelabel, 0)+1
    keylist = list(classCount.keys())
    s = 0
    for l in keylist:
        if classCount[l] > s:
            s = classCount[l]
            res = l
    return res

# 以下为手写数字识别的内容
def img2vector(filename):
    # 将二进制图转换为向量,方便进行距离的计算
    resVec = np.zeros((1, 1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            resVec[0, 32*i+j] = int(lineStr[j])
    return resVec

def visualImg(filename):
    # 可视化手写数字图像
    resimg = np.zeros((32, 32))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            resimg[i][j] = int(lineStr[j])
    cv.imshow("numberImg", resimg)
    cv.waitKey(0)
    return resimg

def handwritingClassTest():
    hwLabels = []
    trainingFileList = os.listdir('./digits/trainingDigits/')
    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('./digits/trainingDigits/%s' % fileNameStr)
    testFileList = os.listdir('./digits/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('./digits/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)))

if __name__ == '__main__':
    '''
    group, labels = createDataSet()
    visualData(group, labels)
    inX = [0, 0]
    classres = classify0(inX, group, labels, 3)
    print(classres)
    '''
    '''
    handWriteTrainDir = "./digits/trainingDigits/"
    handWriteTestDir = "./digits/testDigits/"
    trainlist = os.listdir(handWriteTestDir)
    visualImg(handWriteTrainDir+trainlist[500])
    vec = img2vector(handWriteTrainDir+trainlist[0])
    '''
    handwritingClassTest()

猜你喜欢

转载自blog.csdn.net/louishao/article/details/80398834