knn及其实现(根据机器学习实战 约会预测 手写数字识别)

本文深入解析了KNN算法的基本原理,包括k值选择、距离度量和分类决策规则,并通过约会网站和手写数字识别两个实际案例,展示了算法的具体实现过程。同时,讨论了KNN算法的优点和缺点。

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

knn:给定数据集,对于新的输入实例,在训练集中找到与该实例最邻近的k个实例,k的实例的多数属于某个类,则新的输入实例就属于某个类。
knn三个基本要素:k值的选择,距离度量,分类决策规则
算法实现:
1.计算已知类型数据集中的点与当前点的距离
2.按照距离递增次序进行排序。
3.选取与当前点距离最小的k个点。
4.确定前k个点所在类别出现的频率。
5.返回出现频率最高的类别作为预测分类。

首先进行一个knn对约会网站的实现

import numpy as np
import operator    #运算符模块
def createDataSet():
    group=np.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=np.tile(inX,(dataSetSize,1)) - dataSet  #将输入向量扩充到样本数行,再和训练集样本相减
    sqDiffMat=diffMat ** 2 #平方
    sqDistances=sqDiffMat.sum(axis=1)  #每行求和
    distances=sqDistances ** 0.5
    sortedDistIndicies=distances.argsort()    #返回的是从小到大的距离的索引
    
    classCount={}
    for i in range(k):
        voteIlabel=labels[sortedDistIndicies[i]]  
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1  #统计前k个各个label的个数
    sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1)
    ,reverse=True)  #将字典分解为元组 运用itemgetter方法按照第二个元素的次序对元组进行排序,为逆序
    return sortedClassCount[0][0]
# =============================================================================
# 将文本记录转换到numpy的程序
# =============================================================================
def file2matrix(filename):
    fr=open(filename)
    arrayOLines=fr.readlines()   #一行一行读进来
    numberOfLines=len(arrayOLines)  #行数
    returnMat=np.zeros((numberOfLines,3))  #构造1000*3的0矩阵
    classLabelVector=[]
    index=0
    for line in arrayOLines:
        line=line.strip()  #删除开头和结尾的字符
        listFromLine=line.split('\t')  #在\t处进行切片操作,得到了4个值
        returnMat[index,:]=listFromLine[0:3]   #第index行填入获取到的4个中的前3个值
        classLabelVector.append(int(listFromLine[-1]))
        index+=1
    return returnMat,classLabelVector

# =============================================================================
# 特征进行归一化
# =============================================================================
def autoNorm(dataSet):
    minVals=dataSet.min(0) #每列的最小值
    maxVals=dataSet.max(0) #每列的最大值
    ranges=maxVals-minVals
    normDataSet=np.zeros(dataSet.shape)
    m=dataSet.shape[0] #行数
    normDataSet=dataSet-np.tile(minVals,(m,1))   #np.tile(minVals,(m,1))代表这一个m行的矩阵,值为minVals
    normDataSet=normDataSet/np.tile(ranges,(m,1))
    return normDataSet,ranges,minVals

# =============================================================================
    #画图
# import matplotlib
# import matplotlib.pyplot as plt
# fig=plt.figure()
# ax=fig.add_subplot(111)
# ax.scatter(returnMat[:,1],returnMat[:,2])  训练集的第二列 第三列数据
# plt.show()
# =============================================================================
    
# =============================================================================
# 测试代码
# =============================================================================
def datingClassTest():
    hoRatio=0.1
    datingDataMat,datinglabels=file2matrix('datingTestSet2.txt')
    normMat,ranges,minVals=autoNorm(datingDataMat)
    m=normMat.shape[0]   #样本数
    numTestVecs=int(m*hoRatio)
    errorCount=0
    for i in range(numTest):
        classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],datinglabels[numTestVecs:m],3)
        print('the classifier came back with: %d,the real answer is: %d' %(classifierResult,datinglabels[i]))
        if (classifierResult != datinglabels[i]):
            errorCount+=1
    print('the total error rate is :%f' % (errorCount/float(numTestVecs)))
    

if __name__ == '__main__':
    datingClassTest()

接下来进行了一个knn对于手写数字识别的实现

import numpy as np
import operator    #运算符模块
from os import listdir   #返回指定的文件夹包含的文件或文件夹的名字的列表


def classify0(inX,dataSet,labels,k):
    dataSetSize=dataSet.shape[0]   #训练集的样本数
# =============================================================================
#     计算输入向量和所有训练样本之间的距离   二范数
# =============================================================================
    diffMat=np.tile(inX,(dataSetSize,1)) - dataSet  #将输入向量扩充到样本数行,再和训练集样本相减
    sqDiffMat=diffMat ** 2 #平方
    sqDistances=sqDiffMat.sum(axis=1)  #每行求和
    distances=sqDistances ** 0.5
    sortedDistIndicies=distances.argsort()    #返回的是从小到大的距离的索引
    
    classCount={}
    for i in range(k):
        voteIlabel=labels[sortedDistIndicies[i]]  
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1  #统计前k个各个label的个数
    sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1)
    ,reverse=True)  #将字典分解为元组 运用itemgetter方法按照第二个元素的次序对元组进行排序,为逆序
    return sortedClassCount[0][0]


# =============================================================================
# 将图像转化为向量   32*32到1*1024
# =============================================================================
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
    
# =============================================================================
# 测试代码
# =============================================================================
def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')           #load the training set
    m = len(trainingFileList)
    trainingMat = np.zeros((m,1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]     #take off .txt
        classNumStr = int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
    testFileList = listdir('testDigits')        #iterate through the test set
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]     #take off .txt
        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)))
    
if __name__ == '__main__':
    handwritingClassTest()

优点:精度高,对于异常值不敏感,无数据输入假定。
缺点:计算复杂度高,空间复杂度高。
可以用kd树来对传统的knn算法进行优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值