【scikit-learn】K近邻(KNN)

KNN

KNN(K-Nearest Neighbor)是最简单的机器学习算法之一,可以用于分类和回归,是一种监督学习算法。它的思路是这样,如果一个样本在特征空间中的K个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。也就是说,该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

KNN用于分类

  1. 计算待分类点与已知类别的点之间的距离
  2. 按照距离递增次序排序
  3. 选取与待分类点距离最小的K个点
  4. 确定前K个点所在类别的出现次数
  5. 返回前K个点出现次数最高的类别作为待分类点的预测分类

KNN回归预测

回归中,一般采用平均值法或者加权平均值法

平均值法: 每个邻近样本的权重是一样的,最终预测的结果为所有邻近样本目标属性值的均值
加权平均值法:一般采用权重和距离成反比的方式计算

KNN算法要注意的问题

无论是分类还是回归,KNN算法的几个关键点:

  • 算法超参数K
  • 距离度量,特征空间中样本点的距离是样本点间相似程度的反映
  • 分类决策规则,少数服从多数。

算法超参数K

如果选择较小的K值,就相当于用较小的邻域中的训练实例进行预测,“学习”的近似误差会减小,只有输入实例较近的训练实例才会对预测结果起作用。但缺点是“学习”的估计误差会增大,预测结果会对近邻实例点非常敏感。如果邻近的实例点恰巧是噪声,预测就会出错。换句话说,K值得减小就意味着整体模型非常复杂,容易发生过拟合。
如果选择较大的K值,就相当于用较大邻域中的训练实例进行预测,其实优点是减少学习的估计误差,但缺点是学习的近似误差会增大。这时与输入实例较远的训练实例也会起预测作用,使预测发生错误,k值的增大就意味着整体的模型变得简单,容易发生欠拟合。可以假定极端条件K=N,那么无论输入实例是什么,都将简单的预测它属于训练实例中最多的类。这时,模型过于简单,完全忽略训练中的大量有用信息,是不可取的。
在应用中,通常采用交叉验证法来选择最优K值。从上面的分析也可以知道,一般K值取得比较小。我们会选取K值在较小的范围,同时在验证集上准确率最高的那一个确定为最终的算法超参数K。

距离度量

常用的距离有三种,分别为曼哈顿距离、欧式距离和闵可夫斯基距离

KNN的优缺点

优点:
1)算法简单,理论成熟,既可以用来做分类也可以用来做回归。
2)可用于非线性分类。
3)没有明显的训练过程,而是在程序开始运行时,把数据集加载到内存后,不需要进行训练,直接进行预测,所以训练时间复杂度为0。
4)由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属的类别,因此对于类域的交叉或重叠较多的待分类样本集来说,KNN方法较其他方法更为适合。
5)该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量比较小的类域采用这种算法比较容易产生误分类情况。

缺点:
1)需要算每个测试点与训练集的距离,当训练集较大时,计算量相当大,时间复杂度高,特别是特征数量比较大的时候。
2)需要大量的内存,空间复杂度高。
3)样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少),对稀有类别的预测准确度低。
4)是lazy learning方法,基本上不学习,导致预测时速度比起逻辑回归之类的算法慢。

实战


'''
数据集:Mnist
训练集数量:60000
测试集数量:10000(实际使用:10)可更改
'''

import time
import numpy as np
import sys
import os

# 导入处于不同目录下的Mnist.load_data
parent_path=os.path.dirname(os.path.dirname(sys.argv[0])) # 获取上级目录
sys.path.append(parent_path) # 修改sys.path
from torchvision.datasets.mnist import MNIST


from torch.utils.data import DataLoader
from torchvision import datasets
import torchvision
import torchvision.transforms as transforms




class KNN:
    def __init__(self, x_train, y_train, x_test, y_test, k):
        '''
        Args:
            x_train [Array]: 训练集数据
            y_train [Array]: 训练集标签
            x_test [Array]: 测试集数据
            y_test [Array]: 测试集标签
            k [int]: k of kNN
        '''
        self.x_train, self.y_train = x_train, y_train
        self.x_test, self.y_test = x_test, y_test
        # 将输入数据转为矩阵形式,方便运算
        self.x_train_mat, self.x_test_mat = np.mat(
            self.x_train), np.mat(self.x_test)
        self.y_train_mat, self.y_test_mat = np.mat(
            self.y_test).T, np.mat(self.y_test).T
        self.k = k

    def _calc_dist(self, x1, x2):
        '''计算两个样本点向量之间的距离,使用的是欧氏距离
        :param x1:向量1
        :param x2:向量2
        :return: 向量之间的欧式距离
        '''
        return np.sqrt(np.sum(np.square(x1 - x2)))

    def _get_k_nearest(self,x):
        '''
        预测样本x的标记。
        获取方式通过找到与样本x最近的topK个点,并查看它们的标签。
        查找里面占某类标签最多的那类标签
        :param trainDataMat:训练集数据集
        :param trainLabelMat:训练集标签集
        :param x:待预测的样本x
        :param topK:选择参考最邻近样本的数目(样本数目的选择关系到正确率,详看3.2.3 K值的选择)
        :return:预测的标记
        '''
        # 初始化距离列表,dist_list[i]表示待预测样本x与训练集中第i个样本的距离
        dist_list=[0]* len(self.x_train_mat)

        # 遍历训练集中所有的样本点,计算与x的距离
        for i in range( len(self.x_train_mat)):
            # 获取训练集中当前样本的向量
            x0 = self.x_train_mat[i]
            # 计算向量x与训练集样本x0的距离
            dist_list[i] = self._calc_dist(x0, x)

        # 对距离列表排序并返回距离最近的k个训练样本的下标
        # ----------------优化点-------------------
        # 由于我们只取topK小的元素索引值,所以其实不需要对整个列表进行排序,而argsort是对整个
        # 列表进行排序的,存在时间上的浪费。字典有现成的方法可以只排序top大或top小,可以自行查阅
        # 对代码进行稍稍修改即可
        # 这里没有对其进行优化主要原因是KNN的时间耗费大头在计算向量与向量之间的距离上,由于向量高维
        # 所以计算时间需要很长,所以如果要提升时间,在这里优化的意义不大。
        k_nearest_index = np.argsort(np.array(dist_list))[:self.k]  # 升序排序
        return k_nearest_index


    def _predict_y(self,k_nearest_index):
        # label_list[1]=3,表示label为1的样本数有3个,由于此处label为0-9,可以初始化长度为10的label_list
        label_list=[0] * 10
        for index in k_nearest_index:
            one_hot_label=self.y_train[index]
            number_label=np.argmax(one_hot_label)
            label_list[number_label] += 1
        # 采用投票法,即样本数最多的label就是预测的label
        y_predict=label_list.index(max(label_list))
        return y_predict

    def test(self,n_test=10):
        '''
        测试正确率
        :param: n_test: 待测试的样本数
        :return: 正确率
        '''
        print('start test')

        # 错误值计数
        error_count = 0
        # 遍历测试集,对每个测试集样本进行测试
        # 由于计算向量与向量之间的时间耗费太大,测试集有6000个样本,所以这里人为改成了
        # 测试200个样本点,若要全跑,更改n_test即可
        for i in range(n_test):
            # print('test %d:%d'%(i, len(trainDataArr)))
            print('test %d:%d' % (i, n_test))
            # 读取测试集当前测试样本的向量
            x = self.x_test_mat[i]
            # 获取距离最近的训练样本序号
            k_nearest_index=self._get_k_nearest(x)
            # 预测输出y
            y=self._predict_y(k_nearest_index)
            # 如果预测label与实际label不符,错误值计数加1
            if y != np.argmax(self.y_test[i]):
                error_count += 1
            print("accuracy=",1 - (error_count /(i+1)))

        # 返回正确率
        return 1 - (error_count / n_test)


if __name__ == "__main__":

    k=1
    start = time.time()

    train_dataset = datasets.MNIST(root = '../datasets', train = True,transform = transforms.ToTensor(), download = True)
    test_dataset = datasets.MNIST(root = '../datasets', train = False, transform = transforms.ToTensor(), download = True)

    # batch_size = len(train_dataset)
    train_loader = DataLoader(dataset=train_dataset, batch_size=60000, shuffle=True)
    test_loader = DataLoader(dataset=test_dataset, batch_size=1000, shuffle=True)
    x_train,y_train = next(iter(train_loader))
    x_test,y_test = next(iter(test_loader))
    x_train = x_train.squeeze(1).view(60000,-1)
    x_test = x_test.squeeze(1).view(1000,-1)


    model=KNN( x_train, y_train, x_test, y_test,k)
    accur=model.test()
    end = time.time()
    print("total acc:",accur)
    print('time span:', end - start)
start test
test 0:10
accuracy= 1.0
test 1:10
accuracy= 1.0
test 2:10
accuracy= 1.0
test 3:10
accuracy= 1.0
test 4:10
accuracy= 1.0
test 5:10
accuracy= 1.0
test 6:10
accuracy= 1.0
test 7:10
accuracy= 1.0
test 8:10
accuracy= 1.0
test 9:10
accuracy= 1.0
total acc: 1.0
time span: 17.0129714012146

使用sklearn

  1. 将数据集分割成测试数据集和训练数据集
from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test =train_test_split(X,y)
  1. 创建一个KNeighborsClassifier 对象
from sklearn.neighbors import KNeighborsClassifier

knn= KNeighborsClassifier()
  1. 使用KNeighborsClassifier 对象进行fit创建出模型,得出分类准确度
knn.fit(X_train,y_train)
knn.score(X_test,y_test)
  1. 使用模型预测测试集
y_predict = knn.predict(X_test)

KNN的初始函数(构造函数)的参数和默认参数是(n_neighbors=5,weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’,metric_params=None, n_jobs=1, **kwargs)

[n_neighbors] 也就是K,默认参数为5。

[weights] str参数,为了降低k值的影响所设置的权重值,即每个拥有投票权的样本是按什么比重投票,'uniform'表示等比重投票,'distance'表示按距离 反比投票,[callable]表示自己定义的一个函数,这个函数接收一个距离数组,返回一个权值数组。默认参数为‘uniform’。

[algorithm] str参数,即内部采用什么数据结构实现。有以下几种选择参:'ball_tree':球树、'kd_tree':kd树、'brute':暴力搜索、'auto':自动根据数据的类型和结构选择合适的算法。默认情况下是‘auto’。暴力搜索就不用说了大家都知道。具体前两种树型数据结构哪种好视情况而定。KD树是依次对K维坐标轴,以中值切分构造的树,每一个节点是一个超矩形,在维数小于20时效率最高。ball tree 是为了克服KD树高维失效而发明的,其构造过程是以质心C和半径r分割样本空间,每一个节点是一个超球体。一般低维数据用kd_tree速度快,用ball_tree相对较慢。超过20维之后的高维数据用kd_tree效果反而不佳,而ball_tree效果要好,具体构造过程及优劣势的理论大家有兴趣可以去具体学习。

[leaf_size] int参数,基于以上介绍的算法,此参数给出了kd_tree或者ball_tree叶节点规模,叶节点的不同规模会影响数的构造和搜索速度,同样会影响用于储存树的内存的大小。具体最优规模是多少视情况而定。

[matric] str或者距离度量对象,即怎样度量距离,前面有具体介绍。默认minkowski。

[p] int参数,就是以上闵氏距离各种不同的距离参数,默认为2,即欧氏距离。p=1代表曼哈顿距离等等。

[metric_params] 距离度量函数的额外关键字参数,一般不用管,默认为None。

[n_jobs] int参数,指并行计算的线程数量,默认为1表示一个线程,为-1的话表示为CPU的内核数,也可以指定为其他数量的线程,这里不是很追求速度的话不用管,需要用到的话去看看多线程。
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很抱歉,我是AI语言模型,无法提供代码。以下是使用scikit-learn库实现k近邻算法对forge数据集进行回归的步骤: 1. 导入所需库和数据集 ```python from sklearn.neighbors import KNeighborsRegressor from sklearn.datasets import make_regression X, y = make_regression(n_samples=100, n_features=2, noise=10, random_state=0) ``` 2. 创建K近邻回归模型 ```python knn = KNeighborsRegressor(n_neighbors=5) ``` 3. 拟合模型并进行预测 ```python knn.fit(X, y) y_pred = knn.predict(X) ``` 4. 评估模型性能 ```python from sklearn.metrics import mean_squared_error mse = mean_squared_error(y, y_pred) print("Mean Squared Error:", mse) ``` 以上是使用scikit-learn库实现k近邻算法对forge数据集进行回归的基本步骤,具体实现可参考scikit-learn官方文档。 ### 回答2: 使用k近邻算法对Forge数据集进行回归可以帮助我们根据Forge数据集的样本数据推断出新的数据,以预测未知的样本数据。该任务可以使用Scikit-learn库的KNeighborsRegressor实现。下面是实现该任务的详细步骤: 1. 导入必要的库和数据集 ```python import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import make_regression from sklearn.neighbors import KNeighborsRegressor from sklearn.model_selection import train_test_split # 加载数据集 X, y = make_regression(n_samples=100, n_features=2, noise=10) ``` 2. 分离训练集和测试集 ```python X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) ``` 3. 创建K近邻回归模型 ```python # 创建K近邻回归模型 knn = KNeighborsRegressor(n_neighbors=5) ``` 4. 训练模型 ```python # 训练模型 knn.fit(X_train, y_train) ``` 5. 预测测试集 ```python # 预测测试集 y_pred = knn.predict(X_test) ``` 6. 评估模型性能 ```python # 评估模型性能 print("Test set R^2: {:.2f}".format(knn.score(X_test, y_test))) ``` 7. 可视化结果 ```python # 可视化结果 plt.scatter(y_test, y_pred) plt.plot([-100, 100], [-100, 100], '--k') plt.axis('tight') plt.xlabel('True value') plt.ylabel('Predicted value') plt.show() ``` 完整代码: ```python import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import make_regression from sklearn.neighbors import KNeighborsRegressor from sklearn.model_selection import train_test_split # 加载数据集 X, y = make_regression(n_samples=100, n_features=2, noise=10) # 分离训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) # 创建K近邻回归模型 knn = KNeighborsRegressor(n_neighbors=5) # 训练模型 knn.fit(X_train, y_train) # 预测测试集 y_pred = knn.predict(X_test) # 评估模型性能 print("Test set R^2: {:.2f}".format(knn.score(X_test, y_test))) # 可视化结果 plt.scatter(y_test, y_pred) plt.plot([-100, 100], [-100, 100], '--k') plt.axis('tight') plt.xlabel('True value') plt.ylabel('Predicted value') plt.show() ``` 运行以上代码可以在Scikit-learn使用k近邻算法实现对Forge数据集进行回归预测。 ### 回答3: K近邻算法是一种很常用的算法,常用于分类和回归问题scikit-learn是一个Python的机器学习库,很方便使用,可以轻松实现K近邻算法。 Forge数据集是一个人工创建的用于分类和回归问题的数据集数据集是二维的,包含两个特征值和一个目标变量值。我们用K近邻算法对目标变量进行回归分析。 代码如下: ```python # 导入库和数据集 from sklearn.datasets import make_regression from sklearn.neighbors import KNeighborsRegressor from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt import numpy as np # 创建数据集 X, y = make_regression(n_samples=100, n_features=2, noise=10) # 数据集的划分 X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) # 实例化KNN模型 knn = KNeighborsRegressor(n_neighbors=3) # 拟合模型 knn.fit(X_train, y_train) # 预测结果 y_pred = knn.predict(X_test) # 打印预测结果和实际结果 print('预测结果:', y_pred) print('实际结果:', y_test) # 画图展示 plt.scatter(X_test[:, 0], y_test, color='black', label='实际结果') plt.scatter(X_test[:, 0], y_pred, color='blue', label='预测结果') plt.legend() plt.show() ``` 其,make_regression函数是用来生成数据集的。参数如下: - n_samples:生成样本数,默认100; - n_features:生成样本特征数,默认10; - noise:样本随机噪声,默认0.0。 train_test_split函数用来划分样本数据集。返回分别是训练集和测试集的特征数据和目标数据。在实例化KNN模型时,我们需要设置K值,即n_neighbors参数。我们选用了3,表示模型会以目标变量最接近的3个点作为预测结果。 拟合模型后,我们进行预测和实际结果的对比,并且使用plt.scatter绘制出预测结果和实际结果的散点图。 总之,使用scikit-learnKNN模型,对于回归问题需要通过KNeighborsRegressor类来实现,非常方便。而对于分类问题,则需要使用KNeighborsClassifier类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值