python实现KNN算法

用python实现KNN算法

算法设计
在使用自带的sklearn实现knn算法时,需要先引入sklearn包中的类,继而再取得knn分类器,使用内置函数调整knn的要素(邻居个数,分类决策规则),,使用fit()对训练集进行训练,调用knn.predict()去预测新输入的类别,调用knn.score()计算预测的准确率。
在使用原生python实现KNN算法时,首先要解决的问题就是计算输入x与训练集各点的距离distance,根据距离排序,取出距离最小的k个点,其次要对新的数据点做出预测,此时类比knn分类器,就需要我们自己定义一个分类器,来对数据点进行分类,x归为多数类。在计算distance时,选择numpy数组可以让程序运算更简洁。
自定义KNN分类器:定义函数KNNClassify(),其中包含的参数应包含newInput:新输入的待分类数据(x_test),dataset:输入的训练数据集(x_train),lables:输入训练集对应的类别标签(y_train),k:近邻数,weight:决策规则。第一步要做的内容时计算分类数据与训练集各数据点的欧式距离。使用numpy的title函数将newInput在列方向上重复1次,行方向重复numSamples次并减去输入的训练数据集,由此求出距离差值,再将该值平方后累加求和。 distance = (squaredist.sum(axis=1)) ** 0.5 其中 axis=1表示按行累加。第二步就是将距离按升序排序,并取距离最近的k个近邻点。使用argsort()方法实现。定义一个空字典,用来存放k个近邻的分类计数。在计数方法上,选择多数表决法或加权表决法。使用for循环求出第i个近邻点在distance数组中的索引,对应的分类。对k个近邻点的分类按降序排序,返回票数最多的分类结果。根据不同的计数法输出对应分类结果。
接下来建立训练集、测试集。将下载好的数据集新建文件,通过pandas的read_csv()来读取数据文件。使用pandas的head( m )函数来读取前m条数据,如果没有参数m,默认读取前五条数据。iloc函数,基于索引位来选取数据集,这里是前闭后开集合。np.random.permutation(len(iris_x))将鸢尾花的长度数据打乱。选取鸢尾花的数据集,测试集,将dataframe格式的数据转换为numpy array格式,便于 调用函数计算,将labels的形状设置为(130,)

源代码:
Final.py

import numpy as np
import operator
import pandas as pd
# newInput: 新输入的待分类数据(x_test),本分类器一次只能对一个新输入分类
# dataset:输入的训练数据集(x_train),array类型,每一行为一个输入训练集
# labels:输入训练集对应的类别标签(y_train),格式为['A','B']而不是[['A'],['B']]
# k:近邻数
# weight:决策规则,"uniform" 多数表决法,"distance" 距离加权表决法

def KNNClassify(newInput, dataset, labels, k, weight):
    numSamples = dataset.shape[0]

    """step1: 计算待分类数据与训练集各数据点的距离(欧氏距离:距离差值平方和开根号)"""
    diff = np.tile(newInput, (numSamples, 1)) - dataset  # 凸显numpy数组的高效性——元素级的运算
    squaredist = diff ** 2
    distance = (squaredist.sum(axis=1)) ** 0.5  # axis=1,按行累加

    """step2:将距离按升序排序,并取距离最近的k个近邻点"""
    # 对数组distance按升序排序,返回数组排序后的值对应的索引值
    sortedDistance = distance.argsort()

    # 定义一个空字典,存放k个近邻点的分类计数
    classCount = {}

    # 对k个近邻点分类计数,多数表决法
    for i in range(k):
        # 第i个近邻点在distance数组中的索引,对应的分类
        votelabel = labels[sortedDistance[i]]
        if weight == "uniform":
            # votelabel作为字典的key,对相同的key值累加(多数表决法)
            classCount[votelabel] = classCount.get(votelabel, 0) + 1
        elif weight == "distance":
            # 对相同的key值按距离加权累加(加权表决法)
            classCount[votelabel] = classCount.get(votelabel, 0) + (1 / distance[sortedDistance[i]])
        else:
            print("分类决策规则错误!")
            print("\"uniform\"多数表决法\"distance\"距离加权表决法")
            break

            # 对k个近邻点的分类计数按降序排序,返回得票数最多的分类结果
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    if weight == "uniform":
        print("新输入到训练集的最近%d个点的计数为:" % k, "\n", classCount)
        print("新输入的类别是:", sortedClassCount[0][0])

    elif weight == "distance":
        print("新输入到训练集的最近%d个点的距离加权计数为:" % k, "\n", classCount)
        print("新输入的类别是:", sortedClassCount[0][0])

    return sortedClassCount[0][0]

iris=pd.read_csv("iris.txt") #读取数据文件
iris.head()

iris_x=iris.iloc[:,[0,1,2,3]] #基于索引位来选取数据集
iris_y=iris.iloc[:,[4]]


indices=np.random.permutation(len(iris_x)) #将鸢尾花的长度数据打乱

iris_x_train=iris_x.iloc[indices[0:130]]
iris_y_train=iris_y.iloc[indices[0:130]]

iris_x_test=iris_x.iloc[indices[130:150]]
iris_y_test=iris_y.iloc[indices[130:150]]

# 将dataframe格式的数据转换为numpy array格式,便于 调用函数计算
iris_x_train=np.array(iris_x_train)
iris_y_train=np.array(iris_y_train)

iris_x_test=np.array(iris_x_test)
iris_y_test=np.array(iris_y_test)

# 将labels的形状设置为(130,)
iris_y_train.shape=(130,)

# if __name__ == '__main__':
#     test_index = 12
#     predict = KNNClassify(iris_x_test[test_index], iris_x_train, iris_y_train, 20, "distance")
#     print(predict)
#     print("新输入的实际类别是:", iris_y_test[test_index])
#     print("\n")
#
#     if predict == iris_y_test[test_index]:
#         print("预测准确!")
#     else:
#         print("预测错误!")

测试用例设计及调试过程截屏
在测试时,定义测试方法类,需要继承unittest.TestCase类,除了setUp()方法(一般用来准备测试环境,在每个测试方法执行之前执行。)及tearDown()方法(一般用来清理测试环境,在每个测试方法执行之后执行,且不论测试方法执行是否成功。)外只需测试自定义的分类器函数,在测试时用到断言函数assertEqual(),其中第一个参数将其设置为预测的鸢尾花种类,第二个参数为使用knn算法计算出的分类结果,最终输出实际类别,根据测试结果看与其预测的结果是否一致。

Test_final.py

Test_final.py
mport unittest
from final import KNNClassify, iris_x_train, iris_y_train, iris_y_test, iris_x_test


class TiestFinal(unittest.TestCase):#定义测试方法类,需要继承unittest.TestCase类

    def setUp(self):
        print('setUp')

    def tearDown(self):
        print('tearDown\n')
    def test_KNNClassify(self):
        print('分类器......')
        #print(predict)
        self.assertEqual('Iris-virginica',KNNClassify(iris_x_test[12], iris_x_train, iris_y_train, 20, "distance"))
        print("新输入的实际类别是:", iris_y_test[12])
        print("\n")


if __name__ == '__main__':
    unittest.main()

调试截屏:
python实现KNN算法_第1张图片
python实现KNN算法_第2张图片
python实现KNN算法_第3张图片
python实现KNN算法_第4张图片
python实现KNN算法_第5张图片
python实现KNN算法_第6张图片
测试截图:由于每次运行时random.permutation()函数都会将鸢尾花数据打乱,所以对于同样的预测值,在不同运行情况下实际的knn分类情况可能不同。此处给出两次运行结果,如图所示:
python实现KNN算法_第7张图片
python实现KNN算法_第8张图片
总结
在本次实验中,我对python中常用的三个库的便利性和高效性深有体会,在使用python编写程序的过程中似乎更重要的是算法的思想及解法,具体的小步骤万能的python都会帮你解决,比如矩阵运算,在求欧式距离中使用numpy数组求得的距离差,及近邻距离排序,一步到位。在下载好鸢尾花数据后,遇到的问题就是怎样将数据读入,后来通过查阅,使用iris=pd.read_csv(“iris.txt”) #读取数据文件,使用iris.head()来读取前m条数据,默认为5.还有就是测试用例的问题。在此之前,包括上学期的所有java实验,在老师要求给出测试用例时我都一概以main()函数去书写,朋友给我发了个java测试用例我才发现根本和main()函数还有差别呢,就如单元测试是用来对最小可测试单元(如一个函数、一个类或者一个模块)进行检查和验证。尤其是在对代码进行修改或重构后,可以快速准确的定位出现问题的问题。通过在网上查询测试用例的写法,在这次实验中我写出了自己的第一个python测试用例。认真对待过后,收获了很多。

你可能感兴趣的:(python实现KNN算法)