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

相关推荐
NAGNIP8 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab9 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab9 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP13 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年13 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼13 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS13 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区14 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈14 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang15 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx