我的机器学习之路-KNN算法
一、 K近邻算法
k-近邻(k-Nearest Neighbour,简称KNN)是一种基本分类与回归方法,属于是有监督学习中的分类算法。
二、 算法介绍
2.1 思路
k近邻算法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类。k近邻法假设给定一个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其k个最近邻的训练实例的类别,通过多数表决等方式进行预测。因此k近邻法不具有显示的学习过程。k近邻法实际上利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”。
2.2 距离计算
特征空间中两个实例点的距离是两个实例点相似程度的反映。k近邻模型的特征空间一般是n维实数向量空间Rn。使用的距离是欧式距离,也可以是其它距离。
2.2.1 数学公式
欧拉公式:
二维空间:
如果在三维空间,就多了个z轴:
在整个特征空间中可能维度远不止3维,那么更加方便的写法就是X加下标用来表示特征的维度,N为空间的公式:
最后化简:
三、Python实现
3.1 测试数据集
选取sklearn库中的鸢尾花卉数据集(iris),iris是一类多重变量分析的数据集。数据集包含150个数据样本,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性,预测鸢尾花卉属于(Setosa,Versicolour,
Virginica)三个种类中的哪一类。
3.2 数据预处理
选取花萼的长度和宽度作为特征,总共选取20个数据点作为数据集,对应的类别分别用0和1表示。
from sklearn.datasets import load_iris
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
iris = load_iris()# 返回的是Bunch对象 与字典非常类似 包含键和值
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
plt.scatter(df[0:10]['sepal length'], df[:10]['sepal width'], label='0')
plt.scatter(df[70:80]['sepal length'], df[70:80]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
# plt.show()
通过matplotlib画图可以看到:
分别选取十个不同类的鸢尾花数据,总共20个数据把它们作为接下来的数据集合:
data1 = np.array(df.iloc[0:10, [0, 1, -1]])
data2 = np.array(df.iloc[70:80, [0, 1, -1]])
data = np.append(data1,data2,axis=0) # 合并两个narray数组
再将数据分成训练集和测试集:
X, y = data[:,:-1], data[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
3.3 KNN算法实现
from collections import Counter
import numpy as np
class KNN_Model():
def __init__(self,X_train,y_train,n_neighbors = 3,p = 2):
"""
:param X_train: 训练集
:param y_train: 训练集
:param n_neighbors: 临近点数量 此处默认为3
:param p: 距离度量 此处默认使用欧式距离
"""
self.X_train = X_train
self.y_train = y_train
self.neighbors = n_neighbors
self.p = p
def prediction(self,X):
knn_list = []
# 首先选取前3个点进行计算
for i in range(self.neighbors):
# 计算 待分类点与训练集中数据的欧式距离
distance = np.linalg.norm(X - self.X_train[i],ord=self.p)
# 将距离 和 对应的类别存在列表中
knn_list.append((distance,self.y_train[i]))
# 此时knn_list的数据如下
# 每个元组里面 前一个数据为距离 后一个数据为对应的类别
# [(0.8944271909999156, 1.0), (2.209072203437452, 1.0), (0.3999999999999999, 0.0)]
for i in range(self.neighbors,len(self.X_train)):
# 首先max函数里面选取出 knn_list 里面最大的距离数据
# 然后取出它的索引存在 max_index
max_index = knn_list.index(max(knn_list, key=lambda x: x[0]))
# 继续计算待分类点与其他训练集数据的欧式距离
distance = np.linalg.norm(X - self.X_train[i],ord=self.p)
# 每次循环迭代knn_list的数据 目标是找到距离小的点
# 如果新的点比之前列表中的距离要小 那么就添加进去 因为比较的是最大点
# 要做的就是把距离大的点从列表中剔除出去
if knn_list[max_index][0] > distance :
knn_list[max_index] = (distance,self.y_train[i])
# 解析列表 把对应的类别放入新的列表
# 此处数据形式
# [1.0, 1.0, 1.0]
knn = [k[-1] for k in knn_list]
# Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key
# 此处就是为了统计每个类别出现的次数,好方便后面最终确定待分类点属于哪一类
# 对应数据格式
# Counter({1.0: 3})
count_pairs = Counter(knn)
# 最后进行排序 得出出现次数最多的那个类别 作为最终的分类结果
# 因为 sorted返回的数据格式是列表形式 所以需要进行索引
# 如 [1.0]
max_count = sorted(count_pairs, key=lambda x: x)[-1]
return max_count
def score(self,X_test,y_test):
# 分类正确的次数 初始为 0
right_count = 0
for X, y in zip(X_test, y_test):
label = self.prediction(X) # 调用预测模型 得出分类标签
# print("预测类别为:{}".format(label))
# print("实际类别为: {}".format(y))
# print("")
if label == y: # 如果分类正确
right_count += 1 #次数 +1
# 返回最终分类结果表现 正确次数/测试数据集总数
return right_count / len(X_test)
3.4 模型评估
使用之前处理好的鸢尾花数据来进行测试:
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import Statistical_learning_method.Chapter_3.MyFunction as Myfun
from Statistical_learning_method.Chapter_3.DataSet_1 import data as source
# from Statistical_learning_method.Chapter_3.KNN import KNN as the_old_knn
data = source
X, y = data[:,:-1], data[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# print("X_train:\n{}".format(X_train))
# print("")
# print("X_test : \n{}".format(X_test))
# print("")
# print("y_train:\n{}".format(y_train))
# print("")
# print("y_test:\n{}".format(y_test))
# clf = Myfun.KNN_Model(X_train, y_train)
# clf = the_old_knn(X_train,y_train)
# 调用sklearn库封装的KNN算法
clf_sk = KNeighborsClassifier()
clf_sk.fit(X_train, y_train)
print(clf_sk.score(X_test,y_test))
参考资料: 李航<<统计学习方法>>