0012机器学习KNN算法

0012机器学习KNN算法

一、KNN算法原理

  • K近邻(K-nearest neighbors,KNN)是一种基本的机器学习算法,所谓k近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表。比如:判断一个人的人品,只需要观察与他来往最密切的几个人的人品好坏就可以得出,即"近朱者赤,近墨者黑";KNN算法既可以应用于分类应用中,也可以应用在回归应用中。
  • KNN在做回归和分类的主要区别在于最后做预测的时候的决策方式不同。KNN在分类预测时,一般采用多数表决法;而在做回归预测时,一般采用平均值法。

  • KNN算法一般用欧氏距离求

二、KNN三要素

在KNN算法中,非常重要的主要是三个因素:

  • K值的选择:对于K值的选择,一般根据样本分布选择一个较小的值,然后通 过交叉验证来选择一个比较合适的最终值;当选择比较小的K值的时候, 表示使用较小领域中的样本进行预测,训练误差会减小,但是会导致模型变得 复杂,容易过拟合;当选择较大的K值的时候,表示使用较大领域中的样本进行预测,训练误差会增大,同时会使模型变得简单,容易导致欠拟合;
  • 距离的度量:一般使用欧氏距离;
  • 决策规则:在分类模型中,主要使用多数表决法或者加权多数表决法;在回 归模型中,主要使用平均值法或者加权平均值法。
    • KNN分类预测规则

      • 多数表决法:每个邻近样本的权重是一样的,也就是说最终预测的结果为出现类别最多的那个类
      • 加权多数表决法:每个邻近样本的权重是不一样的,一般情况下采用权重和距离成反比的方式来计算,也就是说最终预测结果是出现权重最大的那个类别;
    • KNN回归预测规则

      • 平均值法:每个邻近样本的权重是一样的,也就是说最终预测的结果为所有邻近样本的目标属性值的均值;
      • 加权平均值法:每个邻近样本的权重是不一样的,一般情况下 采用权重和距离成反比的方式来计算,也就是说在计算均值的时候进行加权操作;

三、python代码理解KNN算法

python 复制代码
'''
简单实现一下等权分类 封装成KNN类
    实现fit,predict,score方法
'''
import sys
import numpy as np
import pandas as pd
class KNN:
    '''
    KNN的步骤:
    1、从训练集合中获取K个离待预测样本距离最近的样本数据;
    2、根据获取得到的K个样本数据来预测当前待预测样本的目标属性值
    '''
    def __init__(self, k):
        self.k = k
        pass
    def fit(self, x, y):
        '''
        训练模型  实际上就是存储数据
        :param x: 训练数据x
        :param y: 训练数据y
        :return:
        '''
        ### 将数据转化为numpy数组的形式进行存储
        self.train_x = np.array(x)
        self.train_y = np.array(y)
    def feach_k_neighbors(self, x):
        '''
        # 1、从训练集合中获取K个离待预测样本距离最近的样本数据;
        # 2、根据获取得到的K个样本数据来预测当前待预测样本的目标属性值
        :param x:待预测的一条数据
        :return: 最近k个邻居的label
        '''
        ###列表 [[dis1,标签1],[dis2,标签2].。。。。。。]
        listdistance = []
        ##循环每一个数据,计算他的dis
        for index, i in enumerate(self.train_x):
            # print(index)
            dis = np.sum((np.array(i) - np.array(x)) ** 2) ** 0.5  # 求差值的平方和再开方(求欧氏距离)
            listdistance.append([dis, self.train_y[index]])
        # print(listdistance)
        ##按照dis从小到大进行排序
        listdistance.sort()
        # print(listdistance)
        # sys.exit()
        ##选取K个邻居放入投票箱
        # print(listdistance[:self.k])
        arr = np.array(listdistance[:self.k])[:, -1]
        # print(arr)
        return arr
    def predict(self, x):
        '''
        对待预测数据进行预测
        :param x: 待预测数据的特征属性x 是个矩阵
        :return: 所有数据的预测label
        '''
        ### 将数据转化为numpy数组的形式
        self.pre_x = np.array(x)
        # 遍历每一条带预测数据
        Y_pre = []
        for x in self.pre_x:
            # print(x)
            # 1、从训练集合中获取K个离待预测样本距离最近的样本数据;
            k_nearst_neighbors_label = self.feach_k_neighbors(x)

            # 2、根据获取得到的K个样本数据来预测当前待预测样本的目标属性值
            ##统计投票
            a = pd.Series(k_nearst_neighbors_label).value_counts()
            # print(a)
            # pre = a.idxmax()  ##idxmax() 和 argmax 功能一样,获取最大值对应的下标索引
            y_pre = a.idxmax()
            # pre = a.argmax()
            # print(pre)
            Y_pre.append(y_pre)
        return Y_pre
    def score(self, x, y):
        '''
        准确率
        :param x:
        :param y:
        :return: 准确率
        '''
        y_hat = self.predict(x)
        acc = np.mean(y == y_hat)
        return acc
def movie():
    T = np.array([
        [3, 104, -1],
        [2, 100, -1],
        [1, 81, -1],
        [101, 10, 1],
        [99, 5, 1],
        [98, 2, 1]])
    X_train = T[:, :-1]
    Y_train = T[:, -1]
    x_test = [[18, 90], [50, 50]]
    knn = KNN(k=3)
    knn.fit(x=X_train, y=Y_train)
    print('训练集预测结果:{}'.format(knn.predict(X_train)))
    print('训练集得分:{}'.format(knn.score(x=X_train, y=Y_train)))
    # knn.fetch_k_neighbors(x_test[0])
    print('预测结果:{}'.format(knn.predict(x_test)))
def iris():
    from sklearn.datasets import load_iris  # 加载一些数据集
    from sklearn.model_selection import train_test_split

    X, Y = load_iris(return_X_y=True)
    print(X.shape, Y.shape)
    x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=0)
    print(x_train.shape, y_train.shape)
    knn01 = KNN(k=3)
    knn01.fit(x_train, y_train)
    print(knn01.score(x_train, y_train))
    print(knn01.score(x_test, y_test))
if __name__ == '__main__':
    #movie()
    iris()

以上代码是通过蛮力实现(brute):计算预测样本到所有训练集样本的距离,然后选择最小的k个距离即可得到K个最邻近点。缺点在于当特征数比较多、样本数比较多的时候,算法的执行效率比较低

四、KD树(kd_tree)

当样本数据量少的时候,我们可以使用brute这种暴力的方式进行求解最近邻,即计算到所有样本的距离。但是当样本量比较大的时候,直接计算所有样本的距离,工作量有点大,所以在这种情况下,我们可以使用KD Tree来快速的计算。

KD树采用从m个样本的n维特征中,分别计算n个特征取值的方差,用方差最大的第k维特征nk作为根节点。对于这个特征,选择取值的中位数nkv作为样本的划分点,对于小于该值的样本划分到左子树,对于大于该值的样本划分到右子树,该值为根节点。接下来对左右子树采用同样的方式找方差最大的特征作为根节点,递归即可产生 KD树。


如果来了一个预测样本,可以根据划分后的树,从局部找到k个邻居,不用在全局找

五、球树(Ball Tree)

  • 使用kd树最近邻预测时,矩形与超球面易于相交,时常会因为菱角相交导致一些无关多余的搜索,球树就是对kd树的这个缺点进行改进而生,通过将特征点转化为球状分割,从而减少无效相交。
  • 球树的构建步骤:
    • 先构建一个超球体,这个超球体是可以包含所有样本的最小球体。
    • 从球中选择一个离球的中心最远的点,然后选择第二个点离第一个点最远,将球中所有的点分配到离这两个聚类中心最近的一个上,然后计算每个聚类的中心,以及聚类能够包含它所有数据点所需的最小半径。这样我们得到了两个子超球体,和KD树里面的左右子树对应。
    • 对于这两个子超球体,递归执行步骤最终得到了一个球树

六、API调用

1.1 基于鸢尾花数据的KNN案例

python 复制代码
import pandas as pd
import numpy as np
import sys
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.metrics import accuracy_score, recall_score, precision_score, r2_score
from sklearn.preprocessing import LabelEncoder

# 1. 加载数据(数据一般存在于磁盘或者数据库)
path = '../data/iris.data'
names = ['x1', 'x2', 'x3', 'x4', 'y']
df = pd.read_csv(path, header=None, names=names, sep=",")
print(df.head())
print(df.shape)
print(df["y"].value_counts())
# sys.exit()

# 2. 数据清洗
# NOTE: 不需要做数据处理
def parse_record(row):
    result = []
    r = zip(names, row)
    for name, value in r:
        if name == 'y':
            if value == 'Iris-setosa':
                result.append(1)
            elif value == 'Iris-versicolor':
                result.append(2)
            elif value == 'Iris-virginica':
                result.append(3)
            else:
                result.append(0)
        else:
            result.append(value)
    return result


df = df.apply(lambda row: pd.Series(parse_record(row), index=names), axis=1)
df['y'] = df['y'].astype(np.int32)
df.info()
print(df["y"].value_counts())
flag = False
# sys.exit()
# df = df[df.cla != 3]
# print(df.cla.value_counts())

# # 3. 根据需求获取最原始的特征属性矩阵X和目标属性Y
# X = df.iloc[:,:-1]
X = df[names[0:-1]]
print(X.shape)
Y = df[names[-1]]
print(Y.shape)
print(Y.value_counts())
# sys.exit()

# 4. 数据分割
# train_size: 给定划分之后的训练数据的占比是多少,默认0.75
# test_size:
# random_state:给定在数据划分过程中,使用到的随机数种子,默认为None,使用当前的时间戳;给定非None的值,可以保证多次运行的结果是一致的。
x_train, x_test, y_train, y_test = train_test_split(X, Y, train_size=0.8, random_state=1)
print("训练数据X的格式:{}, 以及类型:{}".format(x_train.shape, type(x_train)))
print("测试数据X的格式:{}".format(x_test.shape))
print("训练数据Y的类型:{}".format(type(y_train)))

# 5. 特征工程的操作
# NOTE: 不做特征工程

# 6. 模型对象的构建
"""
KNN:
    n_neighbors=5,
    weights='uniform',
    algorithm='auto', 
    leaf_size=30,
    p=2,
    metric='minkowski', 
    metric_params=None, 
    n_jobs=1
"""
KNN = KNeighborsClassifier(n_neighbors=10, weights='uniform', algorithm='kd_tree')

# 7. 模型的训练
KNN.fit(x_train, y_train)

# 8. 模型效果评估
train_predict = KNN.predict(x_train)
test_predict = KNN.predict(x_test)
print("KNN算法:测试集上的效果(准确率):{}".format(KNN.score(x_test, y_test)))
print("KNN算法:训练集上的效果(准确率):{}".format(KNN.score(x_train, y_train)))
print(accuracy_score(y_true=y_train, y_pred=train_predict))
# 模型的保存与加载
# pip install joblib
import joblib
joblib.dump(KNN, "./knn.m")  # 保存模型
# joblib.load(path) # 加载模型

2.2 KNNImputer缺失值填充

KNNImputer可以更便捷地处理缺失值,并且与直接用均值、中位数相比更为可靠。利用"近朱者赤"的KNN算法原理,这种插补方法借助其他特征的分布来对目标特征进行缺失值填充。

举例1:

举例2:




3.3 基于KNN的数据下采样方法

所谓数据下采样,就是减小数据集的样本数量。

  • 当样本量太多的时候,为了能够快速学习迭代模型,我们可能会剔除掉训练集中的冗余样本,所谓的冗余样本,就是指对模型的训练效果没有影响或者影响较小的样本,即使剔除后,模型仍能够做到较高的可用性。
  • 当数据不均衡的时候,可以使用数据下采样减少样本数量多的那种类型的样本数量(也可以增加样本 数量少的那种类型的样本数量,称为数据上采样。)

1、CNN


2、ENN

相关推荐
CoderYanger1 小时前
动态规划算法-路径问题:9.最小路径和
开发语言·算法·leetcode·动态规划·1024程序员节
汤姆yu1 小时前
基于springboot+ai的健康管理系统
人工智能·spring boot·后端
北岛寒沫1 小时前
北京大学国家发展研究院 经济学辅修 经济学原理课程笔记(第三课 需求与供应弹性)
数据库·人工智能·笔记
月明长歌1 小时前
【码道初阶】一道经典的简单题:Boyer-Moore 多数投票算法|多数元素问题(LeetCode 169)
算法·leetcode·职场和发展
CoderYanger1 小时前
动态规划算法-路径问题:7.礼物的最大价值
开发语言·算法·leetcode·动态规划·1024程序员节
蕓晨1 小时前
钱币找零问题-贪心算法解析
c++·算法·贪心算法
北京青翼科技1 小时前
【TES818 】基于 VU13P FPGA+ZYNQ SOC 的 8 路 100G 光纤通道处理平台
图像处理·人工智能·fpga开发·信号处理·智能硬件
一点一木1 小时前
🚀 2025 年 11 月 GitHub 十大热门项目排行榜 🔥
前端·人工智能·github
hetao17338371 小时前
2025-12-04 hetao1733837的刷题记录
c++·算法