利用k近邻模型进行鸢尾花分类-Python实现

利用k近邻模型进行鸢尾花分类-Python实现

数据集简介https://www.cnblogs.com/mandy-study/p/7941365.html

K近邻算法

参考书:李航的《统计学习方法》
书上的内容写得比我好得多的多!这里不重复叙述,以下给出算法步骤:

  • step1:计算待预测向量 x t e s t x_{test} xtest与数据集中各个 x i x_{i} xi的"距离" D D D
  • step2:从 D D D中选择 k k k个最小"距离"的数据集中的向量,记 N K ( x ) N_{K}(x) NK(x)
  • step3:对 N K ( x ) N_{K}(x) NK(x)中向量的标签计数,如果标签"apple"计数最多,则
    待测向量 x t e s t x_{test} xtest的标签即为"apple"

距离度量公式

利用k近邻模型进行鸢尾花分类-Python实现_第1张图片

Python实现(部分截图+代码解释)

最终版代码在最后面给出

  1. 从sklearn.datasets模块中加载鸢尾花的数据集。
    利用k近邻模型进行鸢尾花分类-Python实现_第2张图片
  2. 用 df 可以以 dataframe 的结构观察数据,也可以用df.values 换成array结构。
    利用k近邻模型进行鸢尾花分类-Python实现_第3张图片
  3. 先写一个距离度量公式
def f1(x1,x2,p):
    #两向量之间的距离度量
    return sum(np.power(abs(x1-x2),p))**(1/p)
  1. 将任务拆解成3个部分、用4个def完成
  • 完成算法step1内容
    计算1个向量 x 1 x_{1} x1与数据集中所有向量的距离;返回为:数据集中向量的索引,为:待测向量 x 1 x_{1} x1与数据集中对应索引向量的距离的字典
def distance_dict(x1,Training_set,p):
    return {inx:f1(x1,val,p) for inx,val in enumerate(Training_set)}

上述代码中的enumerate()方法可以查阅相关文章。

  • 完成算法step2内容
  1. 对字典的值进行排序,取出k个最小的值;
    返回为:索引值 为:距离值 的字典
def sort_dict(x1,Training_set,p,k):
    #排序dict_distance中最小的k个数据,
    Distance_dict = distance_dict(x1,Training_set,p)
    Sort_Distance_dict = sorted(Distance_dict.items(),key=lambda x:x[1])  #元组形式
    return dict([Sort_Distance_dict[i] for i in range(k)])

上述代码中的第4行可以查阅字典排序的相关文章。

  • 完成算法step3内容
  1. 对上面函数得到的字典进行处理,sort_dict(x1,Training_set,p,k) 得到的结果是字典,而字典的键是对应于Training_set中的索引值。
    我们知道的是在Training_setTraining_lable 中array和list数据结构,定位查找的复杂度是常数级的。
    下面的函数 count_dict(sort_dict,Training_lable) 的目的是利用索引值构造与待测向量 x t e s t x_{test} xtest最接近 k k k个数据向量 的标签计数的字典结构。(通俗点就是对 N K ( x ) N_{K}(x) NK(x)中向量的标签行计数)
#sort_k_dict参数是字典的数据结构
def count_dict1(sort_dict,Training_lable):
    counter = {}
    for i in sort_dict.keys():                #i是该字典的键,即为索引值!
        if Training_lable[i] not in counter:
            counter[Training_lable[i]] = 1
        else:
            counter[Training_lable[i]] += 1
    return counter

关于计数功能,这里给出第二个方法:

#通过collection Counter类 实现相同效果
from collections import Counter
def count_dict2(sort_dict,Training_lable):
    counter = Counter([Training_lable[i] for i in sort_dict.keys()]) 
    return counter
  1. 在字典中找出 值最大所对应的键,即k个标签中占比最高的那个!(通俗点就是如果标签"apple"计数最多,则待测向量 x t e s t x_{test} xtest的标签即为"apple")
#count_dict 参数是字典,键是标签、值是k个标签中对应于键的个数;
def print_count_dict(count_dict):
    max_num = max(count_dict.values())
    key_list = [key for key,value in count_dict.items() if value == max_num]
    if len(key_list) == 1:                #如果k个标签中能确定只有1个标签对应的值最大
        return key_list[0]                #返回该标签
    else:                                 
        return tuple(key_list)    #否则以元组的形式返回多个标签

最后

到目前为止,算法的分解步骤已经完成!只要将上述def进行整理用个def封装起来(class我不会…)。最终版代码如下:(建议用jupyter)

import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
#df.values     查看df的数据
#定义一个KNN函数,将部分中间过程的函数整理一下;直接对 向量x1是属于数据集的哪个标签进行判断,返回标签值;
def f1(x1,x2,p):
    #两向量之间的距离
    return sum(np.power(abs(x1-x2),p))**(1/p)

def KNN(x1,Training_set,Training_lable,p,k):    
    def distance_dict(x1,Training_set,p):
        return {inx:f1(x1,val,p) for inx,val in enumerate(Training_set)}
    
    def sort_dict(x1,Training_set,p,k):
        #排序dict_distance中最小的k个数据,
        Distance_dict = distance_dict(x1,Training_set,p)
        Sort_Distance_dict = sorted(Distance_dict.items(),key=lambda x:x[1])  #元组形式
        return dict([Sort_Distance_dict[i] for i in range(k)])
    
    def count_dict1(sort_dict,Training_lable):
        counter = {}
        for i in sort_dict.keys():                #i是该字典的键,即为索引值!
            if Training_lable[i] not in counter:
                counter[Training_lable[i]] = 1
            else:
                counter[Training_lable[i]] += 1
        return counter
    
    def print_count_dict(count_dict):
        max_num = max(count_dict.values())
        key_list = [key for key,value in count_dict.items() if value == max_num]
        if len(key_list) == 1:                #如果k个标签中能确定只有1个标签对应的值最大
            return key_list[0]                #返回该标签
        else:                                 
            return tuple(key_list)    #否则以元组的形式返回多个标签
        
    Sort_dict = sort_dict(x1,Training_set,p,k)          #计算x1与数据集Trainin_set中所有向量的距离,并返回k个最小的索引,距离值的字典
    Count_dict = count_dict1(Sort_dict,Training_lable)  #将排序好的Sort_dict进行 数据集标签 的计数,返回字典形式
    Print_count_dict = print_count_dict(Count_dict)     #输出标签计数字典中值最大的键,如果最大值有多个,则以元组的形式返回多个标签
    
    return Print_count_dict 

思路
本次实践将0、1、2类标签划分为训练集和测试集两个数据集,其中训练集占比80%(0、1、2类标签数据各40条),测试集占比20%(0、1、2类标签数据各10条)

#处理数据
dataAll = df.values
dataAll_x = np.array([np.delete(i,-1) for i in dataAll])
dataAll_label = np.array([i[-1] for i in dataAll])

这里因为提前知道了鸢尾花数据,所以才这么划分!

#数据集和测试集用np.array的数据结构,而数据标签用列表的数据结构
training_set = np.array(list(dataAll_x[0:40])+list(dataAll_x[50:90])+list(dataAll_x[100:140]))
training_set_lable = list(dataAll_label[0:40])+list(dataAll_label[50:90])+list(dataAll_label[100:140])

test_set = np.array(list(dataAll_x[40:50])+list(dataAll_x[90:100])+list(dataAll_x[140:150]))
test_set_lable = list(dataAll_label[40:50])+list(dataAll_label[90:100])+list(dataAll_label[140:150])

现在判断测试集中的向量是属于哪些类别;
然后把遍历KNN函数得到的结果用列表的结果装起来,将此列表与测试集已知的标签列表进行对比,才有准确率的概念

#KNN算法中的参数:p取2 k取7
KNN_lable_list = [KNN(i,training_set,training_set_lable,2,7)for i in test_set]
KNN_lable_list
num1 = 0
for i,j in zip(KNN_lable_list,test_set_lable):
    if i != j:
        num1 += 1
        print("KNN算法判断的类别与测试标签类别有误\n"+"判断的类别为:"+str(i)+"\n原测试集类别为:"+str(j))
accuracy = (len(test_set_lable)-num1)/len(test_set_lable)
print("准确率:"+str(accuracy))

总结

本人非计算机专业,对于代码的优化、数据结构的应用上做的并不好!而且上面代码写完后发现标签的单词打错了…label打成了lable!
建议
在参考此份代码的同时,一定要把函数def调出来,自己多带几个参数进去玩,同时也要去参考李航《统计学习方法》中的K近邻。

你可能感兴趣的:(python,机器学习,算法,近邻算法)