kNN算法原理与实战

kNN简单数据分类实践

<比如:计算地理位置的相似度>
……

有以下先验数据,使用knn算法对未知类别数据分类

属性1属性2类别
1.01.0A
0.10.2B
0.00.1B

未知类别数据

属性1属性2类别
1.21.0?
0.10.3?

python实现

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

from numpy import *
import operator

'''
kNN:k近邻

Input:      inX: 待分类向量 (1xN)
            dataSet: 先验数据集 (NxM)
            labels: 先验数据分类标签 (1xM vector)
            k: 参数:k个近邻 (should be an odd number)

Output:     分类标签
'''

# 创建一个数据集,包含2个类别共4个样本
def createDataSet():
    group = array([[1.0,1.1], [1.0,1.0], [0,0], [0,0.1]])
    labels = ['A', 'A', 'B', 'B']
    return group, labels

# KNN分类器
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]  # shape[0]表示行数

    ## step 1: 计算距离
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet # 按元素求差值 tile
    sqDiffMat = diffMat**2  # 将差值平方
    sqDistences = sqDiffMat.sum(axis=1) # 按行累加
    distences = sqDistences**0.5    #将差值平方和求开方,即得距离
    ## step 2: 对距离排序
    sortedDistIndicies = distences.argsort()  # 排序后的索引   argsort() 返回排序后的索引值
    ## step 3: 选择k个最近邻
    classCount = {}
    for i in range(k):
        voteLable = labels[sortedDistIndicies[i]]
        ## step 4: 计算k个最近邻中各类别出现的次数
        classCount[voteLable] = classCount.get(voteLable, 0) + 1
    ## step 5: 返回出现次数最多的类别标签
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

if __name__ == '__main__':
    # kNN分类器测试
    group, labels = createDataSet()
    res = classify0([3,3], group, labels, 3)
    print(res)

输出结果为 A

运行过程中各个变量的值:
这里写图片描述

约会对象分类

婚恋网站数据: data/datingTestSet2.txt
属性:
每年的飞行里程
玩游戏所花时间百分比
每年吃几升冰激凌
标签:
3 -> 喜欢 2 -> 一般 1 -> 不喜欢
散点图
dating.py

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

import kNN
from numpy import *

#
# Author: yz
# Date: 2017-12-01
#

'''
约会对象分类
婚恋网站数据:datingTestSet2.txt
    每年的飞行里程
    玩游戏所花时间百分比
    每年吃几升冰激凌
    3 -> 喜欢  2 -> 一般  1 -> 不喜欢 

验证结果:
    前50%作为测试集,后50%作为训练集:
        错误的数量为33
        错误率为0.066

    前50%作为训练集,后50作为测试集:
        错误的数量为19
        错误率为0.038
'''

# 文件转换成矩阵
def file2matrix(filename):
    file = open(filename)
    numOfLines = len(file.readlines())  # 文件的行数
    returnMat = zeros((numOfLines, 3))    # 初始化要return的矩阵,numOfLines行,3列
    classLabelVector = []   # 初始化要return的标签向量
    index = 0
    file = open(filename)
    for line in file.readlines():
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector


# 归一化
# 每列的range = 每列的最大值 - 每列的最小值
# 每个元素归一化后的值 = (原来的值 - 该列的最小值) / 该列的最大值
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    #ranges = maxVals - minVals
    # normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]    # 行数
    normDataSet = (dataSet - tile(minVals, (m, 1))) / tile(maxVals, (m, 1))
    return normDataSet


# 用50%数据来做测试,统计分类结果错误率  总共1000行数据
def datingClassTest():
    ratio = 0.50    # 训练和测试的比例
    datingDataMat, datingLabels = file2matrix('data/datingTestSet2.txt')
    normMat = autoNorm(datingDataMat)
    m = normMat.shape[0]    # 行数
    numTestVecs = int(m * ratio)
    errorCount = 0
    for i in range(numTestVecs):
        classifyRes = kNN.classify(normMat[i, :], normMat[numTestVecs:m,:], datingLabels[numTestVecs:m], 3)
        print("kNN分类器分类结果为:{}, 真实的类别为:{}".format(classifyRes, datingLabels[i]) )
        if (classifyRes != datingLabels[i]): errorCount += 1
    print("错误的数量为%d" % errorCount)
    print("错误率为{}".format(str(errorCount/numTestVecs)))


# 图形化展现
def graphicalDisplay():
    import matplotlib.pyplot as plt
    datingDataMat, datingLabels = file2matrix('data/datingTestSet2.txt')

    fig = plt.figure()
    ax = fig.add_subplot(111)
    # ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
    # ax.scatter(datingDataMat[:, 0], datingDataMat[:, 2], 15.0 * array(datingLabels), 15.0 * array(datingLabels))
    ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * array(datingLabels), 15.0 * array(datingLabels))
    plt.show()


if __name__ == '__main__':
    # 约会对象分类效果测试
    datingClassTest()
    # 图形化展现
    graphicalDisplay()

kNN实现手写数字识别

需求

利用一个手写数字“先验数据”集,使用knn算法来实现对手写数字的自动识别;
先验数据(训练数据)集:

数据维度比较大,样本数比较多。
数据集包括数字0-9的手写体。
每个数字大约有200个样本。
每个样本保持在一个txt文件中。
手写体图像本身的大小是32x32的二值图,转换到txt文件保存后,内容也是32x32个数字,0或者1,如下:

模型分析:
- 1、手写体因为每个人,甚至每次写的字都不会完全精确一致,所以,识别手写体的关键是“相似度”
- 2、既然是要求样本之间的相似度,那么,首先需要将样本进行抽象,将每个样本变成一系列特征数据(即特征向量)
- 3、手写体在直观上就是一个个的图片,而图片是由上述图示中的像素点来描述的,样本的相似度其实就是像素的位置和颜色之间的组合的相似度
- 4、因此,将图片的像素按照固定顺序读取到一个个的向量中,即可很好地表示手写体样本
- 5、抽象出了样本向量,及相似度计算模型,即可应用KNN来实现
handWriting.py

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

import kNN
from numpy import *
from os import listdir

#
# Author: yz
# Date: 2017-12-01
#

'''
利用分类器进行手写数字识别测试
识别结果:
    错误的数量为10
    错误率为0.010570824524312896
'''

def img2vector(filePath):
    returnVect = zeros((1, 1024))
    file = open(filePath)
    for i in range(32):
        line = file.readline()
        for j in range(32):
            returnVect[0,32*i+j] = int(line[j])
    return returnVect



def handwritingClassTest():
    trainingFilePath = "data/digits/trainingDigits/"
    testFilePath = "data/digits/testDigits/"
    hwLabels = []
    trainingFileList = listdir(trainingFilePath)
    m = len(trainingFileList)   # 1934
    trainingMat = zeros((m, 1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]   # 0_10.txt
        fileName = fileNameStr.split(".")[0]    # 0_10
        classNum = int(fileName.split("_")[0])  # 0
        hwLabels.append(classNum)
        trainingMat[i, :] = img2vector(trainingFilePath + fileNameStr)
    testFileList = listdir(testFilePath)
    errorCount = 0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileName = fileNameStr.split(".")[0]
        classNum = int(fileName.split("_")[0])
        testVector = img2vector(testFilePath + fileNameStr)
        classifyRes = kNN.classify(testVector, trainingMat, hwLabels, 3)
        print("kNN分类器分类结果为:{}, 真实的数字为:{}".format(classifyRes, classNum))
        if (classifyRes != classNum): errorCount += 1
    print("错误的数量为%d" % errorCount)
    print("错误率为{}".format(str(errorCount / mTest)))


if __name__ == '__main__':
    handwritingClassTest()

ps: 本文所有代码和数据集已上传到我的github: MachineLearning/kNN/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值