机器学习入门笔记07-k近邻

Deadly 提交于 2020-01-20 22:24:28

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近邻算法的优点

  1. 简单,易于实现;
  2. 因为找的是最近邻的数据点,因此当某些点数量稀少时,划分越准确,适合对稀有点分类;
  3. 使用多分类问题。

Python实现

这次我们通过一个案例,逐步实现kNN算法。

项目概述

海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人:1:不喜欢的人;2:魅力一般的人;3:极具魅力的人。她希望:不喜欢的人则直接排除掉;工作日与魅力一般的人约会;周末与极具魅力的人约会。

现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。

开发流程

海伦把这些约会对象的数据存放在文本文件 datingTestSet2.txt 中,总共有 1000 行。海伦约会的对象主要包含以下 3 种特征:

  1. Col1:每年获得的飞行常客里程数
  2. Col2:玩视频游戏所耗时间百分比
  3. 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])

 

参考资料

  1. 《统计学习方法》.李航
  2. 《机器学习》.周志华
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!