基于KNN 算法,实现对于鸢尾花分类
数据集的准备和处理
- 数据集介绍
Iris数据集每个样本x包含了花萼长度(sepal length)、花萼宽度(sepal width)、花瓣长度(petal length)、花瓣宽度(petal width)四个特征。样本标签y共有三类,分别是Setosa,Versicolor和Virginica。Iris数据集总共包含150个样本,每个类别由50个样本,整体构成一个150行5列的二维表。
- 数据集的处理
1.导入 numpy库 和 pandas库提取数据
import numpy as np
import pandas as pd
data = pd.read_csv(r"iris.arff.csv", header=0)
2.对数据集进行处理使得便于进行分析
# 将鸢尾花种类名称映射为数字
data["class"] = data["class"].map({"Iris-versicolor":0,"Iris-setosa":1,"Iris-virginica":2})
# data = data.drop("Id",axis=1,inplace = True) # 删除列
print(len(data))
if data.duplicated().any(): # 重复值
data.drop_duplicates(inplace=True) #删除重复值
print(len(data))
data["class"].value_counts() # 查看各个类别的鸢尾花记录
150
147
0 50
2 49
1 48
Name: class, dtype: int64
KNN分类算法的实现
原理:
1.将Iris数据集分为两部分,构建训练集和测试集。其中后四十条数据设置为测试集,来进行数据的测试和验证。
提取方法:把每个类中的鸢尾花的数据进行洗牌
后按照相同比例放入建训练集和测试集
2.求测试集合的每一个数据到训练集的数据中的欧氏距离,选取距离最近的前k个元素,并取k个元素中对应的类别数组中出现次数最多的类别为判断类别
代码实现:
class KNN:
'''使用python语言实现k邻近算法(实现分类)'''
def __init__(self, k):
'''初始化方法
Parameters
-----
k:int
邻居个位数
'''
self.k = k
def fit(self, X, y):
'''训练方法
Parameeters
-----
X: 类数组类型(特征矩阵),可以是List也可以是Ndarray,形状为: [样本数量,特征数量]
待训练的样本特征(属性)
y: 类数组类型,形状为:[样本数量]
每个样本的目标值(标签)
'''
#将X,y转换为ndarray类型
self.X = np.asarray(X)
self.y = np.asarray(y)
def predict(self, X):
'''根据参数传递的样本,对样本数据进行预测
Parameters:
-----
X: 类数组类型,可以是List也可以是Ndarray,形状为: [样本数量,特征数量]
待测试的样本特征(属性)
Returns:
-----
result :
数组类型,预测结果
'''
#转换为数组类型
X = np.asarray(X)
#保存预测的结果值
result = []
#对ndarray数组进行遍历,每次取数组中的一行
for x in X:
#测试集-训练集 求欧式距离
dis = np.sqrt(np.sum((x - self.X) ** 2, axis = 1))
#返回排序后,每个元素在原数组(排序之前的数组) 中的索引
index = dis.argsort()
#进行截取,只取前k个元素 (取距离最近的k个元素的索引)
index = index[:self.k]
# 返回数组中每个整数元素出现次数,元素必须是非负整数
count = np.bincount(self.y[index])
# 返回ndarray中值最大的元素所对应的索引,就是出现次数最多的索引,也就是我们判定的类别
result.append(count.argmax())
return np.asarray(result)
训练与测试
- 数据集的分类(原理1)
#提取出每个类中的鸢尾花的数据
t0 = data[data["class"]==0]
t1 = data[data["class"]==1]
t2 = data[data["class"]==2]
#对每个类别数据进行洗牌 ,random_state参数指定随机的种子
t0 = t0.sample(len(t0), random_state=0)
t1 = t1.sample(len(t1), random_state=0)
t2 = t2.sample(len(t2), random_state=0)
#构建训练集和测试集 ,axis=0按照纵向方式进行拼接
train_X = pd.concat([t0.iloc[:40, :-1], t1.iloc[:40, :-1], t2.iloc[:40, :-1]], axis=0) #取前40条
train_y = pd.concat([t0.iloc[:40, -1], t1.iloc[:40, -1], t2.iloc[:40, -1]], axis=0)
test_X = pd.concat([t0.iloc[40:, :-1], t1.iloc[40:, :-1], t2.iloc[40:, :-1]], axis=0) #取四十条后
test_y = pd.concat([t0.iloc[40:, -1], t1.iloc[40:, -1], t2.iloc[40:, -1]], axis=0)
- 创建KNN对象,进行训练与测试
#创建KNN对象,进行训练与测试
knn = KNN(k = 3)
#进行训练
knn.fit(train_X,train_y)
#进行测试获,得测试结果
result = knn.predict2(test_X)
display(np.sum(result == test_y))
display(len(result))
26
27
预测了27个数据 与测试合符合26个
预测结果可视化展示
#预测结果可视化展示
import matplotlib as mpl
import matplotlib.pyplot as plt
#设置画布的大小
plt.figure(figsize=(20,10))
#默认情况下,matplotlib不支持中文显示,设置支持中文
#设置字体为黑体,以支持中文显示
mpl.rcParams["font.family"] = "SimHei"
#设置在中文字体时,能够正常的显示负号(-)
mpl.rcParams["axes.unicode_minus"] = False
#绘制训练集数据 "Iris-versicolor":0,"Iris-setosa":1,"Iris-virginica":2
plt.scatter(x = t0["sepallength"][:40], y = t0["petallength"][:40], color = 'r', label = "Iris-versicolor")
plt.scatter(x = t1["sepallength"][:40], y = t1["petallength"][:40], color = 'g', label = "Iris-setosa")
plt.scatter(x = t2["sepallength"][:40], y = t2["petallength"][:40], color = 'b', label = "Iris-virginica")
#绘制测试集数据 "Iris-versicolor":0,"Iris-setosa":1,"Iris-virginica":2
right = test_X[result == test_y]
wrong = test_X[result != test_y]
plt.scatter(x = right["sepallength"], y = right["petallength"], color = 'c', label = "right", marker = "x")
plt.scatter(x = wrong["sepallength"], y = wrong["petallength"], color = 'm', label = "wrong", marker = ">")
#设置散点图参数
plt.xlabel('花萼长度')
plt.ylabel('花瓣长度')
plt.title('KNN分类结果')
plt.legend(loc='best')
plt.show()
样本预测加入权重计算
- 原理:
距离越远权重越小,使用距离的倒数作为权重
- 对KNN类中的predict进行改写
def predict2(self, X):
'''对样本进行预测,加入权重计算(距离越远权重越小,使用距离的倒数作为权重)
Parameters:
-----
X: 类数组类型,可以是List也可以是Ndarray,形状为: [样本数量,特征数量]
待训练的样本特征(属性)
Returns:
-----
result :
数组类型,预测结果
'''
X = np.asarray(X)
result = []
for x in X:
#测试集-训练集 求欧式距离
dis = np.sqrt(np.sum((x-self.X)**2, axis=1))
#返回排序后,每个元素在原数组(排序之前的数组) 中的索引
index = dis.argsort()
#进行截取,只取前k个元素 (取距离最近的k个元素的索引)
index = index[:self.k]
#返回数组中每个整数元素出现次数,元素必须是非负整数 (使用weight考虑权重,权重为距离的倒数。)
count = np.bincount(self.y[index], weights=1/dis[index])
# 返回ndarray中值最大的元素所对应的索引,就是出现次数最多的索引,也就是我们判定的类别
result.append(count.argmax())
return np.asarray(result)