一、 KNN算法概述

K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。需要注意的是KNN算法是监督学习中的分类算法,看起来和它的兄弟Kmeans算法有点像。KNN算法它的类别都是已知的,通过对已知分类的数据进行训练和学习,找到这些不同类的特征,在对未分类的数据进行分类。而kmeans算法属于非监督学习,我们事先是不知道数据会分为几类,通过聚类分析后将数据合成几个群体。

二、 KNN算法介绍

K-近邻算法(KNN)算法实现简单、高效。在分类、回归、模式识别等方面有着广泛的应用。该方法的思路是:在特征空间中,如果一个样本附近的k个最近(即特征空间中最邻近)样本的大多数属于某一个类别,则该样本也属于这个类别。就比如经常和我一块玩的人有10个人,他们中有6个人是好人,有1个人是比较坏的,有3个人又好又坏的,那么大家就认为我也是个好人,通过我接触某个类型的多少以此来给我定性。对应中国的古话:“近朱则赤,近墨者黑”。所以这个K的取值是很重要的,

机器学习-KNN聚类算法原理及Python代码实现_机器学习

上图中圆环就是K的取值范围,当K的取值是4时,K被分到黄色一栏,当K的取值是6时,K被分到蓝色一栏.

K值选择

通过上图我们知道K的取值是很重要的,那么我们该如何确定K的取值呢?在KNN中K是一个超参数,需要我们进行指定,一般情况下这个k和数据有很大关系,都是交叉验证进行选择,但是建议使用交叉验证的时候,k∈[2,20],使用交叉验证得到一个很好的k值。需注意最好K的取值为奇数,防止出现平票而无法分类的情况。 k值还可以表示我们的模型复杂度,k值越小,意味着模型复杂度变大,更容易过拟合(用极少数的样例来绝对这个预测的结果,很容易产生偏见,这就是过拟合)。k值越大,学习的估计误差越小,但是学习的近似误差就会增大,容易造成欠拟合。

距离计算

度量空间中点距离有好几种度量方式,比如常见的曼哈顿距离计算,欧式距离计算等等。通常KNN算法中采用的是欧式距离,二维空间两个点的欧式距离计算公式如下:

欧氏距离:

机器学习-KNN聚类算法原理及Python代码实现_近邻算法_02

曼哈顿距离:

机器学习-KNN聚类算法原理及Python代码实现_近邻算法_03

三、KNN算法优缺点

优点

  • knn算法是一种在线技术,新数据可以直接加入数据集,而不必重新训练数据
  • knn理论简单,容易实现
  • 简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归
  • 可用于数值型数据和离散型数据
  • 训练时间复杂度为O(n);无数据输入假定
  • 对异常值不敏感

缺点

  • 对于样本容量大的数据集,计算量大
  • 样本不平衡时,预测偏差较大。如:某一类样本较少而其他类样本数据较少。
  • knn每一次分类都会重新进行全局运算,因此计算负载重,需要很多内存。
  • k的选择
  • 模型在高维空间中可能效果不佳,而且当数据规模增长时,模型的扩展性能也不好。
  • 难以确定那种距离计算好。

四、KNN算法实现步骤

输入:

  1. 训练集数据:包括要训练的样本数和它们对应的标签
  2. 测试数据:只包含样本数据,没有对应的标签
  3. K值:K的取值范围,指定计算距离最近邻的数量

算法:

  1. 通过欧式距离计算公式:√(x - x1)² + (y - y1)²,计算测试数据与训练集中所有样本数据之间的距离
  2. 根据计算出来的距离数据,取K个最近邻的数据
  3. 统计这K个最近邻数据的标签
  4. 将测试集中的样本分类为统计数据中出现次数最多的那个类别

输出:

  1. 分类结果:将测试集中的样本被分类为对应的类别

五、KNN算法Python实现

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
from pylab import *
import openpyxl

datafile = '/Users/desktop/leastsquare.xlsx'


def knn(train_data, testdata, data_tag, k):
    # 将数组统一化
    # 将输入数据平铺为train_data_size行1列,便于与训练数据做差
    new_testtdata = np.tile(test_data, (train_data.shape[0], 1))
    new_testtdata = new_testtdata - train_data
    new_testtdata = new_testtdata ** 2
    new_testtdata1 = new_testtdata.sum(axis=1)
    new_testtdata1 = new_testtdata1 ** 0.5  # 欧氏距离
    # new_testtdata1 = pow(new_testtdata1,0.5)
    # 将欧氏距离排序,返回对应的索引值
    sortdata = np.argsort(new_testtdata1)

    # 统计前k个类型数量
    count = {}
    for i in range(k):
        vote = data_tag[sortdata[i]]  # 第i个距离对应的标签
        count[vote] = count.get(vote, 0) + 1  #统计某个标签个数
    sortdata = sorted(count.items(), key=lambda x: x[1], reverse=True)
    return sortdata[0][0], test_data


def Read_data():
    data = pd.read_excel(datafile, index_col=u'电影名称', sheet_name='knn_data2')
    x = data[u'打斗次数'].tolist()
    y = data[u'接吻次数'].tolist()
    # z = data[u'z'].tolist()
    b = data[u'电影类型'].tolist()
    return x, y, b


x, y, b = Read_data()


def write_data(write_test_data, write_test_tag):
    """
    :param write_test_data: 将测试数据写入文件
    :param write_test_tag: 将预测后的标签写入文件
    :return:
    """
    # 打开Excel文件
    workbook = openpyxl.load_workbook(datafile)
    # 选择工作表
    worksheet = workbook['knn_data2']
    # 在指定单元格中写入数据
    worksheet['A{}'.format(int(len(x)) + 2)] = int(len(x)) + 2
    worksheet['B{}'.format(int(len(x)) + 2)] = write_test_data[0]
    worksheet['C{}'.format(int(len(x)) + 2)] = write_test_data[1]
    worksheet['D{}'.format(int(len(x)) + 2)] = write_test_tag
    # 保存Excel文件
    workbook.save(datafile)


# 调用matplotlib实现散点图
def Show():
    '''
    plt.scatter(x, y, c=b,cmap='viridis')
    plt.show()
    '''
    # mpl.rcParams['font.sans-serif'] = ['SimHei']
    tag1, data1 = knn(train_data, test_data, data_tag, k)
    write_data(data1, tag1)
    print('测试数据为:{}\n'
          'k取值范围为:{}\n'
          '输入数据的类型为:“{}”'.format(test_data, k, tag1))
    # 添加颜色列表
    colourlist = []
    for i in b:
        if i == '动作片':
            colourlist.append('black')
        if i == '科幻片':
            colourlist.append('orange')
        if i == '爱情片':
            colourlist.append('red')
    if tag1 == '动作片':
        colourlist1 = ['black', 'Testdata is Action movie']
    if tag1 == '科幻片':
        colourlist1 = ['orange', 'Testdata is Science fiction film']
    if tag1 == '爱情片':
        colourlist1 = ['red', 'Testdata is Romantic film']
    # 画出图表
    plt.title("KNN algorithm implemented in Python")
    plt.ion()
    plt.scatter(x, y, c=colourlist, s=15)
    plt.scatter(data1[0], data1[1], c=colourlist1[0], marker='*', s=100)
    didntLike = mlines.Line2D([], [], color='black', marker='.',
                              markersize=6, label='Action movie')
    smallDoses = mlines.Line2D([], [], color='orange', marker='.',
                               markersize=6, label='Science fiction film')
    largeDoses = mlines.Line2D([], [], color='red', marker='.',
                               markersize=6, label='Romantic film')
    testdata = mlines.Line2D([], [], color=colourlist1[0], marker='*',
                             markersize=12, label=colourlist1[1])
    # 添加图例
    plt.legend(handles=[didntLike, smallDoses, largeDoses, testdata])
    plt.show()


if __name__ == '__main__':

    train_data = np.array(
        [i for i in zip(Read_data()[0], Read_data()[1])]
    )
    data_tag = np.array(Read_data()[2])
    test_x = int(input("输入测试数据x:"))
    test_y = int(input("输入测试数据y:"))
    test_data = np.array([test_x, test_y])
    k = int(input("输入k取值范围:"))
    Show()

测试数据

机器学习-KNN聚类算法原理及Python代码实现_人工智能_04

运行结果

机器学习-KNN聚类算法原理及Python代码实现_python_05

机器学习-KNN聚类算法原理及Python代码实现_近邻算法_06

上图五角星就是我们的分类结果