k近邻算法
k近邻(k-Nearest Neighbor)学习是一种常见的监督学习方法,其算法简单、直观:首先寻找与预测数据最近的k个样本数据,然后观察这k个样本数据的标记类别,数量最多的类别就作为预测数据的类别。我们用图表示一下:
如上图,中间绿色的圆点为要预测的数据,样本数据有两种类型,分别用正方形和三角形表示,当k=3时,就如图中实现所围,距离预测点最近的3个点中两个为三角形,一个为正方形,因此此时预测类型与三角形数据一致;当k=5时,看图中虚线所围面积,正方形点有三个,三角形还是两个,此时预测类型就与正方形数据一致。
k值的选择
经过以上的分析,我们可以看出,k近邻算法的结果受k值的影响很大,因此如何选择k值是非常重要的。
若选择较小的k值,就相当于用较小的领域中的样本进行预测(范围小),此时只有与预测数据更近似的样本才能对预测结果起到作用;但如果最近的k个点噪点较多,则预测就很容易出错。选择较小k值会使整体模型变复杂,容易发生过拟合。
若选择较大的k值,这时与预测数据较远的(相似度较低的)样本也会对其影响,整体模型变得更简单,容易预测错误。
在实际应用中,k值一般取一个比较小的值,通常用交叉验证法来选取最优k值。
距离度量
除了k值的选择外,距离度量公式的选择也对分类结果影响重大。
k近邻模型的特征空间一般是n维实数向量空间,因此一般使用欧式距离,但也可使用其他距离。
k近邻算法的优点
- 简单,易于实现;
- 因为找的是最近邻的数据点,因此当某些点数量稀少时,划分越准确,适合对稀有点分类;
- 使用多分类问题。
Python实现
这次我们通过一个案例,逐步实现kNN算法。
项目概述
海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人:1:不喜欢的人;2:魅力一般的人;3:极具魅力的人。她希望:不喜欢的人则直接排除掉;工作日与魅力一般的人约会;周末与极具魅力的人约会。
现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。
开发流程
海伦把这些约会对象的数据存放在文本文件 datingTestSet2.txt 中,总共有 1000 行。海伦约会的对象主要包含以下 3 种特征:
- Col1:每年获得的飞行常客里程数
- Col2:玩视频游戏所耗时间百分比
- Col3:每周消费的冰淇淋公升数
文本文件数据格式如下: ```python 40920 8.326976 0.953952 3 14488 7.153469 1.673904 2 26052 1.441871 0.805124 1 75136 13.147394 0.428964 1 38344 1.669788 0.134296 1…
代码编写
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
data = pd.read_csv(r'datingTestSet2.txt',sep = '\t',header = None)
X = np.array(data.iloc[:,:-1])
y = np.array(data.iloc[:,-1])
# 切分数据
'''
我们可以直接调用sklearn的函数将数据集切分为训练集和测试集
'''
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 将我们自己撰写的分类器中添加评分函数,这就是一个相对完整的分类器了
from collections import Counter
class KNN:
def __init__(self,k,metric ='euclidean'):
pass
self.metric = metric
self.k = k
def train(self,X,y):
self.X_train = np.array(X)
self.y_train = np.array(y)
def predict(self,x_test):
x = np.array(x_test)
(m,d) = np.shape(x)
ypred = np.zeros((m))
#============================= show me your code =======================
y_temp = []
min_indexes = []
for i in range(m):
distance = np.sqrt(np.sum(np.square(self.X_train - X_test[i]),axis = 1)) #求欧式距离
for n in range(self.k):
min_index = np.argmin(distance)
min_indexes.append(min_index)
distance[min_index] = distance.max()
for j in min_indexes: #将最近点的分类存入临时数组
y_temp.append(self.y_train[j])
ypred[i] = y_temp[np.argmax(Counter(y_temp))] # 预测值
min_indexes = []
y_temp = []
#============================= show me your code =======================
return ypred
def score(self,ypred,ytest):
return sum(ypred == ytest)/len(ytest)
# 和sklearn的KNeighborsClassifier算法做比较
#数据标准化
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
X_ = ss.fit(X)
X_std =ss.transform(X)
k_range = range(1, 31)
k_error = []
# 用我们自己撰写的K近邻算法测试数据,
# 用同样的作图法输出每个K值的误差结果。
from sklearn.model_selection import KFold
kf = KFold(n_splits=5,shuffle=False) #将数据集分为互斥的5等份,用作测试
k_errors = [] #建立初始的误差列表
for k in k_range:
knn = KNN(k=k)
scores = []
for train , test in kf.split(X_std,y):
knn.train(X_std[train],y[train])
ypred = knn.predict(X_std[test])
score = knn.score(ypred,y[test])
scores.append(1-score)
k_errors.append(np.mean(scores))
plt.plot(k_range, k_errors)
plt.xlabel('Value of K for KNN')
plt.ylabel('Error')
plt.show()
# 预测一个新数据
knn = KNN(k=5)
knn.train(X_std,y)
# 定义类别对应的标签
resultList = ['不喜欢的人', '魅力一般的人', '极具魅力的人']
#输入数据
ffMiles = float(input("每年获得的飞行常客里程数?"))
percentTats = float(input("玩视频游戏所耗时间百分比?"))
iceCream = float(input("每周消费的冰淇淋公升数?"))
inArr = np.array([[ffMiles, percentTats, iceCream]])
#用之前的fit的标准化数据来转换数据
x_new = ss.transform(inArr)
#预测数据
ypred = knn.predict(x_new)
print("这个人属于: ", resultList[int(ypred) - 1])
参考资料
- 《统计学习方法》.李航
- 《机器学习》.周志华
来源:CSDN
作者:xyc_undermoon
链接:https://blog.csdn.net/xyc_undermoon/article/details/104055994