k-近邻算法

来源:互联网 发布:mac死机自动重启 编辑:程序博客网 时间:2024/05/29 11:10

一、kNN算法的简介

(1)工作原理:存在一个样本数据集合,并且样本中每个数据都有标签。对于待分类的数据,将其各个特征与样本                              数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般只                              选取样本数据集中前k个最相似的数据,选择的k个数据中出现次数最多的分类,作为待分类数据的                              最终分类结果。(通常k是不大于20的整数)

(2)特点: kNN属于监督学习
(3)缺点:计算复杂度高,空间复杂度高
(4)优点:精度高、对异常值不敏感、无数据输入假定
(5)适用数据范围:数值型和标称型
下面举个小例子:
比如下面这个图,我们有两类数据,分别是蓝色方块
和红色三角形,它们分布在一个上图的二维中间中。对绿色圆圈进行分类。
如果k=3,在离绿色圆最近的3个数据中红色三角形类最多,所以绿色圆被赋予红色三角形类。
如果k=5,在离绿色圆最近的5个数据中蓝色方块类最多,所以绿色圆被赋予蓝色方块类。
由此也说明了KNN算法的结果很大程度取决于K的选择.


二、kNN基础实践

一个简单的待解决问题:已有数据:[1.0,0.9],[1.0,1.0],[0.1,0.2],[0.0,0.1],标签分别为'A','A','B','B'。两组待分类数据:[1.2,1.1],[0.1,0.1]
求两组待分类数据分别属于哪一类。
算法伪代码:
对未知类别属性的数据集中的每个点依次执行以下操作:
1)计算已知类别数据集中的点与当前点之间的距离;
2)按照距离递增次序排序;
3)选取与当前点距离最小的k个点;
4)确定前k个点所在类别的出现频率;
5)返回前k个点出现频率最高的类别作为当前点的预测分类。


运用python书写代码 如下:

from numpy import *  
import operator  

 
# create a dataset which contains 4 samples with 2 classes  
def createDataSet():  
    # create a matrix: each row as a sample  
    group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]])  
    labels = ['A', 'A', 'B', 'B'] # four samples and two classes  
    return group, labels  

def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0] #获取数据集的行数
 
    #计算距离
    #tile(a,(b,c)):将a的内容在行上重复b次,列上重复c次
    #下面这一行代码的结果是将待分类数据集扩展到与已有数据集同样的规模,然后再与已有数据集作差
    diffMat = tile(inX, (dataSetSize,1)) - dataSet 
 
    sqDiffMat = diffMat**2  #对上述差值求平方   
    sqDistances = sqDiffMat.sum(axis=1) #对于每一行数据求和
    distances = sqDistances**0.5 #对上述结果开方
    sortedDistIndicies = distances.argsort()  #对开方结果建立索引 
 
    #计算距离最小的k个点的Lable
    classCount={}  #建立空字典,类别字典,保存各类别的数目
    for i in range(k): #通过循环寻找k个近邻
        voteIlabel = labels[sortedDistIndicies[i]] #先找出开方结果索引表中第i个值对应的Label值
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 # 存入当前label以及对应的类别值
    #对类别字典进行逆排序,级别数目多的往前放
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #sorted函数用来排列       字典,items返回字典的元组对的列表,key是用列表的某个属性作为关键字,reverse指定是否逆序排列
    #返回结果
    return sortedClassCount[0][0] #返回级别字典中的第一个值,也就是最有可能的Label值

在界面输入:

if __name__ == "__main__" :
    dataset,labels=createDataSet()
    X = array([1.2, 1.1])
    Y = array([0.1, 0.1])
    k = 3
    labelX =  classify0(X,dataset,labels,k)
    labelY =  classify0(Y,dataset,labels,k)
    print "Your input is:", X, "and classified to class: ", labelX
    print "Your input is:", Y, "and classified to class: ", labelY

运行结果如下:

三、归一化特征值

观察下表,如果想计算样本3和样本4之间的距离,我们容易发现,数值差值大的属性对计算结果的影响最大,即飞行里程数对结果影响远大于其他两特征。但是我们认为这三种特征是等同重要的,因此处理这种不同取值范围特征值,我们通常采用方法是将数值归一化,如将取值范围处理为0到1或-1到1。


归一化特征值程序清单:

def autoNorm(dataSet):
    minVals = dataSet.min(0)    #函数从列中选取最小值
    maxVals = dataSet.max(0)    #函数从列中选取最大值
    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

python完整代码:

from numpy import *  
import operator  

# create a dataset which contains 4 samples with 2 classes  
def createDataSet():  
    # create a matrix: each row as a sample  
    group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]])  
    labels = ['A', 'A', 'B', 'B'] # four samples and two classes  
    return group, labels  


def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0] #获取数据集的行数
 
    #计算距离
    #tile(a,(b,c)):将a的内容在行上重复b次,列上重复c次
    #下面这一行代码的结果是将待分类数据集扩展到与已有数据集同样的规模,然后再与已有数据集作差
    diffMat = tile(inX, (dataSetSize,1)) - dataSet 
 
    sqDiffMat = diffMat**2  #对上述差值求平方   
    sqDistances = sqDiffMat.sum(axis=1) #对于每一行数据求和
    distances = sqDistances**0.5 #对上述结果开方
    sortedDistIndicies = distances.argsort()  #对开方结果建立索引 
 
    #计算距离最小的k个点的Lable
    classCount={}  #建立空字典,类别字典,保存各类别的数目
    for i in range(k): #通过循环寻找k个近邻
        voteIlabel = labels[sortedDistIndicies[i]] #先找出开方结果索引表中第i个值对应的Label值
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 # 存入当前label以及对应的类别值
 
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #对类别字典进行逆排序,级别数目多的往前放
 
    #返回结果
    return sortedClassCount[0][0] #返回级别字典中的第一个值,也就是最有可能的Label值

def autoNorm(dataSet):
    minVals = dataSet.min(0)    #函数从列中选取最小值
    maxVals = dataSet.max(0)    #函数从列中选取最大值
    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

程序测试: