(有监督)Python实现KNN算法(学习笔记)

一、前言

数据挖掘十大算法–KNN算法。K-NN是一种基于实例的学习,或者是局部近似和将所有计算推迟到分类之后的惰性学习。k-近邻算法是所有的机器学习算法中最简单的之一。基于实例的学习方法只是简单地把训练样例存储起来。从这些实例中泛化的工作被推迟到必须分类新的实例时。每当学习器遇到一个新的查询实例,它分析这个新实例与以前存储的实例的关系,并据此把一个目标函数值赋给新实例。
KNN算法虽然简单,但是很经典,对于后续有监督学习算法的学习理解会有很大的帮助。

FLAG:以后相关的机器学习算法都要自己编程实现,然后和相关的库函数实现做一个对比。

二、实验环境

  • python3.6.4
  • IDE:Pycharm 2018
  • 操作系统:windows10
  • 本实验用到的数据均为python的机器学习库自带,或者是自己生成的数据,数据集为鸢尾花数据集。
  • 依赖库:scipy,matplotlib, numpy,pandas,sklearn

三、KNN分类器的基本原理


3.1、算法简介

KNN最邻近分类算法的实现原理:为了判断未知样本的类别,以所有已知类别的样本作为参照,计算未知样本与所有已知样本的距离,从中选取与未知样本距离最近的K个已知样本,根据少数服从多数的投票法则(majority-voting),将未知样本与K个最邻近样本中所属类别占比较多的归为一类。

KNN算法的关键之处在于:

  • 1、样本的所有特征都要做可比较的量化

    若是样本特征中存在非数值的类型,必须采取手段将其量化为数值。例如样本特征中包含颜色,可通过将颜色转换为灰度值来实现距离计算。

  • 2、样本特征要视情况做归一化处理

    样本有多个参数,每一个参数都有自己的定义域和取值范围,他们对距离计算的影响不一样,如取值较大的影响力会盖过取值较小的参数。所以样本参数必须做一些 scale 处理,最简单的方式就是所有特征的数值都采取归一化处置。

  • 3、需要一个距离函数以计算两个样本之间的距离

通常使用的距离函数有:欧氏距离、余弦距离、汉明距离、曼哈顿距离等,一般选欧氏距离作为距离度量,但是这是只适用于连续变量。在文本分类这种非连续变量情况下,汉明距离可以用来作为度量。

  • 4、K值的确定,对于近邻数的选择是一般考经验来选。

K值选的太大易引起欠拟合,太小容易过拟合,需交叉验证确定K值。
k太小,分类结果易受噪声点影响;k太大,近邻中又可能包含太多的其它类别的点。(对距离加权,可以降低k值设定的影响)
k值通常是采用交叉检验来确定(以k=1为基准)
经验规则:k一般低于训练样本数的平方根

3.2、数学推导

KNN算法是基于实例的学习算法,没有一个规则的训练过程,因此KNN算法没有严格的数学推导过程,这里也就不再探讨。
需要指出的是:
kNN是一种懒惰算法,平时不好好学习,考试(对测试样本分类)时才临阵磨枪(临时去找k个近邻)。
懒惰的后果:构造模型很简单,但在对测试样本分类地的系统开销大,因为要扫描全部训练样本并计算距离。
已经有一些方法提高计算的效率,例如压缩训练样本量等。

3.3、行业应用

客户流失预测、欺诈侦测等(更适合于稀有事件的分类问题)


3.3、算法迭代步骤

  • step1:数据预处理,划分训练集和测试集,并根据需求标准化或者归一化数据集(预处理)
  • step2:计算测试样本与训练样本之间的欧氏距离(算距离)
  • step3:圈定与待分类样本距离最近的K个已分类样本,作为待分类样本的近邻(找邻居)
  • step4:根据这K个近邻中的大部分样本所属的类别来决定待分类样本该属于哪个分类(做分类)
  • step5:计算分类的准确率(模型评估)

KNN算法的流程图如下:

四、KNN算法的编程实现(python)

本实例用到得到实验数据为python的机器学习库自带的尾花卉数据集,并且对数据进行分割,从而形成训练集和测试集

PCA算法实现的脚本如下:

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
import scipy.io as sio # load mat
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import random
from sklearn.datasets import load_iris #导入IRIS数据集
from sklearn import preprocessing#导入数据标准化模块
import operator

'''
使用sklearn实现需要额外导入一下依赖库

'''
# 导入数据集生成器
from sklearn.datasets import make_blobs
# 导入KNN 分类器
from sklearn.neighbors import KNeighborsClassifier
# 导入数据集拆分工具
from sklearn.model_selection import train_test_split

# 这两句代码用来正确显示图中的中文字体,后续都要加上
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

iris = load_iris()  #鸢尾花数据集特征矩阵
#给多维数组提那家一列数据的两种方法
# data = np.c_[np.array(iris.data),np.array(iris.target)]
data = np.insert(iris.data,4,values = iris.target,axis=1)
data = pd.DataFrame(data,columns=['Length', 'Width','PetalLength','PetalLength','label'])
#数据集导出为excel文件
# data.to_excel('C:/Users/13109/desktop/鸢尾花数据集.xlsx',encoding="gb2312")
data = data.values
# print(data)

##拆分数据集为训练集和测试集 train[],test[]
def splitdata(data,split,train=[],test=[]):
    for x in range(len(data)):
        if random.random() <= split:
            train.append(data[x])
        else:
            test.append(data[x])
    return np.array(train),np.array(test)
train=[]
test=[]
train,test=splitdata(data,0.5,train,test)
# print(train)
#导出测试集作对比
test1 = pd.DataFrame(test,columns=['Length', 'Width','PetalLength','PetalLength','label'])
test1.to_excel('C:/Users/13109/desktop/鸢尾花测试集.xlsx',encoding="gb2312")
print('训练集样本量:' + repr(len(train)))
print('测试集样本量:' + repr(len(test)))

#获取k个近邻
def getneighbors(train,test,k):
    datasize = train.shape[0]
    data = np.tile(test, (datasize, 1))
    distance = np.sqrt(np.sum(np.square(train[:,:-1]-data[:,:-1]),axis=1))
    sortdistance = distance.argsort()
    neighbors = []
    for x in range(k):
        np.array(neighbors.append(train[sortdistance[x], :]))
    neighbors = np.array(neighbors)
    return neighbors

# # print(data[:,:-1])
# distance = np.sqrt(np.sum(np.square(train[:,:-1] - data[:,:-1]), axis=1))
# sortdistance = distance.argsort()
# # print(sortdistance)
# neighbors = []
# for x in range (5):
#     np.array(neighbors.append(train[sortdistance[x],:]))
# neighbors = np.array(neighbors)
# print(neighbors)

##在距离较近的k个实例中找到占比较大的分类
def getclassvotes(neighbors):
    classvotes = {}
    for x in range(len(neighbors)):
        votelabel = neighbors[x,-1]
        classvotes[votelabel] = classvotes.get(votelabel,0)+1
        sortclass = sorted(classvotes.items(),key=operator.itemgetter(1),reverse=True)
        predata = max(classvotes,key = classvotes.get)
    return predata,sortclass


##计算KNN准确率
def accuracy(test,predata):
    correct = 0
    for i in range(len(test)):
        if test[i,-1] == predata[i]:
            correct+=1
    accur = (correct/float(len(test)))*100.0
    return accur


def main():
    predictions = []
    for i in range(len(test)):
        neighbors = getneighbors(train,test[i],5)
        result,sortclass = getclassvotes(neighbors)
        predictions.append(result)
    accur = accuracy(test,predictions)
    print('KNN分类识别率: ' + repr(accur) + '%')
main()


  • 脚本输出结果
(machine_learning) D:\CloudMusic\virtualenv\machine_learning\machine>python KNN.py
训练集样本量:83
测试集样本量:67
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0
, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.
0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0]
(自编脚本)KNN分类识别率: 98.50746268656717%

上面的代码是自己编程实现的KNN算法,下面我们再来直接调用scikit-learn库相关的代码来实现KNN,使用的数据集还是IRIS数据集。

五、KNN算法的scikit-learn库实现

###5.1、实现对鸢尾花数据集的分类

实现的脚本如下:


'''
使用Python的sklearn库进行KNN

 KNeighborsClassifier(n_neighbors=5, weights='uniform', 
                      algorithm='auto', leaf_size=30, 
                      p=2, metric='minkowski', 
                      metric_params=None, n_jobs=1, **kwargs)
 n_neighbors: 默认值为5,表示查询k个最近邻的数目
 algorithm:   {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’},指定用于计算最近邻的算法,auto表示试图采用最适合的算法计算最近邻
 leaf_size:   传递给‘ball_tree’或‘kd_tree’的叶子大小
 metric:      用于树的距离度量。默认'minkowski与P = 2(即欧氏度量)
 n_jobs:      并行工作的数量,如果设为-1,则作业的数量被设置为CPU内核的数量
 查看官方api:http://scikit-learn.org/dev/modules/generated/sklearn.neighbors.KNeighborsClassifier.html#sklearn.neighbors.KNeighborsClassifier

————————————————
'''

#划分训练集和测试集
x_train,x_test,y_train,y_test = train_test_split(iris.data,
                                                 iris.target,
                                                 test_size = 0.5,
                                                 random_state=33)
# # 训练数据和测试数据进行归一化处理
# ss = preprocessing.MinMaxScaler()
# x_train = ss.fit_transform(x_train)
# x_test = ss.transform(x_test)

'''
KNN分类器的具体应用
'''
#建立一个模型对象
knc = KNeighborsClassifier(algorithm='auto',n_neighbors=5)
#输入训练数据建模
knc.fit(x_train, y_train)
#对测试数据进行预测
y_predict = knc.predict(x_test)
#打印对比预测结果和实际分类结果
print(y_predict+1 )
print(y_test+1)

x = range(1,len(y_predict)+1)
plt.scatter(x,y_predict+1,c='orange',edgecolor='k',label='KNN分类')
plt.scatter(x,y_test+1,c='k',edgecolor='r',label='实际分类')
plt.legend()
plt.show()


  • 使用scikit-learnj机器学习库实现KNN分类的预测实际对比图

  • 实验对比(自编脚本和scikit-learnj机器学习库实现的对比)
(machine_learning) D:\CloudMusic\virtualenv\machine_learning\machine>python KNN.py
训练集样本量:81
测试集样本量:69
(自编脚本)KNN分类识别率: 95.65217391304348%
scikit-learnj机器学习库分类准确率:98.66666666666667%


根据上述的的实验结果可以看出,自编脚本与scikit-learnj第三方库实现的结果基本一致,存在微小的差异。第三方库的准确率要高一点的主要原因,可能是近邻算法方面有所优化,knc = KNeighborsClassifier(algorithm=‘auto’,n_neighbors=5),参数algorithm='auto’表示自动选择最适合的算法计算最近邻。总的来说,差异不大。

###5.2、使用KNN算法进行回归分析并绘图
这里我们使用scikit-learn的make_regression生成数据集来进行实验,演示KNN算法在回归分析中的表现。

实验脚本如下:

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
'''
基于邻居的回归可用于数据标签是连续的而不是离散的变量的情况。分配给查询点的标签是根据其最近邻居的标签的平均值计算的。

class sklearn.neighbors.KNeighborsRegressor(n_neighbors = 5,weights ='uniform',algorithm ='auto',
leaf_size = 30,p = 2,metric ='minkowski',metric_params = None,n_jobs = None,** kwargs )

n_neighbors:近邻个数,默认为5
weights:近邻权重函数,默认为'uniform','distance':weight points by the inverse of their distance.
 in this case, closer neighbors of a query point will have a greater influence than neighbors which are further away.
algorithm:{'auto','ball_tree','kd_tree','brute'},可选,'auto'将尝试根据传递给fit方法的值来确定最合适的算法。
metric:默认度量标准是minkowski,p = 2等于标准欧几里德度量标准
'''



import matplotlib.pyplot as plt
import numpy as np
# 导入用于回归分析的KNN模型
from sklearn.neighbors import KNeighborsRegressor
# 导入数据集拆分工具
from sklearn.model_selection import train_test_split
# 导入随机回归数据集生成器
from sklearn.datasets.samples_generator import make_regression

# 这两句代码用来正确显示图中的中文字体,后续都要加上
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号


'''
介绍一下sklearn.datasets.make_regression函数用于生成随机回归数据
sklearn.datasets.make_regression(n_samples=100, n_features=100, n_informative=10, n_targets=1, 
bias=0.0, effective_rank=None, tail_strength=0.5, noise=0.0, shuffle=True, coef=False, random_state=None)

其中比较重要的参数为:
n_samples : int,可选(默认值= 100)样本数量。
n_features:样本特征维度,也就是X维度
n_informative : int,optional(默认值= 10),信息特征的数量,即用于构建用于生成输出的线性模型的特征的数量。
n_targets : int,optional(默认值= 1),也就是y输出向量的维数。
noise : float,optional(默认值= 0.0)应用于输出的高斯噪声的标准偏差。
random_state:None(默认),数据集随机种子
'''

# 生成样本数为100,
X,Y=make_regression(n_samples=100,n_features=1,n_informative=10,noise=50,random_state=8)


##生成KNN回归对象
reg = KNeighborsRegressor(n_neighbors=5,weights='uniform',algorithm='auto',p = 2)
#X作为训练数据并使用y作为目标值来拟合模型
reg.fit(X,Y)
# 将预测结果用图像进行可视化
z = np.linspace(-3,3,200).reshape(-1,1)
#预测所提供数据的目标
Y_predict = reg.predict(z)
#返回回归拟合优度
r2 = reg.score(X,Y)
print('拟合优度为:'+ repr(r2))
plt.scatter(X,Y,c='orange',edgecolor='k',label = '实际数据点')
plt.plot(z,Y_predict,c='k',Linewidth=3,label = 'KNN回归预测曲线')
plt.title("K近邻回归")
plt.legend()
plt.show()



脚本运行的输出结果如下:

(machine_learning) D:\CloudMusic\virtualenv\machine_learning\machine>python KNN_regression.py
拟合优度为:0.7721167832505298

回归效果对比图如下:

六、总结

KNN算法的主要优点:

  • 1、简单,易于理解,易于实现,无需估计参数,无需训练
  • 2、适合对稀有事件进行分类(例如当流失率很低时,比如低于0.5%,构造流失预测模型)
  • 3、特别适合于多分类问题(multi-modal,对象具有多个类别标签),多分类情况下kNN比SVM的表现要好
  • 4、近邻算法具有较强的一致性结果。随着数据趋于无限,算法保证错误率不会超过贝叶斯算法错误率的两倍[9]。对于一些K值,K近邻保证错误率不会超过贝叶斯的。

KNN算法的主要缺点:

  • 1、懒惰算法,对测试样本分类时的计算量大,内存开销大,评分慢;
  • 2、可解释性较差,无法给出决策树那样的规则;

适用条件:

  • 1、一般对于多分类问题,而且贝叶斯分类,决策树分类等不好用的情况下才是用这种方法
  • 2、对于大样本的情况下可以尝试采用KNN做对比,毕竟这种算法在数学上不是很严谨

七、参考文章链接和推荐的教程

  • 机器学习之KNN最邻近分类算法
  • KNN算法理解
  • python实现kNN算法(k-近邻算法)
  • 预处理数据的方法总结(使用sklearn-preprocessing)
  • python之sklearn学习笔记
  • sklearn.neighbors.KNeighborsRegressor 函数官方文档
  • KNN算法-维基百科
  • 机器学习算法的随机数据生成
  • sklearn API
  • sklearn官网

你可能感兴趣的:(机器学习算法,KNN,有监督,机器学习)