k近邻学习
多摘自西瓜书
1.概述
k k k 近邻 ( k k k-Nearest Neighbor, KNN) 学习是一种常用的监督学习方法,其工作机制简单:
给定测试样本,基于某种距离度量找出训练集中与之最靠近的 k k k 个训练样本,然后基于这 k k k 个“邻居”的信息进行预测。通常,在分类任务中可以使用“投票法”,及选择这 k k k 个样本中出现最多的类别标记作为预测结果;在回归任务中可以使用“平均法”,即将这 k k k 个样本的实值输出标记的平均值作为预测结果;还可以基于距离远近进行加权平均或加权投票,距离越近的样本权重越大。
KNN学习是“懒惰学习” (lazy learning) 的著名代表,此类学习技术在训练阶段仅仅将样本保存起来,训练时间开销为零,待收到测试样本后再进行处理。与之相反,再训练阶段就对样本进行学习处理的方法成为“急切学习” (eager learning).
显然, k k k 是一个重要的超参数,当 k k k 取不同值时,分类结果也会有显著不同。另一方面,若采用不同的距离计算方式,则找出的“近邻”可能有显著差别,从而也会导致分类结果有显著不同。
2.距离度量
以下摘自(https://blog.csdn.net/weixin_45884316/article/details/115221211)
特征空间中两个实例点之间的距离是二者相似程度的反应,所以 k k k 近邻算法中一个重要的问题是计算样本之间的距离,以确定训练样本中哪些样本与测试样本更加接近。
2.1欧式距离
2.2曼哈顿距离
2.3其他
当已有距离度量方法不能满足需求时,可以探索符合需求的距离度量方法。
实际的数据中,往往是离散型变量和连续型变量同时存在,如何计算这种混合变量下的样本相似度是一个开放性的问题。一种简单的方法是,在进行距离计算之前对样本中的离散型变量进行One-Hot编码,然后选取上述介绍的距离计算方法进行处理。
3.KNN简单实现
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# 手写KNN算法 (分类任务)
class KNN():
def __init__(self, k=5):
self.k = k
def fit(self, xtrain, ytrain): # 懒惰学习,保存训练数据
self.xtrain = xtrain
self.ytrain = ytrain
# 构造距离计算函数
def cal_distance(self, x):
# x为一个样本
# 欧氏距离
distance = np.sqrt(np.sum(np.power((np.mat(self.xtrain) - np.mat(x)), 2), axis=1)) # 广播机制
return distance
# 根据 k 投票法
def k_vote(self, distance):
index = np.argsort(distance, axis=0)[:self.k]
labels = self.ytrain[index]
labels = [i[0] for i in labels] # 将多个嵌套array转换成list便于处理
label = pd.Series(labels).value_counts().idxmax()
return label
# 构造预测函数
def predict(self, xtest):
y_pred = []
for i in xtest:
distance = self.cal_distance(i)
label = self.k_vote(distance)
y_pred.append(label)
return y_pred
# 构造打分score函数
def score(self, xtest, ytest):
y_pred = self.predict(xtest)
from sklearn.metrics import accuracy_score
return accuracy_score(ytest, y_pred)
if __name__ == '__main__':
# 读取数据集
data = load_breast_cancer()
# DateFrame格式显示
X = data.data
y = data.target
name = ['平均半径', '平均纹理', '平均周长', '平均面积',
'平均光滑度', '平均紧凑度', '平均凹度',
'平均凹点', '平均对称', '平均分形维数',
'半径误差', '纹理误差', '周长误差', '面积误差',
'平滑度误差', '紧凑度误差', '凹度误差',
'凹点误差', '对称误差',
'分形维数误差', '最差半径', '最差纹理',
'最差的边界', '最差的区域', '最差的平滑度',
'最差的紧凑性', '最差的凹陷', '最差的凹点',
'最差的对称性', '最差的分形维数', '患病否']
data = np.concatenate((X, y.reshape(-1, 1)), axis=1)
table = pd.DataFrame(data=data, columns=name)
X = table.iloc[:, :-1] # 特征
y = table.iloc[:, -1] # 标签
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size=0.3, random_state=420)
clf = KNN()
clf.fit(np.array(Xtrain), np.array(Ytrain))
acc = clf.score(np.array(Xtest), np.array(Ytest))
print(acc)
"""
0.9181286549707602
"""
4.sklearn库实现
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
#读取数据集
data = load_breast_cancer()
#DateFrame格式显示
X = data.data
y = data.target
name = ['平均半径','平均纹理','平均周长','平均面积',
'平均光滑度','平均紧凑度','平均凹度',
'平均凹点','平均对称','平均分形维数',
'半径误差','纹理误差','周长误差','面积误差',
'平滑度误差','紧凑度误差','凹度误差',
'凹点误差','对称误差',
'分形维数误差','最差半径','最差纹理',
'最差的边界','最差的区域','最差的平滑度',
'最差的紧凑性','最差的凹陷','最差的凹点',
'最差的对称性','最差的分形维数','患病否']
data=np.concatenate((X,y.reshape(-1,1)),axis=1)
table=pd.DataFrame(data=data,columns=name)
X = table.iloc[:,:-1] # 特征
y = table.iloc[:,-1] # 标签
# 划分测试集和训练集(Xtrain,Xtest,Ytrain,Ytest)
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size = 0.2,random_state=420)# random_state 随机种子 随机取样
#建模
clf = KNeighborsClassifier(n_neighbors=5)
clf = clf.fit(Xtrain,Ytrain)
score = clf.score(Xtest,Ytest)
print(score)
'''
0.9385964912280702
'''
5.绘制学习曲线确定最优超参数 k k k
import matplotlib.pyplot as plt
score = [] # 得分列表
k = range(1,20) # 设置k范围
# 遍历k分别计算得分
for i in k:
clf = KNeighborsClassifier(n_neighbors=i)
clf = clf.fit(Xtrain,Ytrain)
score.append(clf.score(Xtest,Ytest))
plt.plot(k,score)
_ = plt.xticks(range(1,20)) # x轴刻度
plt.show
仅作学习笔记使用,侵删