KNN(K- Nearest Neighbor)近邻算法是最简单的机器学习模型之一。通过计算样本特征间的距离来预测结果。
文章目录
1 KNN算法介绍
1.1 k近邻法原理
常言道,近朱者赤近墨者黑,而KNN就是这样一种分类模型:
第一步:首先需要给定一堆带有标签y_test的数据X_test,作为模型的训练集;
第二步:然后输入没有一条没有标签的新样本,依次计算新样本和训练集X_test所有样本的距离,一般采用欧氏距离或曼哈顿距离。
第三步:取出距离新样本最近的K个训练集样本,用K个样本中所属标签占比最多那一类作为新样本的分类标签。
1.2 KNN优缺点
优点:易于理解实现,精确度相对来说也是比较高的。
缺点:由于要计算大量的数据值,这种模型算法时间复杂度和空间复杂度较高。另外如果训练集的样本标签不平衡,有的标签多,有的标签少,就会导致**输入样本容易被错误的划分到标签多的样本里。**如果出现这类情况,可以采用对K近邻点加权的方式,让距离近的点有较大的权重。
1.3 KNN注意点分析
- 数据量化:KNN模型是计算特征之间的距离,也就是把每个样本当作多维空间的点,计算点与点之间的距离。因此输入的训练集数据需要是数值型的,非数值型数据要进行量化处理。
- 数据归一化:同一个样本有多条特征,每条特征的的数据的值域都不同,就会导致在计算时,值域大的特征更容易影响到计算结果,因此训练集的数据一般要进行归一化处理,确保每一条特征对距离的影响相同。
- K值的选取:K值是新样本的邻居个数,一般不会超过20个。K的选择太低容易欠拟合,太高又容易过拟合,因此可以采用交叉验证的方式来选择合适的K。
2 代码实现
sklearn是基于python语言实现的机器学习工具,其中已经封装好了很多机器学习模型,而且内置了部分数据集提供我们学习使用。
2.1 加载数据
测试的数据来源于Sklearn模块中的鸢尾花数据,调取鸢尾花数据的代码如下所示
import pandas as pd
# 导入内置数据集
from sklearn import datasets
from sklearn.model_selection import train_test_split as split
# 加载鸢尾花数据
iris = datasets.load_iris()
iris_df = pd.DataFrame(iris.data,columns=iris.feature_names)
iris_df['target'] = iris.target
# 分割数据
X_train, X_test, y_train, y_test = split(iris_df.iloc[:,:-1], iris_df.iloc[:, -1])
2.2 调用KNN模块实现knn分类
# 导入knn分类模块
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier() #创建模型
# fit训练数据,predict预测数据
knn1.fit(X_train,y_train)
print(knn1.predict(X_test))
虽然sklearn已经帮我们封装好了很多模型,使用也相当简单,但要真正入门理解机器学习模型,我们就需要尝试自己去封装模型。
2.3 自己封装简单的KNN模型
import numpy as np
class Knnclass(object):
'''
本模型为knn分类模型,构造时需要允许传递的参数如下所示:
n_neighbors:默认为5。表示邻点的个数n,训练时会把距离最短的n个邻居加到列表中,最后出现次数最多的预测结果
p:默认为2,表示计算距离的方式为欧氏距离,设置为1时,则采用曼哈顿距离进行计算
knn不需要训练数据,因此在调用fit函数时,程序只是将训练数据传给对象的x_train和y_train
'''
# 类初始化
def __init__(self, *, n_neighbors=5, p=2):
# 默认邻居个数为5个
self.n_neighbors = n_neighbors
# 默认计算距离方式为p=2表示欧氏距离p=1计算曼哈顿距离
self.p = p
# 将测试数据私有化,只有在调用fit函数时可以进行修改
self._x_train = None
self._y_train = None
# 用于存放测试后的y数据
self._y_test = None
# fit 函数用于训练样本
def fit(self,x_train ,y_train):
self._x_train = x_train
self._y_train = y_train
def predict(self,x_texts):
# 定义空列表用于放置预测后的y值
pre_list = []
for index in x_texts.index:
# 一一计算预测y值并添加到列表中
pre_list.append(self.predict_one(x_texts.loc[index]))
self._y_test = np.array(pre_list)
return np.array(pre_list)
# predict函数用于预测样本
def predict_one(self,x_text):
# 把用于分类邻居加到一个列表里
knn_list = []
for i in self._x_train.index:
# 判断列表中个数不得超过邻居
if len(knn_list) < self.n_neighbors:
# print(x_text-self._x_train.iloc[])
# 计算前self.n_neighbors个特征数据与测试的距离
# ord表示要求的距离1表示曼哈顿,2表示欧氏距离。keepdims表示保留原数组格式,因为只需要求距离的值,因此不保留
dist = np.linalg.norm(x_text-self._x_train.loc[i],ord=self.p,keepdims=False)
# 将距离和y_train的结果一一添加到列表中
knn_list.append((dist,self._y_train.loc[i]))
# 找到列表中距离最长的索引
max_index = knn_list.index(max(knn_list,key=lambda x:x[0]))
else:
# 继续计算剩余特征集的距离,若计算的距离小于knn_list中任意一个,则将其替换
dist = np.linalg.norm(x_text-self._x_train.loc[i],ord=self.p,keepdims=False)
if dist < knn_list[max_index][0]:
knn_list[max_index] = (dist,self._y_train.loc[i]) # 替换
# 最大值的索引更新
max_index = knn_list.index(max(knn_list,key=lambda x:x[0]))
# print(dist,knn_list)
# 将结果y的值和出现的次数分别作为键和值加入字典
dist_dict={}
for j in knn_list:
if j[1] not in dist_dict:
dist_dict[j[1]] = 1
else:
dist_dict[j[1]] += 1
# 找到出现字典中次数最多的结果y:根据值排序并返回元组,升序排序因此获取最后一个元素的值
max_count_y = sorted(dist_dict.items(),key=lambda x:x[1])[-1][0]
return max_count_y
# 计算得分
def score(self,x_test,y_test):
self.predict(x_test)
y_test = y_test.ravel()
result = (y_test == self._y_test)
# 将result中正确的True加入一个列表中,然后用个数除以测试集总个数得到训练的分数
score = len([i for i in result if i])/len(y_test)
return score
2.3 测试
from sklearn.model_selection import train_test_split as split
import pandas as pd
# 导入并加载鸢尾花数据集
from sklearn import datasets
iris = datasets.load_iris()
# 将数据集转成dataframe数据表
iris_df = pd.DataFrame(iris.data,columns=iris.feature_names)
iris_df['target'] = iris.target
# 分割数据
X_train, X_test, y_train, y_test = split(iris_df.iloc[:,:-1], iris_df.iloc[:, -1])
# 打印真实y数据
print('真实结果',y_test.ravel())
print('-'*30)
from KnnClass import Knnclass # 导入knn分类模型
knn = Knnclass()
knn.fit(X_train,y_train)
# knn训练后的结果
# print('KNN预测后的结果',knn.predict(X_test))
print('KNN预测后的分数',knn.score(X_test,y_test))
print('-'*30)
# 导入官方knn模型
from sklearn.neighbors import KNeighborsClassifier
# knn训练后的结果
knn1 = KNeighborsClassifier()
knn1.fit(X_train,y_train)
# print('官方KNN预测数据',knn1.predict(X_test))
print('官方KNN预测分数',knn1.score(X_test,y_test))
下面是测试的结果
真实结果 [0 0 1 2 2 1 1 1 2 1 1 1 2 2 0 0 0 1 2 0 1 0 2 2 0 2 0 2 0 1 1 2 1 0 0 0 0
2]
------------------------------
KNN预测后的结果 [0 0 1 2 2 1 1 1 2 2 1 1 2 2 0 0 0 1 2 0 1 0 1 2 0 2 0 2 0 1 1 2 1 0 0 0 0
2]
KNN预测后的分数 0.9473684210526315
------------------------------
官方KNN预测数据 [0 0 1 2 2 1 1 1 2 2 1 1 2 2 0 0 0 1 2 0 1 0 1 2 0 2 0 2 0 1 1 2 1 0 0 0 0
2]
官方KNN预测分数 0.9473684210526315