机器学习_K近邻

分类算法-knn

K近邻(k-nearest neighbour, KNN)

概念

  • K近邻(k-nearest neighbour, KNN)是一种基本分类方法,通过测量不同特征之间的距离进行分类,它的思路是:如果一个样本在特征空间中的K个最相似的样本中大多数属于某一个类别,则该样本也属于这个类别,其中K通常是不大于20的整数
  • KNN算法中,所选择的邻居都是已经正确分类的对象

也可以根据距离的远近给定权重

如果是二分类的话 一般是K的选择是个奇数(少数服从多数

示例

  • 图中 蓝色正方形红色三角形分别属于两个类别,此时有一个绿色圆,那么这个 属于哪个类别呢

  • 如果k=3,会选择最近的2个红色三角形和1个蓝色正方形,红色>蓝色,此时绿圆属于红色的类别

  • 如果k=5, 会选择虚线圆中的3个蓝色正方形和2个红色三角形,蓝色>红色,此时绿圆属于蓝色的类别

所以,KNN的算法结果很大程度上取决于K的选择

KNN距离计算

KNN中,通过计算对象间距离来作为各个对象之间的非相似性指标,避免了对象之间的匹配问题,在这里距离一般使用欧氏距离或曼哈顿距离,常用的是欧式距离

公式中的x,y都是向量

$$
欧式距离:\quad d(x,y) = \sqrt{ \sum_{k=1}^{n} (x_k - y_k)^2}
$$

$$
曼哈顿距离:\quad d(x,y) = \sqrt{ \sum_{k=1}^{n} |x_k - y_k|}
$$

KNN算法过程

  • 在训练集中的数据和标签都已知的情况下,输入测试数据,将测试数据的特征与训练集中对应的特征进行互相比较,找到训练集中与之最为相似的前K个数据,则该测试数据对应的类别是K个数据中出现次数最多的那个分类,其算法的描述为:
  1. 计算测试数据与各个训练数据之间的距离
  2. 按照距离的递增关系进行排序
  3. 选取距离最小的k个点
  4. 确定前k个点所在类别的出现频率
  5. 返回前K个点中出现频率最高的类别作为测试数据的预测分类

训练数据已经知道标签了

代码

import numpy as np
import pandas as pd

# 这里直接引入sklearn里的数据集,iris鸢尾花
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split  # 切分数据集为训练集和测试集
from sklearn.metrics import accuracy_score # 计算分类预测的准确率

iris = load_iris()
# print(iris)
'''
irsis是一个字典数据,主要有4个key值,分别为
'data': array([[5.1, 3.5, 1.4, 0.2],....     [4.9, 3. , 1.4, 0.2],
'target': array([0, 0, 0, 0, 0, 0, ..1,1,1.......22222)]
'target_names': array(['setosa', 'versicolor', 'virginica'], 
'feature_names': ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'], 

data中是个二维数组,每个数据代表的是一个样本点,每个样本点有4个特征,也就是有4个x
target是就结果,就是 y
target_names 是每个target是什么品种
feature_names 每个特征具体代表什么

'''
"""
1 指定对应的数据和列名
2.增加一列结果集
3.最后根据结果集的0,1,2替换掉对应的target_name
"""
df = pd.DataFrame(data = iris.data, columns = iris.feature_names)
df['class'] = iris.target
df['class'] = df['class'].map({0: iris.target_names[0], 1: iris.target_names[1], 2: iris.target_names[2]})
# print(df.head(10))
# print(df.describe())


x = iris.data
# 一维数组转二维
y = iris.target.reshape(-1,1)
# print(x.shape, y.shape)
# (150, 4) (150, 1)

# 划分数据集 0.3 表示测试集 占30% 训练集占70%
# random_states随机数种子(指定之后每一次调用方法返回的结果都是一样的,保证测试的时候训练集和测试集是一样的)
# stratify=y 对y保持等比例的分成 (避免不能出现过多的其中一种y,因为数据量少才这么操作而已)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=35, stratify=y)
# print(x_train.shape, y_train.shape)
# print(x_test.shape, y_test.shape)
# (105, 4) (105, 1)s
# (45, 4) (45, 1)

# 定义距离函数
# 曼哈顿距离
# 这里面的 axis=1是对1轴进行sum
def l1_distance(a,b) :
    return np.sum(np.abs(a-b), axis=1)
# 欧式距离
def l2_distance(a,b) :
    return np.sqrt(np.sum((a-b) ** 2 , axis=1))


# 分类器实现
class kNN(object):
    # 定义一个初始化方法,__init__ 是类的构造方法
    # n_neighbors 是K值,默认为1
    def __init__(self, n_neighbors=1, dist_func=l1_distance):
        self.n_neighbors = n_neighbors
        self.dist_func = dist_func

    # 训练模型方法
    def fit(self, x, y):
        self.x_train = x
        self.y_train = y

    # 模型预测方法
    def predict(self, x):
        # 初始化预测分类数组
        y_pred = np.zeros((x.shape[0], 1), dtype=self.y_train.dtype)

        # 遍历输入的x数据点,取出每一个数据点的序号i和数据x_test
        for i, x_test in enumerate(x):
            # x_test跟所有训练数据计算距离
            distances = self.dist_func(self.x_train, x_test)

            # 得到的距离按照由近到远排序,取出索引值
            nn_index = np.argsort(distances)

            # 选取最近的k个点,保存它们对应的分类类别
            nn_y = self.y_train[nn_index[:self.n_neighbors]].ravel()

            # 统计类别中出现频率最高的那个,赋给y_pred[i]
            y_pred[i] = np.argmax(np.bincount(nn_y))

        # fou循环逐步解释
        '''
        dist_func 方法是测试点跟所有样本(向量)求一个距离,返回一个距离矩阵
            例 distances =  [  3.87298335  22.22611077  11.40175425  26.17250466   3. 12.24744871 221.39783197  71.92357055  54.3783045 ]
        np.argsort(distances) 是按照距离从小到大排列,但是返回的是 该距离的下标
            例 nn_index =[4 0 2 5 1 3 8 7 6]
               3是最小的,下标是4 3.8是第二小的 下标是0      
        self.y_train[nn_index[:self.n_neighbors]].ravel()   
            y_train =[[1],[2],[2],[3].[1]]  
            nn_index[:k]是指取出前K个值,K=3 即[4,0,,2]
            y_train[[4,0,2]] 是指把下标为4,0,2的训练数据的y点拿出来
            ravel是展开的意思
            最终 nn_y=[1,1,2]
        np.argmax(np.bincount(nn_y))
            np.bincount(nn_y) 是统计出现的次数
                输出是[0,2,1] 0是指0出现了0次 2是指1出现了2次 1是指2出现了1次
            np.argmax取出最大的值 2 的下标 1
             y_pred[i] = 1
        '''

        return y_pred

# 定义一个knn实例
knn = kNN(n_neighbors = 3)
# 训练模型 (训练集做训练)
knn.fit(x_train, y_train)
# 传入测试数据,做预测(测试集做测试)
y_pred = knn.predict(x_test)

print(y_test.ravel())
print(y_pred.ravel())

# 求出预测准确率
accuracy = accuracy_score(y_test, y_pred)

print("预测准确率: ", accuracy)

print("求不同取值k的结果")
# 定义一个knn实例
knn = kNN()
# 训练模型
knn.fit(x_train, y_train)

# 保存结果list
result_list = []

# 针对不同的参数选取,做预测
for p in [1, 2]:
    knn.dist_func = l1_distance if p == 1 else l2_distance

    # 考虑不同的k取值,步长为2
    for k in range(1, 10, 2):
        knn.n_neighbors = k
        # 传入测试数据,做预测
        y_pred = knn.predict(x_test)
        # 求出预测准确率
        accuracy = accuracy_score(y_test, y_pred)
        result_list.append([k, 'l1_distance' if p == 1 else 'l2_distance', accuracy])
df = pd.DataFrame(result_list, columns=['k', '距离函数', '预测准确率'])
print(df)

结果

预测准确率:  0.9333333333333333

求不同取值k的结果
   k   距离函数     预测准确率
0  1  l1_distance  0.933333
1  3  l1_distance  0.933333
2  5  l1_distance  0.977778
3  7  l1_distance  0.955556
4  9  l1_distance  0.955556
5  1  l2_distance  0.933333
6  3  l2_distance  0.933333
7  5  l2_distance  0.977778
8  7  l2_distance  0.977778
9  9  l2_distance  0.977778
posted @   Czone  阅读(14)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示