KNN算法
一,问题分析
KNN最邻近分类算法的实现原理:为了判断未知样本的类别,以所有已知类别的样本作为参照,计算未知样本与所有已知样本的距离,从中选取与未知样本距离最近的K个已知样本,再以k个样本中占多数的类别作为预测结果。
算法中关键的点是“距离”的计算和占多数类的判断。距离的计算,本次采用欧氏距离,以二维平面为例,这里的“距离”在数学上即为两点的直线距离,公式如下:
三维或更高维在此基础上扩展即可。如三维即有三个特征的样本,公式变为
要完成knn算法的实现,参考课本鸢尾花预测模型的实现,主要做以下几个事情:
1,定义鸢尾花数据集my_iris_datasets
2,定义数据集拆分函数my_train_test_split
3, 定义计算欧式距离的函数
4,自己定义一个分类器
4,定义fit函数
5,定义计算精确度的函数score
二,程序设计
1,数据集的定义
加载下载好的iris数据文本文件,将文件内的数据存入列表,存入列表内的元素仍为列表,包含鸢尾花的4个特征和所属种类。加载需用到csv模块
2,拆分函数
将加载的数据集拆成两部分(训练集合数据集),做法是遍历数据集,利用随机数生成函数生成0到1的小数,判断和给定的某个拆分比例值得大小,小则将样本放在训练集train_set,否则放在测试集test_set,需要导入random模块
3,欧式距离计算函数
这一部分通过给定两个样本实例,和样本特征个数,循环样本的特征数次,循环体内用公式计算欧式距离即可,最后把每个维度的差的平方和开方返回即可。
4,KNNclassifier类的定义
参考书本,在构造函数内定义训练集列表train_set和测试集列表test_set,邻近数K,将参数n_neighbors赋值给K,定义fit函数,对于KNN算法,要做的就是将数据集赋给训练集,
定义评估函数score。
5,模型评估函数
评估模型需要遍历测试集的数据,每一个样本计算和训练集样本的欧氏距离进行计算后,选取最邻近的k个,放入列表中,判断类别最多的作为预测结果,和已知标签比对,最终得到正确率。
三,代码实现
# import pdb
import math
import csv
import operator
import random
import numpy as np
# pdb.set_trace()
# 加载鸢尾花卉数据集:给定路径,将文件数据存入列表
def loadIrisDataset(filename):
with open(filename, 'rt') as csvfile:
lines = csv.reader(csvfile)
dataset = list(lines)
for x in range(len(dataset)):
for y in range(4):
dataset[x][y] = float(dataset[x][y])
return dataset
# 拆分数据集,返回训练集和测试集两个列表 :
#dataset(要拆分的数据集) split(训练集所占比例) trainingSet(训练集) testSet(测试集)
def splitDataSet(dataSet, split ):
trainingSet=[]
testSet = []
for x in range(len(dataSet)):
if random.random() <= split:
trainingSet.append(dataSet[x])
else:
testSet.append(dataSet[x])
print(len(trainingSet))
print(len(testSet))
return trainingSet,testSet
# 计算欧氏距离 dimension:维度,即样本特征数
def euclideanDistance(sample1, sample2, dimension):
distance = 0
for x in range(dimension):
distance += pow((sample1[x] - sample2[x]), 2)
return math.sqrt(distance)
# 选取距离最近的K个实例,以某一个测试集样本为参照,与训练集中每一个样本求欧氏距离
def getNeighbors(trainingSet, testSample, k):
distances = []
#求取样本特征数,减1是因为样本最后一个数值为样本标签,即样本所属类别
demension = len(testSample) - 1
for x in range(len(trainingSet)):
dist = euclideanDistance(testSample, trainingSet[x], demension)
distances.append((trainingSet[x], dist))
distances.sort(key=operator.itemgetter(1))
#选取前k个距离
neighbors = []
for x in range(k):
neighbors.append(distances[x][0])
return neighbors
# 获取距离最近的K个实例中占比例较大的分类
def chooseNearest(neighbors):
classVotes = {}
for x in range(len(neighbors)):
response = neighbors[x][-1]
if response in classVotes:
classVotes[response] += 1
else:
classVotes[response] = 1
sortedVotes = sorted(classVotes.items(), key=operator.itemgetter(1), reverse=True)
return sortedVotes[0][0]
# 计算准确率
def getAccuracy(testSet, predictions):
correct = 0
for x in range(len(testSet)):
if testSet[x][-1] == predictions[x]:
correct += 1
return (correct / float(len(testSet)))
class my_KNNClassifier:
def __init__(self,n_neighbors):
self.train_set=[]
self.test_set=[]
self.k = n_neighbors
def fit(self,train_set,test_set):
self.train_set = train_set
self.test_set = test_set
def score(self):
predictions = []
for x in range(len(test_set)):
neighbors = getNeighbors(train_set, test_set[x], self.k)
result = chooseNearest(neighbors)
predictions.append(result)
accuracy = getAccuracy(test_set, predictions)
print("Accuracy:{:.2f}".format(accuracy))
if __name__=='__main__':
# 使用鸢尾花卉数据集进行分类
dataSet = loadIrisDataset(r'E:\\Download\\iris_dataset.txt')
train_set,test_set = splitDataSet(dataSet, 0.75)
classifier = my_KNNClassifier(n_neighbors=1)
classifier.fit(train_set,test_set)
classifier.score()
四,程序调试和运行
此次是在juputer notebook环境下运行,首次运行时,对读取的文件数据处理没有到位,出现下面的错误:
错误原因是加载得到的数据集里的元素是NoneType类型,还需要把数据部分转化为实数。解决办法是将所有样本的数据部分实数化,如下图:
其他还出现了拼写等小错误,并未列举,运行结果如下:
5 .总结
通过本次KNN算法的简单实现,对算法有了更加深刻的认识,KNN的分类过程关键是算待测样本和已有数据之间的一个“距离”,以及挑出最小的几个。本次采用了欧式距离,其他还有什么Mahalanobis距离,Bhattacharyya距离等,算法简单易懂,但这也是算法的缺点,要遍历已有数据样本,得到n个距离,再找最近的k个,还要找最多的标签,最终对样本标签做出预测。