【自学笔记】KNN算法

介绍

  KNN算法,全称为K-Nearest Neighbors Algorithm,中文名K近邻算法,是一种监督学习算法,既可以用于分类问题也可以用于回归问题。

步骤

  1.选择K个数和一个距离度量方法。
  2.找到距离最近的K个样本(k-近邻)。
  3.以服从多数原则确定预测结果。

常用的距离度量方法

  1.曼哈顿距离 ∑ ∣ x i − x i ′ ∣ \sum |x_{i} - x^{'}_{i}| xixi
  2.欧几里得距离 ∑ ( x i − x i ′ ) 2 \sqrt{\sum (x_{i} - x^{'}_{i})^{2}} (xixi)2
以下为使用欧几里得距离的原理图:
在这里插入图片描述

优缺点

KNN算法的优点包括:
  算法简单,易于理解和实现。
  不需要训练模型,即不需要显式的训练过程,模型维护成本低。
  对于多分类问题有较好的效果。
  对数据分布没有假设,适用于各种类型的数据。

然而,KNN算法也有一些缺点
  计算量大,尤其是当数据集非常大时,计算所有点之间的距离会非常耗时。
  对于高维数据,KNN算法的表现可能不佳,因为高维空间中“近邻”的概念可能变得模糊。
  对于不平衡的数据集,KNN算法容易偏向于数量较多的类别。
  K值的选择对结果影响较大,需要通过交叉验证等方法来确定最优的K值。

KNN算法实现识别手写数字

import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
import joblib

# 加载MNIST数据集
mnist = fetch_openml('mnist_784')
X, y = mnist['data'], mnist['target']

# 数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 数据标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.astype(float))
X_test_scaled = scaler.transform(X_test.astype(float))

# 创建KNN分类器
knn = KNeighborsClassifier(n_neighbors=3)

# 训练模型
knn.fit(X_train_scaled, y_train)

joblib.dump(knn, 'knn_model.pkl')  # 模型将被保存为knn_model.pkl文件

# 加载模型
with open('static/knn_model.pkl', 'rb') as file:
    model = joblib.load(file)
# 使用模型
# 注意data的格式为[[1 * 784]]的二维数组
prediction = model.predict(data)
print('Prediction:', prediction[0])

手写KNN算法

我们采用最普通的写法(别管效率 ,看懂就行),优化可以采用numpy的各项操作

import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split

class KNN:
    def __init__(self, c=100, n=28 * 28, k=30):
        self.c = c  # 系数
        self.n = n  # 数组大小
        self.k = k  # 选取前几
    def fit(self, X, y):
        # 处理数据
        self.X_train = X
        self.y_train = y
    def d(self, x1, x2):
        # 求距离
        k = 0
        for i in range(self.n):
            k += abs(x1[i] - x2[i])
        k /= self.c
        return k
    def find(self, X):
        # 判断属于哪个类别
        d = []
        for i in range(len(self.X_train)):
            d.append(self.d(X, self.X_train.iloc[i]))
        # 初始化数组计数,注意y中的数据为字符
        m = {}
        for i in range(10):
            m[str(i)] = 0
        for i in range(self.k):
            mn = 1e9
            id = -1
            for j in range(len(d)):
                if d[j] < mn:
                    mn = d[j]
                    id = j
            m[self.y_train.iloc[id]] += 1
            d[id] = 1e9
        ans = -1
        cnt = 0
        for u, v in m.items():
            if v > cnt:
                ans = u
                cnt = v
        return ans

# 加载MNIST数据集
mnist = fetch_openml('mnist_784')

# 注意,X,y为dataframe类型
X, y = mnist['data'], mnist['target']

knn = KNN()
knn.fit(X, y)

# 随便测试一下
print(knn.find(X.iloc[0]))
print(y.iloc[0])

补充

  问:上文提到,KNN算法不适合用在高维数据上,那为什么识别手写数字高达28*28维却可以用呢?
  答:尽管每个图像有784(28x28)个像素,但手写数字图像通常在信息量上并不是完全独立的。像素之间存在相关性,这意味着数据的实际信息维度可能远低于784。例如,一个数字的某些部分(如“1”的直线或“0”的环形)在多个像素上表现了一致的特征。这种内在的结构可以被KNN利用来识别模式。
  此外,MNIST数据集足够大。在高维空间中,数据点分散,但如果数据集足够大,仍然可以找到足够多的“近邻”点来做出准确的分类。

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值