机器算法(三)---KNN K近邻(k-nearest neighbors)---阿里云天池

一.
1.KNN(K-Nearest Neighbor)最邻近分类算法是数据挖掘分类(classification)技术中最简单的算法之一,其指导思想是”近朱者赤,近墨者黑“,即由你的邻居来推断出你的类别。

2.KNN建立过程
(1) 给定测试样本,计算它与训练集中的每一个样本的距离。
(2)找出距离近期的K个训练样本。作为测试样本的近邻。
(3) 依据这K个近邻归属的类别来确定样本的类别。
3.类别的判定
①投票决定,少数服从多数。取类别最多的为测试样本类别。
②加权投票法,依据计算得出距离的远近,对近邻的投票进行加权,距离越近则权重越大,设定权重为距离平方的倒数。

4.K这个字母的含义就是要选取的最邻近样本实例的个数,在 scikit-learn 中 KNN算法的 K 值是通过 n_neighbors 参数来调节的,默认值是 5。

5.由于KNN最邻近分类算法在分类决策时只依据最邻近的一个或者几个样本的类别来决定待分类样本所属的类别,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。

6.KNN的应用
KNN即能做分类又能做回归, 还能用来做数据预处理的缺失值填充。由于KNN模型具有很好的解释性,一般情况下对于简单的机器学习问题,我们可以使用KNN作为 Baseline,对于每一个预测结果,我们可以很好的进行解释。推荐系统的中,也有着KNN的影子。例如文章推荐系统中, 对于一个用户A,我们可以把和A最相近的k个用户,浏览过的文章推送给A。

二.算法实战
1.二维数据集–knn分类(数据可视化相关注释见代码)

#1.相应库的导入

import numpy as np 

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

from sklearn.neighbors import KNeighborsClassifier   #KNN近邻分类器
from sklearn import datasets                         #从sklearn中的detasets中选取iris数据集
#2.数据导入(二维数据集)

iris = datasets.load_iris()
X = iris.data[:,:2]            #我会忘掉.data    #选取X4个特征里的前两个
y = iris.target
#!!!
#数据可视化&模型预测(数据可视化是个问题!)

k_list = [1, 3, 5, 8, 10, 15]
h = .02

# 创建不同颜色的画布
cmap_light = ListedColormap(['orange', 'cyan', 'cornflowerblue'])
cmap_bold = ListedColormap(['darkorange', 'c', 'darkblue'])

plt.figure(figsize=(15,14))

# 根据不同的k值进行可视化
for ind,k in enumerate(k_list):       #ind为索引,k是列表值
    
    clf = KNeighborsClassifier(k)      #KNN分类器模型
    clf.fit(X, y)
    
    # 画出决策边界
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1    #x来表示X中第一列即第一个特征
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1    #y来表示X中第二列即第二个特征
    
    
    #np.meshgrid 从坐标向量返回坐标矩阵
    #例如:import numpy as np    x = np.arange(-2, 2)    y = np.arange(0, 5)    m, n =np.meshgrid(x, y)
    #m返回结果:                                                     n返回结果:
     #array([[-2, -1,  0,  1],                   array([[0,  0,  0,  0],
     #      [-2, -1,  0,  1],                         [1,  1,  1,  1],
    #       [-2, -1,  0,  1],                         [2,  2,  2,  2],
    #       [-2, -1,  0,  1],                         [3,  3,  3,  3],
     #      [-2, -1,  0,  1]])                        [4,  4,  4,  4]])
    #由此可见, np.meshgrid函数将参数1当做第1个结果的每一行, 并且一共有参数2的长度个行。同时, 第2个结果的每一列为参数2的内容, 并且重复参数1的长度个列。(当然, meshgrid的参数并不受限, 甚至可以得到任意N维空间中的坐标矩阵
#np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。
#np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等
#ndarray.ravel()    ndarray.flatten()    都是将多维数组降为一维, 两者的区别在于ravel返回的是一个view, 而flatten返回的是一个copy 。
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    #np.arange():函数返回一个有终点和起点的固定步长的排列
    ##一个参数 默认起点0,步长为1 输出:[0 1 2] a = np.arange(3)#两个参数 默认步长为1 输出[3 4 5 6 7 8]a = np.arange(3,9)
    #三个参数 起点为0,终点为3,步长为0.1 输出[ 0.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1.   1.1  1.2  1.3  1.4 1.5  1.6  1.7  1.8  1.9  2.   2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9]
    #a = np.arange(0, 3, 0.1)

    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    # 根据边界填充颜色
    Z = Z.reshape(xx.shape)

    
    ## 整个Figure会被划分为numRows行,numCols列,并从左往右,从上往下编号为1,2…。也就是说plotNum指定了子图所在的位置。
    # 此外,如果三个参数的都小于10,则可以简写在一起,例如:subplot(4,3,2)也可以写成subplot(432)
    # 例如:plt.subplot(222)表示将整个图像窗口分为2行2列, 当前位置为2.
    plt.subplot(321+ind)  
    plt.pcolormesh(xx, yy, Z, cmap=cmap_light)
    # 数据点可视化到画布
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold,
                edgecolor='k', s=20)
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())
    plt.title("3-Class classification (k = %i)"% k)

plt.show()

output:
在这里插入图片描述
原理简析
如果选择较小的K值,就相当于用较小的领域中的训练实例进行预测,例如当k=1的时候,在分界点位置的数据很容易受到局部的影响,图中蓝色的部分中还有部分绿色块,主要是数据太局部敏感。当k=15的时候,不同的数据基本根据颜色分开,当时进行预测的时候,会直接落到对应的区域,模型相对更加鲁棒。

2.KNN分类-(iris数据集)

#1相应库导入
import numpy as np

from sklearn import datasets                              #iris数据集所在处
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split      #用来划分测试集和训练集
#2数据导入
iris = datasets.load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
#模型模拟
#class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights=’uniform’, 
#algorithm=’auto’, leaf_size=30, 
#p=2, metric=’minkowski’, 
#metric_params=None, n_jobs=None, **kwargs)
#n_neighbors : int,optional(default = 5) 默认情况下kneighbors查询使用的邻居数。就是k-NN的k的值,选取最近的k个点。
clf = KNeighborsClassifier(n_neighbors=5, p=2, metric="minkowski")
clf.fit(X_train, y_train)
# 预测
X_pred = clf.predict(X_test)
acc = sum(X_pred == y_test) / X_pred.shape[0]
print("预测的准确率ACC: %.3f" % acc)

预测的准确率ACC: 0.933

(以下为阿里云训练营的解释,我觉得非常好,借鉴一下)
我们用表格来看一下KNN的训练和预测过程。这里用表格进行可视化:

  1. 训练数据[表格对应list]
feat_1feat_2feat_3feat_4label
5.13.51.40.20
4.93.1.40.20
4.73.21.30.20
4.63.11.50.20
6.43.24.51.51
6.93.14.91.51
5.52.34.1.31
6.52.84.61.51
5.82.75.11.92
7.13.5.92.12
6.32.95.61.82
6.53.5.82.22
  1. knn.fit(X, y)的过程可以简单认为是表格存储
feat_1feat_2feat_3feat_4label
5.13.51.40.20
4.93.1.40.20
4.73.21.30.20
4.63.11.50.20
6.43.24.51.51
6.93.14.91.51
5.52.34.1.31
6.52.84.61.51
5.82.75.11.92
7.13.5.92.12
6.32.95.61.82
6.53.5.82.22
  1. knn.predict(x)预测过程会计算x和所有训练数据的距离
    这里我们使用欧式距离进行计算, 预测过程如下

x = [ 5. , 3.6 , 1.4 , 0.2 ] y = 0 x = [5. , 3.6, 1.4, 0.2] \\ y=0 x=[5.,3.6,1.4,0.2]y=0

step1: 计算x和所有训练数据的距离

feat_1feat_2feat_3feat_4距离label
5.13.51.40.20.141421360
4.93.1.40.20.608276250
4.73.21.30.20.509901950
4.63.11.50.20.648074070
6.43.24.51.53.663331821
6.93.14.91.54.219004621
5.52.34.1.33.148015251
6.52.84.61.53.849675311
5.82.75.11.94.246174752
7.13.5.92.15.350700892
6.32.95.61.84.730750472
6.53.5.82.25.096076922

step2: 根据距离进行编号排序

距离升序编号feat_1feat_2feat_3feat_4距离label
15.13.51.40.20.141421360
34.93.1.40.20.608276250
24.73.21.30.20.509901950
44.63.11.50.20.648074070
66.43.24.51.53.663331821
86.93.14.91.54.219004621
55.52.34.1.33.148015251
76.52.84.61.53.849675311
95.82.75.11.94.246174752
127.13.5.92.15.350700892
106.32.95.61.84.730750472
116.53.5.82.25.096076922

step3: 我们设置k=5,选择距离最近的k个样本进行投票

距离升序编号feat_1feat_2feat_3feat_4距离label
15.13.51.40.20.141421360
34.93.1.40.20.608276250
24.73.21.30.20.509901950
44.63.11.50.20.648074070
66.43.24.51.53.663331821
86.93.14.91.54.219004621
55.52.34.1.33.148015251
76.52.84.61.53.849675311
95.82.75.11.94.246174752
127.13.5.92.15.350700892
106.32.95.61.84.730750472
116.53.5.82.25.096076922

step4: k近邻的label进行投票

nn_labels = [0, 0, 0, 0, 1] --> 得到最后的结果0。

3.KNN回归

#库函数导入
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
np.random.seed(0)
#np.random.seed():这个函数控制着随机数的生成。当你将seed值设为某一定值,则np.random下随机数生成函数生成的随机数永远是不变的。

X = np.sort(5 * np.random.rand(40, 1), axis=0)
# 随机生成40个(0, 1)之前的数,乘以5,再进行升序
#作用:通过本函数可以返回一个或一组服从“0~1”均匀分布的随机样本值。随机样本取值范围是[0,1),不包括1。

T = np.linspace(0, 5, 500)[:, np.newaxis]#np.newaxis的作用就是选取部分的数据增加一个维度
# 创建[0, 5]之间的500个数的等差数列, 作为测试数据

y = np.sin(X).ravel()
# 使用sin函数得到y值,并拉伸到一维


y[::5] += 1 * (0.5 - np.random.rand(8))## Add noise to targets[y值增加噪声]
#a=np.random.rand(5)
#print(a)
#[ 0.64061262  0.8451399   0.965673    0.89256687  0.48518743]
#print(a[::-1]) ### 取从后向前(相反)的元素
#[ 0.48518743  0.89256687  0.965673    0.8451399   0.64061262]
 #print(a[2::-1]) ### 取从下标为2的元素翻转读取
#[ 0.965673  0.8451399   0.64061262]
# #############################################################################
# Fit regression model
# 设置多个k近邻进行比较
n_neighbors = [1, 3, 5, 8, 10, 40]

# 设置图片大小
plt.figure(figsize=(10,20))

for i, k in enumerate(n_neighbors):
    
    # 默认使用加权平均进行计算predictor
    clf = KNeighborsRegressor(n_neighbors=k, p=2, metric="minkowski")
    # 训练
    clf.fit(X, y)
    # 预测
    y_ = clf.predict(T)
    
    plt.subplot(6, 1, i + 1)
    plt.scatter(X, y, color='red', label='data')
    plt.plot(T, y_, color='navy', label='prediction')
    plt.axis('tight')
    plt.legend()
    plt.title("KNeighborsRegressor (k = %i)" % (k))

plt.tight_layout()
plt.show()

4.马绞痛数据–kNN数据预处理+kNN分类pipeline

import numpy as np
import pandas as pd

from sklearn.neighbors import KNeighborsClassifier# kNN分类器

from sklearn.impute import KNNImputer# kNN数据空值填充
#所以当我们需要填补缺失值时,可以考虑直接使用KNN的这个算法填补。

# 计算带有空值的欧式距离
from sklearn.metrics.pairwise import nan_euclidean_distances   #在scikit-learn包中,有一个euclidean_distances方法,可以用来计算向量之间的距离。

# 交叉验证
from sklearn.model_selection import cross_val_score

# KFlod的函数
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.pipeline import Pipeline
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
imputer = KNNImputer(n_neighbors=2, metric='nan_euclidean')
imputer.fit_transform(X)
nan_euclidean_distances([[np.nan, 6, 5], [3, 4, 3]], [[3, 4, 3], [1, 2, np.nan], [8, 8, 7]])
# load dataset, 将?变成空值

#文件输入
input_file = './horse-colic.csv'
df_data = pd.read_csv(input_file, header=None, na_values='?')

# 得到训练数据和label, 第23列表示是否发生病变, 1: 表示Yes; 2: 表示No. 
data = df_data.values
ix = [i for i in range(data.shape[1]) if i != 23]
X, y = data[:, ix], data[:, 23]

# 查看所有特征的缺失值个数和缺失率
for i in range(df_data.shape[1]):
    n_miss = df_data[[i]].isnull().sum()      #表示缺失值的个数
    perc = n_miss / df_data.shape[0] * 100    #缺失率
    if n_miss.values[0] > 0:
        print('>Feat: %d, Missing: %d, Missing ratio: (%.2f%%)' % (i, n_miss, perc))

# 查看总的空值个数
print('KNNImputer before Missing: %d' % sum(np.isnan(X).flatten()))
#np.isnan()判断是否在空值
#flatten()即返回一个折叠成一维的数组。但是该函数只能适用于numpy对象,即array或者mat,普通的list列表是不行的。
#>>>a = array([[ 1 , 2 ],[ 3 , 4 ],[ 5 , 6 ]])   ###此时a是一个array对象
#>>>a.flatten()
#array([ 1 , 2 , 3 , 4 , 5 , 6 ])

# 定义 knnimputer
imputer = KNNImputer()
# 填充数据集中的空值
imputer.fit(X)
# 转换数据集
Xtrans = imputer.transform(X)
# 打印转化后的数据集的空值
print('KNNImputer after Missing: %d' % sum(np.isnan(Xtrans).flatten()))
results = list()

strategies = [str(i) for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 18, 20, 21]]
for s in strategies:
    # create the modeling pipeline
    pipe = Pipeline(steps=[('imputer', KNNImputer(n_neighbors=int(s))), ('model', KNeighborsClassifier())])
    # 数据多次随机划分取平均得分
    scores = []
    for k in range(20):
        
        # 得到训练集合和验证集合, 8: 2
        X_train, X_test, y_train, y_test = train_test_split(Xtrans, y, test_size=0.2)
        pipe.fit(X_train, y_train)
        # 验证model
        score = pipe.score(X_test, y_test)
        scores.append(score)
    # 保存results
    results.append(np.array(scores))
    print('>k: %s, Acc Mean: %.3f, Std: %.3f' % (s, np.mean(scores), np.std(scores)))
# print(results)
# plot model performance for comparison
plt.boxplot(results, labels=strategies, showmeans=True)
plt.show()

三.
1.原理
k近邻方法是一种惰性学习算法,可以用于回归和分类,它的主要思想是投票机制,对于一个测试实例x, 我们在有标签的训练数据集上找到和最相近的k个数据,用他们的label进行投票,分类问题则进行表决投票,回归问题使用加权平均或者直接平均的方法。knn算法中我们最需要关注两个问题:k值的选择和距离的计算。 kNN中的k是一个超参数,需要我们进行指定,一般情况下这个k和数据有很大关系,都是交叉验证进行选择,但是建议使用交叉验证的时候,k∈[2,20],使用交叉验证得到一个很好的k值。
k值还可以表示我们的模型复杂度,当k值越小意味着模型复杂度变大,更容易过拟合,(用极少数的样例来绝对这个预测的结果,很容易产生偏见,这就是过拟合)。我们有这样一句话,k值越多学习的估计误差越小,但是学习的近似误差就会增大。
2.距离的计算
一般使用对于一般使用Lp距离进行计算。当p=1时候,称为
曼哈顿距离
(Manhattan distance),当p=2时候,称为欧氏距离(Euclidean distance),当p=∞时候,称为极大距离(infty distance), 表示各个坐标的距离最大值,另外也包含夹角余弦等方法。
一般采用欧式距离较多,但是文本分类则倾向于使用余弦来计算相似度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

maxchet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值