【机器学习实战学习笔记】k-近邻算法

工作原理

存在一个样本数据集合,并且样本集合中每个数据都存在标签,即知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据的分类标签
**一般k不大于20

特点

–优点:(1)精度高
(2)对异常值不敏感
(3)无数据输入假定
–缺点:计算复杂度高、空间复杂度高
–适用数据范围:数值型和标称型
注:
标称型数据:一般在有限的数据中取,而且只存在“是”和“否”两种不同的结果,一般用于分类问题
数值型数据:可以在无限的数据中取,而且数据比较具体化,一般用于回归问题

一般流程

(1)收集数据:可以使用任何方法
(2)准备数据:距离计算所需要的数值,最好是结构化的数据格式
(3)分析数据:可以使用任何方法
(4)训练方法:可略过
(5)测试算法:计算错误率
(6)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理

使用算法过程

对未知类别属性的数据集中的每个点依次执行以下操作:
(1)计算已知类别数据集中的点与当前点之间的距离
(2)按照距离递增次序排序
(3)选取与当前点距离最小的k个点
(4)确定前k个点所在类别的出现频率
(5)返回前k个点出现频率最高的类别作为当前点的预测分类

示例1

假设有4个点,(1,1.1)、(1,1)、(0,0)、(0,0.1),假设有一个新的点,对这个新点进行k-近邻分类

from numpy import *
import operator

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

def classify0(inX,dataSet,labels,k):
    dataSetSize = dataSet.shape[0] # 获取有多少个数据
    # 算出距离
    diffMat = tile(inX,(dataSetSize,1)) - dataSet #tile(A,n) 将数组A重复n次,以此算出差值即计算出(X1-X2)和(Y1-Y2)
    sqDiffMat = diffMat ** 2
    sqDistance = sqDiffMat.sum(axis=1)  #算出 (X1-X2)^2 + (Y1-Y2)^2
    distances = sqDistance ** 0.5
    sortedDistIndices = distances.argsort()
    # 截取前k个
    classCount = {}
    for i in range(k):
        voteLabel = labels[sortedDistIndices[i]]
        classCount [voteLabel] = classCount.get(voteLabel,0) + 1
    # 选出频率最高的
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
    return sortedClassCount[0][0]

if __name__ == '__main__':
    group,labels = createDataSet()
    inX = [0,0]
    print(classify0(inX=inX,dataSet=group,labels=labels,k=3))

如果新点为[0,0](举例),则分类结果为B:

在这里插入图片描述

若新点为[7,8](举例),则分类结果为A:

在这里插入图片描述

示例2

使用k-近邻算法改进约会网站的配对效果

一般流程

(1)收集数据:提供文本文件
(2)准备数据:使用Python解析文本文件
(3)分析数据:使用matplotlib画图
(4)训练算法:略过
(5)测试算法:使用所提供的的部分数据作为测试样本
(6)使用算法:产生简单的命令行程序,而后自行进行测试

数据:
数据存放在txt中


数据格式:每年获得的飞行常客里程数;玩视频游戏所消耗时间百分比;每周消费的冰激凌公升数;类别分类
类别分为3类:1-不喜欢的人;2-魅力一般的人;3-极具魅力的人

from numpy import *
import matplotlib
import matplotlib.pyplot as plt


def file2matrix(filename):
    fr = open(filename)
    arrayLines = fr.readlines()
    numberOfLines = len(arrayLines)
    returnMat = zeros((numberOfLines,3))
    classLabelVector = []
    index = 0
    for line in arrayLines:
        line = line.strip().split('\t')
        returnMat[index,:] = line[0:3]
        classLabelVector.append(int(line[-1]))
        index += 1
    return returnMat,classLabelVector

if __name__ == '__main__':
    datingDataMat,datingDataLabel = file2matrix('datingTestSet2.txt')
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(datingDataMat[:,1],datingDataMat[:,2]) # 使用datingDataMat矩阵的第二、第三列数据
    plt.show()

最终画出来的数据点是这样的…
在这里插入图片描述
为了分出不同类别的数据,采用以下代码:

ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingDataLabel),15.0*array(datingDataLabel))

结果是这样的…
在这里插入图片描述
如果选取第一、第二列的特征,是这样的…
在这里插入图片描述
如果选取的是第一、第三列,结果是这样的…
在这里插入图片描述
∴ 选取第一、第二个特征比较好~

在datingTestSet2.txt文件上测试分类器

from numpy import *
import operator

"""
Fuction:
    将txt分解为特征值和标签值两个矩阵
Patameters:
    filename:数据文件
Output/Return:
    returnMat:特征矩阵
    classLabelVector:标签值
"""
def file2matrix(filename):
    fr = open(filename)
    arrayLines = fr.readlines()
    numberOfLines = len(arrayLines)
    returnMat = zeros((numberOfLines,3))
    classLabelVector = []
    index = 0
    for line in arrayLines:
        line = line.strip().split('\t')
        returnMat[index,:] = line[0:3]
        classLabelVector.append(int(line[-1]))
        index += 1
    return returnMat,classLabelVector

"""
Funtion:
    数据归一化
Parameters:
    dataSet:特征矩阵
Output/Return:
    normDataSet:归一化后的特征矩阵
    ranges:变化范围
    minVals:每一特征的最小值
"""
def autonorm(dataSet):
    minVals = dataSet.min()
    maxVals = dataSet.max()
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals,(m,1))
    normDataSet = normDataSet / tile(ranges,(m,1))
    return normDataSet,ranges,minVals

"""
Funtion:
    k-近邻分类器
Parameters:
    inX:测试样本的特征矩阵
    dataSet:训练样本的特征矩阵
    labels:训练样本的标签
    k:截取前k个“答案”值
Output/Return:
    sortedClassCount:根据各个标签在答案中的出现频率,选择最高的频率对应的标签作为分类答案
"""
def classify0(inX,dataSet,labels,k):
    dataSetSize = dataSet.shape[0] # 获取有多少个数据
    # 算出距离
    diffMat = tile(inX,(dataSetSize,1)) - dataSet #tile(A,n) 将数组A重复n次,以此算出差值即计算出(X1-X2)和(Y1-Y2)
    sqDiffMat = diffMat ** 2
    sqDistance = sqDiffMat.sum(axis=1)  #算出 (X1-X2)^2 + (Y1-Y2)^2
    distances = sqDistance ** 0.5
    sortedDistIndices = distances.argsort()
    # 截取前k个
    classCount = {}
    for i in range(k):
        voteLabel = labels[sortedDistIndices[i]]
        classCount [voteLabel] = classCount.get(voteLabel,0) + 1
    # 选出频率最高的
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
    return sortedClassCount[0][0]

"""
Function:
    测试分类器性能
Output:
    错误率
"""
def datingClassTest():
    ratio = 0.1
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
    normMat,ranges,minVals = autonorm(datingDataMat)
    m = normMat.shape[0]
    numTestVec = int(m * ratio)  # 按比例划分测试集
    errorCount = 0.0 # 设置成浮点数,若是整数,则计算百分比只能计算得出0(整数除法--截取),如果是浮点数,则可正确得到百分比
    for i in range(numTestVec):
        classifierResult = classify0(normMat[i,:],normMat[numTestVec:m,:],datingLabels[numTestVec:m],3)
        print("The classifier came back with : %d,the real answer is:%d" % (classifierResult,datingLabels[i]))
        if classifierResult != datingLabels[i]:
            errorCount += 1.0
    print("The total error rate is %f" % (errorCount / numTestVec))


if __name__ == '__main__':
    datingClassTest()

结果:

在这里插入图片描述

错误率:

在这里插入图片描述

示例3

手写识别系统
训练数据和测试数据的格式是txt文件,用1和0两个数值表示,以此来显现不同的手写字体

在这里插入图片描述

from numpy import *
import operator
import os

"""
Funtion:
    将txt文件中的数据存放在数组中
Parameters:
    filename: 存放手写字体的txt文件
Output/Return:
    returnVec: 返回存储的数组
"""
def img2vector(filename):
    returnVec = zeros((1,1024))
    fr = open(filename)

    for i in range(32):
        line = fr.readline()
        for j in range(32):
            returnVec[0,32 * i + j] = int(line[j])
    return returnVec

"""
Funtion:
    k-近邻分类器
Parameters:
    inX:测试样本的特征矩阵
    dataSet:训练样本的特征矩阵
    labels:训练样本的标签
    k:截取前k个“答案”值
Output/Return:
    sortedClassCount:根据各个标签在答案中的出现频率,选择最高的频率对应的标签作为分类答案
"""
def classify0(inX,dataSet,labels,k):
    dataSetSize = dataSet.shape[0] # 获取有多少个数据
    # 算出距离
    diffMat = tile(inX,(dataSetSize,1)) - dataSet #tile(A,n) 将数组A重复n次,以此算出差值即计算出(X1-X2)和(Y1-Y2)
    sqDiffMat = diffMat ** 2
    sqDistance = sqDiffMat.sum(axis=1)  #算出 (X1-X2)^2 + (Y1-Y2)^2
    distances = sqDistance ** 0.5
    sortedDistIndices = distances.argsort()
    # 截取前k个
    classCount = {}
    for i in range(k):
        voteLabel = labels[sortedDistIndices[i]]
        classCount [voteLabel] = classCount.get(voteLabel,0) + 1
    # 选出频率最高的
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
    return sortedClassCount[0][0]

"""
Function:
    手写字体识别
Output:
    错误率
"""
def handwritingClassTest():
    handwritingLabel = []
    trainingFileList = os.listdir('trainingDigits') # 获取训练集文件夹下的内容
    trainingNum = len(trainingFileList) # 获取训练集大小
    trainingMat = zeros((trainingNum,1024))
    for i in range(trainingNum):
        fileName = trainingFileList[i]
        fileStr = fileName.split('.')[0]  # 获取文件夹的名字
        classNum = int(fileStr.split('_')[0]) # 获取手写数字的具体数值
        handwritingLabel.append(classNum)
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileName)  # 将训练文件中的值存储起来
    testFileList = os.listdir('testDigits')
    errorCount = 0.0
    testNum = len(testFileList)
    for i in range(testNum):
        fileName = testFileList[i]
        fileStr = fileName.split('.')[0]
        classNum = int(fileStr.split('_')[0])
        vectorTest = img2vector('testDigits/%s' % fileName)
        classifierResult = classify0(vectorTest,trainingMat,handwritingLabel,3)
        print("The classifier result is %d,the real answer is %d" % (classifierResult,classNum))
        if classifierResult != classNum:
            errorCount += 1.0
    print("The number of wrong answer is %d" % (errorCount))
    print("The error rate is %f" % (errorCount / testNum))

if __name__ == "__main__":
    handwritingClassTest()

结果:

在这里插入图片描述
错误率:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值