机器学习与数据挖掘—K邻近算法(KNN)

KNN:分类算法

目标:

对未知类别的样本进行分类预测

步骤:

1.对于某个未知类别样本,根据距离度量计算每个已知类别样本与其距离。
2.选出K个与该未知类别样本距离最小的已知类别的样本。
3.在K个已知类别样本里得到频数最多的类别,该类别就是未知类别样本的预测。

KNN算法:


# 寻找K值 1~60 打印误差最小的K值和对应的误差个数及误差率
from numpy import *

import operator  # 引入运算符模块,K邻近算法会使用到其中的函数


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 classifyO(inX, dataSet, labels, k):  # KNN算法
    # 用于分类的输入向量是inX,
    # 输入的训练样本集为dataSet,标签向量为labe1s,最后的参数k表示用于选择最近邻居的数目,
    # 其中标签向量的元素数目和矩阵dataset的行数相同。

    dataSetSize = dataSet.shape[0]  # shape[0]就是读取矩阵第一维度的长度。
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    #根据公式可推测出计算差值
    # 把inx行列重复copy(dataSetSize,1)次。
    # inX是个向量,而dataset是个矩阵,两者之间要进行相减的运算,需要把这个向量也补成一个和dataset有相同行数列数的矩阵,
    # tile()的第二个参数,也就是(datasetsize,1),这个参数的意思就是把inX补成有datasetsize行数的矩阵。
    # 然后和dataset相减就是根据矩阵的减法进行的。因此diffMat是一个差值矩阵。
    sqDiffMat = diffMat ** 2  # 欧氏距离计算公式
    sqDistances = sqDiffMat.sum(axis=1)  # axis这个参数,它决定对矩阵求和时候的顺序,axis=0是按照行求和,axis=1是按照列进行求和。
    distances = sqDistances ** 0.5  # 0.5次方即开根号
    sortedDisIndicies = distances.argsort()  # 就是把向量中每个元素进行排序,而它的结果是元素的索引(原来的下标)形成的向量。
    classCount = {}  # 生成字典,对classCount元素赋值,其实是个计数器
    # 选择距离待分类点最小的k个点
    for i in range(k):  # 遍历k,寻找该样本标签的类型
        voteIlabel = labels[sortedDisIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # python3字典的 items() 方法,以列表的形式返回可遍历的(键,值)元组数组。
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # 内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作与sort方法不一样。
    # reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
    # sorted函数返回类型为list
    return sortedClassCount[0][0]  # 返回前k个点出现频率最高的类别作为当前点的预测分类。即最符合的标签

# 文件导入

def file2matrix(filename):
    fr = open(filename)
    # 得到文件行数
    arrayOLines = fr.readlines()
    # 创建返回的NumPy矩阵
    numberOfLines = len(arrayOLines)
    returnMat = zeros((numberOfLines, 3))
    # 解析文件数据到列表
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector

#散点图代码块 (已注释需要运行出图片解注释即可)

#from numpy import array
#import matplotlib
#import matplotlib.pyplot as plt
#fig = plt.figure()
#ax = fig.add_subplot(111)
#datingDataMat,datingLabels = file2matrix('F:\python项目\KNN\datingTestSet2.txt')
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
#plt.show()

#归一化特征值
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    #将每列的最小值放在变量minvals中,将最大值放在变量maxVals中,
    # 其中dataset.min(0)中的参数0使得函数可以从列中选取最小值,而不是选取当前行的最小值。
    # 然后,函数计算可能的取值范围,并创建新的返回矩阵。
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (m,1))#特征在短阵有1000×3个值,而minvals与raange的值都为1×3。因此利用tile()
    #函数将变量内容复制成输入矩阵同样大小的矩阵
    normDataSet = normDataSet/tile(ranges, (m,1)) #矩阵除法
    return normDataSet, ranges, minVals



#自动化调试模块代码


def Autodebug(k):
    minCount = 20 #假如其中有20个误差,结果当然没有这么多,设置这么大也没关系,反正minCount会被覆盖掉
    for k in range(1,60):
        datingClassTest(k)
        #print("the total error rate is : ", datingClassTest(k)[1]) #该条注释用来验证结果
       #print("the total error count is : ", datingClassTest(k)[0]) #该条注释用来验证结果
        if(datingClassTest(k)[0]<minCount):
                                             #误差最小一定误差数最少,误差数最少,误差率一定最小
            minCount=datingClassTest(k)[0]

    print("The minErroK's minErroCount= ",minCount)
    print("The minErroK's minErroRate=",minCount/100)
    for k in range(1,60):
        datingClassTest(k)
        if datingClassTest(k)[0]==minCount:
            print("The nice K= ",datingClassTest(k)[2])




#分类器
def datingClassTest(k):
    hoRatio=0.1
    datingDataMat,datingLabels=file2matrix('F:\python项目\KNN\datingTestSet2.txt')
    normMat,minVals,ranges=autoNorm(datingDataMat)
    m=normMat.shape[0]
    errorCount=0.0
    numTestVecs=int(m*hoRatio)
    for i in range(numTestVecs):
        classifierResult=classifyO(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],k) #//K取3时
        if (classifierResult!=datingLabels[i]):
            errorCount+=1.0
            x=errorCount/float(numTestVecs)
    return (errorCount,x,k) #稍作修改,方便Autodebug()函数调用,自动化测试,返回误差个数和误差率即可






Autodebug(1) #直接Run即可


#如果k值选择较大的话,距离较远的训练样本也能够对实例预测结果产生影响。
# 这时候,模型相对比较鲁棒,不会因为个别噪声点对最终预测结果产生影响。
# 但是缺点也十分明显:算法的近邻误差会偏大,距离较远的点(与预测实例不相似)也会同样对预测结果产生影响,
# 使得预测结果产生较大偏差,此时模型容易发生欠拟合。
#因此,在实际工程实践中,我们一般采用交叉验证的方式选取k值。
# 通过以上分析可知,一般k值选得比较小,我们会在较小范围内选取k值。
# 同时把测试集上准确率最高的那个确定为最终的算法超参数k。

#虽然K值4与7都满足最小误差率,但k值4作为超参数更好
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章