定义:k-近邻算法(k-NearestNeighbor,kNN),顾名思义,即由某样本k个邻居的类别来推断出该样本的类别。给定测试样本,基于特定的某种距离度量方式找到与训练集中最接近的k个样本,然后基于这k个样本的类别进行预测。
实验内容:
待预测未知数据:
X1=[[1.5 , 3 , 5.8 , 2.2], [6.2 , 2.9 , 4.3 , 1.3]]
实现代码:
import numpy as np
import operator
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import *
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier #neighbors有两个类,一个是分类器,一个是回归器
(2)加载数据集
iris = load_iris() #加载数据集
X = iris.data #X
Y = iris.target #Y
# print(X.shape) #查看维度
# print(Y.shape)
plt.figure()
plt.scatter(X[:,1],X[:,3], c=Y,s=40,cmap=plt.cm.Spectral) #展示iris一三列数据集
plt.show()
#划分数据集,数据集划分为测试集占20%;
x_train, x_test, y_train, y_test = train_test_split(
iris.data, iris.target,test_size=0.2)
两种方法: 1.自己写KNN算法 2.调用Sklearn中的KNeighborsClassifier直接得到KNN分类模型
方法一:
(4)手写KNN算法
# 手写kNN算法,返回k个邻居的类别和得到的测试数据的类别
def KNN(x_train,y_train,x_test,n_neighbors=5):
# 计算欧氏距离
numSamples = x_train.shape[0] # shape[0] 表示行数
distances = [] #存放欧式距离的集合
for i in range(len(x_test)): # 多个样本,对每个test样本进行计算,也即x_test中的每一行计算
diff = tile(x_test[i], (numSamples, 1)) - x_train # 计算元素属性值的差,这里利用numpy的属性,直接将x_test的每一个样本直接计算减去x_train得到x_test[i]与x_train中每个样本的差
#注意 x_test[i]维度为[1,4],x_train维度为[120,4],利用tile函数将x_test[i]扩大为维度为[120,4]的数组,然后才能和x_train做减运算
squaredDiff = diff ** 2 #计算差值平方和
squaredDist = sum(squaredDiff, axis = 1) # 按行求和
distance = squaredDist ** 0.5 #开方
distances.append(distance) #得出每一个样本即x_test[i]的distance
# 对距离进行排序
# argsort() 返回按照升序排列的数组的索引
sortedDistIndiceses = argsort(distances) #得到升序的数组索引序列
classCounts = [] #统计所有样本的前k个最短距离的字典,注意里面的每一项是一个x_test样本的字典
for sortedDistIndices in sortedDistIndiceses:
classCount = {} # 定义字典,字典里面包含了前k个最短距离,y值的分布
voteLabels = {} #定义字典,存储前k个最短距离中y的值
for i in range(n_neighbors):
# 选择前k个最短距离
voteLabel = y_train[sortedDistIndices[i]] #记录y值
voteLabels[i] = voteLabel #存入字典中,可以输出查看
# 累计标签出现的次数
# 如果在标签在字典中没有出现的话, get()会返回0
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
#print("k个邻居的类别分别为:"+str(voteLabels))
classCounts.append(classCount) #每个样本的统计
#print(classCounts)
# 返回得到的投票数最多的分类
result = [] #存放最终返回的结果,即每个样本的预测值
for classCount in classCounts: #每个样本
maxCount = 0 #出现的最大次数
for key, value in classCount.items():
if value > maxCount: #找出出现最多的那个y,即找出离样本最近最多的y值是哪个
maxCount = value
maxIndex = key
result.append(maxIndex)
return result #返回结果
(5)计算准确率函数
def KNN_acuracy(result,y_test):
Accuracy_score = list(np.array(result)-y_test).count(0)/len(result) #计算准确率
return Accuracy_score
(6)通过测试集评价模型的准确率
results = KNN(x_train,y_train,x_test)
print(results)
accuracy_score = KNN_acuracy(results,y_test)
print("KNN算法的准确率为:"+str(accuracy_score))
#[0, 0, 2, 2, 1, 1, 1, 2, 0, 0, 0, 1, 0, 2, 2, 0, 2, 2, 1, 2, 0, 1, 1, 2, 1, 1, 2, 0, 1, 0]
#KNN算法的准确率为:1.0
(7)预测结果
X1 = [[1.5, 3, 5.8, 2.2], [6.2, 2.9, 4.3, 1.3]]
predict = KNN(x_train,y_train,X1)
X1_classes = [load_iris().target_names[idx] for idx in predict]
print("两个鸢尾花的预测结果为:[1.5, 3, 5.8, 2.2] :{0}, [6.2, 2.9, 4.3, 1.3] :{1}".format(*X1_classes))
#两个鸢尾花的预测结果为:[1.5, 3, 5.8, 2.2] :virginica, [6.2, 2.9, 4.3, 1.3] :versicolor
方法二:
(8)通过sklearn库中的KNeighborsClassifier直接得到KNN分类模型
#通过sklearn库中的KNeighborsClassifier验证一下结果
X, y = load_iris(return_X_y=True)
# 按照训练集: 测试集 = 8: 2划分数据集
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2)
# 定义模型
model = KNeighborsClassifier(n_neighbors=5)
# 用训练集拟合模型
model.fit(X_train,y_train)
# 得到测试集的准确率
test_accuracy_score = model.score(X_test,y_test)
print("鸢尾花测试集中,模型Accuracy 为: %f" % (test_accuracy_score))
# 加载预测的数据X1
X1 = [[1.5, 3, 5.8, 2.2], [6.2, 2.9, 4.3, 1.3]]
# 用模型进行预测并得到预测结果
predict = model.predict(X1)
# 获得X1中两个鸢尾花预测得到的索引所对应的类别
X1_classes = [load_iris().target_names[idx] for idx in predict]
print("两个鸢尾花的预测结果为:[1.5, 3, 5.8, 2.2] :{0}, [6.2, 2.9, 4.3, 1.3] :{1}".format(*X1_classes))
#鸢尾花测试集中,模型Accuracy 为: 1.000000
#两个鸢尾花的预测结果为:[1.5, 3, 5.8, 2.2] :virginica, [6.2, 2.9, 4.3, 1.3] :versicolor
2.改进模型,数据集划分采用10折交叉验证,并寻找最优的K值(在5-10之间)
(1)导入库
from sklearn.model_selection import cross_val_score #导入K折交叉验证模块
from sklearn.datasets import load_iris #导入iris数据集
from sklearn.neighbors import KNeighborsClassifier #导入K近邻分类算法
(2)加载数据集
iris = load_iris() #加载数据集
# 加载鸢尾花数据集的数据和标签
X = iris.data
y = iris.target
(3)数据集划分采用10折交叉验证,寻找最优的K值
# 定义迭代次数和超参数n_neighbors
epochs = 6
n_neighbors = 5
# 定义用于记录最佳准确率、最佳模型和最佳n_neighbors的参数
best_score = 0
best_model = None
best_n = 5
# 进行迭代
for iter in range(epochs):
# 定义当前的KNN模型
cur_model = KNeighborsClassifier(n_neighbors=n_neighbors)
# 训练模型
model = cur_model.fit(X,y)
cur_score = cross_val_score(cur_model, X, y, cv=10).mean()
# 判断得分是否超过最佳得分, 如果是则替换最佳模型
if cur_score > best_score:
best_n = n_neighbors
best_model = cur_model
best_score = cur_score
print("n_neighbors {0}, 10折交叉验证的平均准确率为{1}".format(n_neighbors, cur_score))
# 每次迭代n_neighbors += 1
n_neighbors += 1
print("最佳模型的n_neighbors 为 {0}, 10折交叉验证的平均准确率为 {1}".format(best_n,best_score))
(4)进行预测
# 定义用于测试的数据
X1 = [[1.5, 3, 5.8, 2.2], [6.2, 2.9, 4.3, 1.3]]
# 用最佳模型进行预测并得到预测结果
predict = best_model.predict(X1)
# 获得X1中两个鸢尾花预测得到的索引所对应的类别
X1_classes = [load_iris().target_names[idx] for idx in predict]
print("两个鸢尾花的预测结果为:[1.5, 3, 5.8, 2.2] :{0}, [6.2, 2.9, 4.3, 1.3] :{1}".format(*X1_classes))
#两个鸢尾花的预测结果为:[1.5, 3, 5.8, 2.2] :virginica, [6.2, 2.9, 4.3, 1.3] :versicolor