我的机器学习之路(2)------kNN算法

本文介绍了KNN算法的基本原理及其实现方法,并通过Python代码详细展示了如何利用KNN算法进行分类预测。文中还提供了从数据预处理到模型评估的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    今天在网上买的《机器学习实战》这本书到了,以前学习的都是理论知识,很多算法没有实现过。理论终究要落实到实践中去,以后应该理论知识和代码实现一起学习。

    KNN算法的工作原理:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。

     2018.1.24本人python3.6版本,在学习过程,借鉴和学习了https://www.cnblogs.com/vrfighters/articles/4715527.html中的内容。对代码进行了一定自己的理解和修改。

     2018.1.25在昨天基础上,对KNN算法进行了实际应用的学习。借鉴和学习了https://www.zhihu.com/question/37146648中的内容。其中遇到了中文显示不正常的问题,在http://blog.youkuaiyun.com/ac540101928/article/details/51273291中得到了解决。

代码如下:

from numpy import *
import operator #运算符模块。
import matplotlib
import matplotlib.pyplot as plt
import os
#创建数据集
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
#算法核心
#inX:用于分类的输入向量。即将对其进行分类。
#dataSet:训练样本集
#labels:标签向量
def classfy0(inX,dataSet,labels,k):  #距离计算
    dataSetSize =dataSet.shape[0]#得到数组的行数。即知道有几个训练数据,第一维用1,为列数,第二维用0,为行的数。可理解为0表示列有多大,1表示行有多大
    diffMat     =tile(inX,(dataSetSize,1))-dataSet#tile:numpy中的函数。tile将原来的一个数组,扩充成了4个一样的数组。diffMat得到了目标与训练数值之间的差值。
    sqDiffMat   =diffMat**2#各个元素分别平方
    sqDistances =sqDiffMat.sum(axis=1)#对应行相加,axis=1表示对应行相加。axis=0表示列相加。一维数组是一个列元素阵列
    distances   =sqDistances**0.5#开方,得到距离。
    sortedDistIndicies=distances.argsort()#升序排列,得到的sortedDistIndicies是按照距离大小,得到的是原数组中的下标
    classCount={}
    for i in range(k):
        voteIlabel=labels[sortedDistIndicies[i]] #根据下标,找到原来的分类集
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1#如果没有这个分类集,则返回0,有则返回键值并且加1   
    sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)  #.item将字典变为元组,key按照元组中第二个元素进行比较。reverse是降序排列。
    return sortedClassCount[0][0]


def file2matrix(filename):  #读取数据
    fr = open(filename)
    numberOfLines = len(fr.readlines())    #得到了数据的行数
    returnMat = zeros((numberOfLines,3))   #zeros函数,得到numberOfLines行,3列的矩阵   
    classLabelVector = []                   
    fr = open(filename)
    index = 0
    for line in fr.readlines(): 
        line = line.strip()    #除去行末尾的空格
        listFromLine = line.split('\t')   #以空格分隔
        returnMat[index,:] = listFromLine[0:3]#[index,:]表示第index行的所有数据赋值,其实就是三个数据
        classLabelVector.append(int(listFromLine[-1]))  #将listFromLine最后一个元素存储
        index += 1       
    return returnMat,classLabelVector


def figure():   #散点图
    a,b=file2matrix("datingTestSet2.txt")


    fig=plt.figure(figsize=(10,8))
    ax1=fig.add_subplot(211)   #分割成2行1列第一块。add_subplot(n,m,p),将画布分割成n行m列放在第p块
    type1_x = []
    type1_y = []
    type2_x = []
    type2_y = []
    type3_x = []
    type3_y = []
    for i in range(len(b)):
        if b[i] == 1:  # 不喜欢
            type1_x.append(a[i][1])
            type1_y.append(a[i][2])


        if b[i] == 2:  # 魅力一般
            type2_x.append(a[i][1])
            type2_y.append(a[i][2])


        if b[i] == 3:  # 极具魅力
            type3_x.append(a[i][1])
            type3_y.append(a[i][2])
    type1 = ax1.scatter(type1_x, type1_y, s=20, c='red')
    type2 = ax1.scatter(type2_x, type2_y, s=40, c='green')
    type3 = ax1.scatter(type3_x, type3_y, s=50, c='blue')
    plt.xlabel('玩视频游戏所消耗的事件百分比')
    plt.ylabel('每周所消费的冰淇淋公升数')
    ax1.legend((type1, type2, type3), ('1', '2', '3'))
    ax2=fig.add_subplot(212)   #分割成2行1列第2块。add_subplot(n,m,p),将画布分割成n行m列放在第p块
    type1_x = []
    type1_y = []
    type2_x = []
    type2_y = []
    type3_x = []
    type3_y = []
    for i in range(len(b)):
        if b[i] == 1:  # 不喜欢
            type1_x.append(a[i][0])
            type1_y.append(a[i][1])


        if b[i] == 2:  # 魅力一般
            type2_x.append(a[i][0])
            type2_y.append(a[i][1])


        if b[i] == 3:  # 极具魅力
            type3_x.append(a[i][0])
            type3_y.append(a[i][1])
    type1 = ax2.scatter(type1_x, type1_y, s=20, c='red')
    type2 = ax2.scatter(type2_x, type2_y, s=40, c='green')
    type3 = ax2.scatter(type3_x, type3_y, s=50, c='blue')
    plt.xlabel('每年获得的飞行常客里程数')
    plt.ylabel('玩视频游戏所消耗的事件百分比')
    ax2.legend((type1, type2, type3), ('不喜欢', '魅力一般', '极具魅力'))
    plt.show()


def autoNorm(dataSet):  #归一化处理,dataSet为一个1000*3的矩阵
    minVals = dataSet.min(0)   #选取每一列中的最小值,0为列,1为行,1*3的矩阵
    maxVals = dataSet.max(0)   #选取每一列中的最大值,1*3的矩阵
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet)) #zeros(1000,3),得到1000*3的全0矩阵
    m = dataSet.shape[0]     #m=行数
    normDataSet = dataSet - tile(minVals, (m,1))   #扩充成和dataset相同行数。
    normDataSet = normDataSet/tile(ranges, (m,1))   #每一行都除以最大和最小的差值。
    return normDataSet, ranges, minVals


#tile(array,(m,n)).array是一维数组,则将扩充为m行和n倍。二维的则复制m行,每行的数组都复制n倍。按原来的排列。具体看https://www.cnblogs.com/zibu1234/p/4210521.html
def datingClassTest():
    hoRatio = 0.10      #设定10%的数据同于测试
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')       #加载数据
    normMat, ranges, minVals = autoNorm(datingDataMat)   #归一化数据处理
    m = normMat.shape[0]           #得到数据的量
    numTestVecs = int(m*hoRatio) #因为数组里面的[]是Int,所以要转换类型  
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classfy0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)  #第一个参数是前10%的数据,第二个是后90%的数据,第三个是标签,第四个是取前三个最近的点。
        print ("分类结果 %d, 实际结果: %d" % (classifierResult, datingLabels[i]))
        if (classifierResult != datingLabels[i]):
            errorCount += 1.0
            print("Error!")
    print ("总错误率: %f" % (errorCount/float(numTestVecs)))


def classifyPerson():
    resultList=['不喜欢','有点魅力','很有魅力']
    percentTats=float(input("玩视频游戏所消耗的事件百分比:"))
    ffMiles=float(input("每年获得的飞行常客里程数:"))
    iceCream=float(input("每周所消费的冰淇淋公升数:"))
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
    normMat,ranges,minVals=autoNorm(datingDataMat)
    inArr=array([ffMiles,percentTats,iceCream])
    classifierResult=classfy0((inArr-minVals)/ranges,normMat,datingLabels,3)#第一个参数是归一化后的新数据
    print("这个人给你的感觉:",resultList[classifierResult-1])


'''手写识别系统'''
def img2vector(filename):
    returnVect = zeros((1,1024))  #形成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


def handwritingClassTest():
    hwLabels = []
    trainingFileList = os.listdir('trainingDigits')           #获取训练目录里文件的文件名
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))   #扩充成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 =os.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 = classfy0(vectorUnderTest, trainingMat, hwLabels, 3)
        print ("训练集分类: %d, 实际类别: %d" % (classifierResult, classNumStr))
        if (classifierResult != classNumStr): errorCount += 1.0
    print( "\n总错误数: %d" % errorCount)
    print ("\n错误率: %f" % (errorCount/float(mTest)))


 
    




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值