机器学习-KNN算法

KNN理论

个人理解的思路

给定已经具有标记的训练数据集合,计算新样本与训练数据集合的距离,升序排序取TopK获取K个标签,最后根据K个标签,进行多票判决规则,预测该新样本点的结果。

拆分细节

1:距离
度量向量点的距离方式很多,根据不同的应用场景取不同的计算距离方式,默认采取的是欧式距离。
常见距离:
1) Lp 距离族, p=1 时表示哈曼顿距离, p=2 表示欧式距离, p= 表示最大距离

Lp=(l=1n|xlixlj|p)1/p

2)汉明距离
3)余弦距离
4)相关系数距离
5)互信息距离值
6)卡方距离
上述2)~6)类似距离的概念,主要是用于衡量两个样本点的近似情况,距离的本质就是衡量两个样本点的差异程度。未给出的距离概念,是一种扩散想法,在某个详细业务场景时,可以按照样本差异程度来考虑距离的概念。
2:K值确定
K值的不同,体现了候选集合的大小,当 K=1 时表示最近的1个样本具有表决权,若是 K=N 表示表决权是全体训练数据集合。显然K值越大,表示模型越简单,反之越复杂。理解这点,可以换角度理解,也就是根据K值与训练数据样本点,可以进行对数据空间进行划分,若是 K=1 时,那么数据空间就会被划分为N个空间,显然,子空间越多,模型是越复杂的。如何选择合适K值,李航书中提到使用交叉验证的方式来评估一个K值选取后的效果,那么根据这个评判,对K值进行一定的枚举选择合适的K值了,一般建议取相对小的,否则学习的近似误差过大,那么估计误差是无意义的,因为根据学习没啥关系了,相当于随机咯。这里对K值多啰嗦了几句,是因为自己当时不是特别理解,尤其是当进行实验室,发现K值增加,估计误差并没有完全的下降,但是学习误差是显然的增加的。
3:决策
默认采取的是多数表决的策略决定最终结果的。这里完全可以发散想的,因为这里的TopK,是磨平了距离值的,完全是可以被考虑进入的,根据距离值,对TopK的距离值进行求平均值,取平均值最小的标签,表示最终结果,或者其他的方式。另外,还有就是标签在训练集合中是存在不同大小的。显然标签不均匀时,标签数目多的会被倾向于被选择,这里可以考虑去除这种训练数据不均带来的影响,也就是可以将距离与标签的权重考虑,也就是 。类似的思考是完全可以采取的,这里都是需要根据具体场景情况进行思考优化改进的。

总结

1:优点
精度高,对异常点不敏感(除非选择的K值不合理),无需训练,实现简单(无论是KD树实现,还是原始版)
2:缺点
空间复杂度过高,根据样本点数目成线性关系,不管KD树实现还是原始版
时间复杂度过高,原始版是与样本数目以及维度都是线性的关系的;KD树优化后,只是在搜索阶段压缩了成为了 log(N) 的复杂度。
适合度:
标注数据量的问题,不能够太小,太小则模型效果不行,不能够太大,太大则模型运行效率不行。总而而言,适合中等数据规模的分类;另外,针对的特征属性的要求是数值类型的(一般都能够满足);

KNN实践

这部分,就按照原始版实现的,KD树版的暂且木有实现,博主懒呗,后期工作用到,需要实现了,再考虑实现吧,原始版实现比较简单,尤其是用python。不废话,具体的代码逻辑见下,不做过多的解释。后期的自己应该看的懂的,毕竟简单嘛

import numpy as np
import matplotlib
import matplotlib.pyplot as plot
import matplotlib.lines as mlines
import sys
from pandas import Series,DataFrame
import pandas as pd
def LoadData(FileName):
    Fi = open(FileName, 'r')
    DataSet = Fi.readlines()
    DataSetLen = len(DataSet)
    DataSetFeat = np.zeros((DataSetLen, 3))#数据特征
    DataSetLable = []
    Index = 0
    LableName = {}
    for line in DataSet:
        line = line.strip()
        LineList = line.split('\t')
        DataSetFeat[Index, :] = LineList[0:3]
        if not LableName.has_key(LineList[-1]):
            LableName[LineList[-1]] = len(LableName) + 1

        DataSetLable.append(LableName[LineList[-1]])
        Index += 1
    print LableName
    return [DataSetFeat,DataSetLable,LableName]
def Normal(DataSetFeat):
    minVals = DataSetFeat.min(0) #表示0维度上的求最小值,也就是列上的
    maxVals = DataSetFeat.max(0)
    ranges = (maxVals - minVals)
    m = DataSetFeat.shape[0]
    DataSetFeat = DataSetFeat - np.tile(minVals, [m, 1]) #tile具备扩散效果
    DataSetFeat = DataSetFeat / np.tile(ranges, (m, 1))

    return DataSetFeat

def classfy0(Feat, DataSetFeat, DataSetLable, K): #选择最近的K个数据,多数投票决定
    m = DataSetFeat.shape[0]
    FeatSet = np.tile(Feat, (m, 1))
    FeatSet = (FeatSet - DataSetFeat) * (FeatSet - DataSetFeat)
    Feat = FeatSet.sum(1)
    Feat = np.sqrt(Feat) #求出预测点到样本集合点的距离
    FeatIndex = np.argsort(Feat) #排序用于获取最近的TOK个点的下标
    DataSetLable = np.array(DataSetLable)
    Lable = DataSetLable[FeatIndex[0:K]]
    #print Lable
    Lable = Series(Lable)
    LableCount = Lable.value_counts()
    LableCount = (-LableCount).argsort().index.tolist()
    #print LableCount[0]
    return LableCount[0]
#K值越大,模型越简单---近似误差变大,估计误差在一定程度上会变小(超出一定能力,会变大)
def Test(DataSetFeat, DataSetLable):
    K = 3
    for K in range(1, 200, 10):
        for TestPercent in [10]:#range(10,100,10):
            TestPercent = float(TestPercent) * 0.01
            TotalNum = len(DataSetLable)
            TestNum = int(TotalNum * TestPercent)
            print TestNum, TotalNum
            ErrCount = 0
            for i in range(TestNum):
                ResultLabe = classfy0(DataSetFeat[i,:], DataSetFeat[TestNum:TotalNum,:], DataSetLable[TestNum:TotalNum], K)
                if ResultLabe != DataSetLable[i]:
                    ErrCount += 1
                #print "Predict Lable: %d, Real Lable: %d"%(ResultLabe, DataSetLable[i])
            print "TestPercent:%f, K:%d, Err Ratio: %f"%(TestPercent, K, float(ErrCount)/float(TestNum))

def TestTrian(DataSetFeat, DataSetLable):
    K = 3
    for K in range(1, 200, 10):
        for TestPercent in [90]:#range(10,100,10):
            TestPercent = float(TestPercent) * 0.01
            TotalNum = len(DataSetLable)
            TestNum = int(TotalNum * TestPercent)
            print TestNum, TotalNum
            ErrCount = 0
            for i in range(TestNum):
                ResultLabe = classfy0(DataSetFeat[i,:], DataSetFeat[0:TestNum,:], DataSetLable[0:TestNum], K)
                if ResultLabe != DataSetLable[i]:
                    ErrCount += 1
                #print "Predict Lable: %d, Real Lable: %d"%(ResultLabe, DataSetLable[i])
            print "TrainData: TestPercent:%f, K:%d, Err Ratio: %f"%(TestPercent, K, float(ErrCount)/float(TestNum))


FileName = sys.argv[1]
[DataSetFeat, DataSetLable, LableName] = LoadData(FileName)
DataSetFeat = Normal(DataSetFeat)
Test(DataSetFeat, DataSetLable)
TestTrian(DataSetFeat, DataSetLable)

#print DataSetFeat
#fig = plot.figure()
#ax = fig.add_subplot(111)
#ax.scatter(DataSetFeat[:,0], DataSetFeat[:,1], 15.0 * np.array(DataSetLable), 15.0 * np.array(DataSetLable))
#plot.show()

你可能感兴趣的:(机器学习学习-基础版)