【机器学习】分类算法--K近邻算法

前言

K近邻算法通常写作Knn算法,它是一种有监督的分类算法。Knn算法与K-means算法不同,K-means算法是是无监督的聚类算法。今天我就用iris的数据和大家聊聊什么是Knn

图为iris部分数据,其中蓝色表示setosa、绿色表示virginica,红色表示我们需要预测的点


正文

Knn原理

通俗的讲,就是找与自己特征距离最近的k个点,根据k个点中出现类别最多的,作为其预测分类。

假如我们先不考虑用什么度量距离,从图可以直观的看到,当K=3时,我们的预测的点是virginica类别。

那么K值取大一点可以吗,答案是可以的。K值与分类的错误率变化曲线是先下降后上升的。在很多资料中提到,通常K是不大于20的整数,这应该是由预测类别数和样本总数预估的结果。

 

距离度量

计算距离其实就是度量特征的相似性,计算方法有:欧式距离、曼哈顿距离、马氏距离、余弦相似度、汉明距离

Knn常用是欧式距离,在二维平面上其表达式为

                                                                     \rho =\sqrt{\left ( x_{2} - x_{1})^{2}+( y_{2} - y_{1})^{2}}

其在三维空间上的表达式为

                                                         \rho =\sqrt{\left ( x_{2} - x_{1})^{2}+( y_{2} - y_{1})^{2}+( z_{2} - z_{1})^{2}}

由此,我们可以扩展得到更高维的计算表达式。那么我们可以想到Knn暴力解决方法是遍历计算所有点与预测点的距离,最终选取K个值,作为分类参考。

整理一下Knn算法的实现逻辑:

  1. 计算已知类别数据集中的点与当前点之间的距离;
  2. 按照距离递增次序排序;
  3. 选取与当前点距离最小的k个点;
  4. 确定前k个点所在类别的出现频率;
  5. 返回前k个点出现频率最高的类别作为当前点的预测分类。

 

代码实现

代码运行环境:python 3.5.2

1、自定义函数实现

# -*- coding: UTF-8 -*-

import pandas as pd
import numpy as np
import operator

iris = pd.read_csv('F:\ML\iris.data',header = None)
iris.columns=['sepal_lengh_cm','sepal_width_cm','petal_length_cm','petal_width_cm','class']

seto = iris.iloc[0:50,:]
virg = iris.iloc[100:150,:]

data = pd.concat([seto,virg]).reset_index(drop=True)
labels = data['class']
data = data[['sepal_lengh_cm','sepal_width_cm']]

def classify_knn(pred, trainData, labels, k):
    """
    @Function:  knn分类
    @Args:        pred:用于分类的输入向量 (1xN)
              trainData:输入的训练样本集 (NxM)
                 labels:标签向量 (1xM vector)
                      k:用于比较的近邻数量 (should be an odd number)
    @Returns:   sortedClassCount[0][0]:分类结果
    """
    trainDataSize = trainData.shape[0]
    diffMat = np.tile(pred, (trainDataSize, 1)) - trainData
    #对求差后的矩阵求平方
    sqDiffMat = diffMat**2
    #对矩阵的每一行求和
    sqDistances = sqDiffMat.sum(axis=1)
    #求矩阵的和的平方根
    distances = sqDistances**0.5
    #对上式结果进行排序,argsort函数返回的是:数组值从小到大的索引值
    sortedDistIndicies = distances.argsort()
    #创建字典
    classCount = {}
    #给字典赋值
    for i in range(k):
        #字典的key
        voteIlabel = labels[sortedDistIndicies[i]]
        #classCount.get(voteIlabel,0):如果字典键的voteIlabel有值就返回值,无值则返回0,
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    #对classCount进行排序,默认的sorted是升序,所以加入reverse=True
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    #返回分类结果
    return sortedClassCount[0][0]


---------------------------------------------------------------------------------
Input:
classify_knn([6.0,3.5], data, labels, 3)

Output: Iris-virginica

2、sklearn方法调用

import pandas as pd
import numpy as np
from sklearn import neighbors


iris = pd.read_csv('F:\ML\iris.data',header = None)
iris.columns=['sepal_lengh_cm','sepal_width_cm','petal_length_cm','petal_width_cm','class']

seto = iris.iloc[0:50,:]
vers = iris.iloc[50:100,:]
virg = iris.iloc[100:150,:]


data = pd.concat([seto,virg]).reset_index(drop=True)
labels = data['class']
data = data[['sepal_lengh_cm','sepal_width_cm']]


classify_knn = neighbors.KNeighborsClassifier(3)
classify_knn.fit(data, labels)
result = classify_knn.predict([[6.0,3.5]])
print(result)

-----------------------------------------------------------------
Output:
['Iris-virginica']

小结

优点:精度高、对异常值不敏感、无数据输入假定

缺点:由于所有的训练数据都会在内存中计算,所以时间复杂度高、空间复杂度

由本次练习得出,学习scikit-learn也能提高自己编码的效率,后面会对scikit-learn做一个专题的学习分享

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值