ML 系列:机器学习和深度学习的深层次总结( 15) — KNN — 第 1 部分

news2024/11/26 0:39:53

一、说明

        K-最近邻 (KNN) 算法是一种流行的监督机器学习算法,用于分类和回归任务。它是非参数惰性学习算法的一个典型例子。KNN 被认为是一种惰性学习算法,因为它在训练阶段不对底层数据分布做出任何假设,也不从训练数据中学习特定模型。相反,它是一种“惰性”或“延迟”学习,它只是记住训练数据集。

二、KNN算法

        KNN 也是一种非参数算法,因为它没有在训练过程中确定的固定数量的参数。KNN 中的参数数量会随着训练数据的大小而增长,因为每个数据点都会成为一个参数。这与线性回归或神经网络等参数模型不同,这些模型无论训练数据大小如何,都有固定数量的参数。

        在 KNN 中,根据 k 个最接近匹配的训练样本中的多数标签,将一个标签(表示为 x_i)分配给一个实例。这种方法允许 KNN 通过比较实例与其相邻数据点之间的相似性来进行预测。
在 K 最近邻 (KNN) 算法中,通过使用距离度量搜索其最近邻居来定位数据点。然后,该算法根据多数类或其 k 个最近邻居的平均值对输出进行分类或预测。例如,在 k=5 的分类问题中,当向算法呈现新的数据点时,将考虑训练集中距离新点最近的五个数据点(由欧几里得距离等距离度量确定)。新点的类标签是根据其五个邻居中的多数类分配的。在回归问题中,算法不是分配类标签,而是通过取其 k 个最近邻居的平均值来预测输出值。图 9-4 说明了 KNN 算法的工作原理。

图 1.“KNN”算法的工作原理

        新数据点用问号表示其未知的标签或值。如前所述,该算法利用 k 个最接近的训练样本中的多数标签对新数据点的输出进行分类或预测。

        在 KNN 中,超参数 k 的选择至关重要,因为它决定了用于预测的邻居数量。k 值越小,决策边界就越复杂,这可能会增加过度拟合训练数据的风险。另一方面,k 值越大,决策边界就越平滑,但可能会导致欠拟合,即模型过度简化数据点之间的关系。

        确定 k 的最佳值通常取决于手头的具体问题,可以通过实验或交叉验证来完成。选择合适的 k 值以在未知数据上实现令人满意的性能,在模型复杂性和泛化之间取得平衡至关重要。

图2显示了选择k值的效果。

图 2. 不同 k 值的选择

图2中,横轴表示特征个数或者说数字k,横轴也表示模型的准确率。

        在本教程中,我们将从头开始探索在 Python 中实现 K-最近邻 (KNN) 算法。KNN 是一种用于分类和回归任务的基本机器学习算法。通过从头开始构建 KNN,我们将更深入地了解其内部工作原理,并学习如何将其应用于现实世界的数据集。

让我们深入研究如何逐步实现 KNN 算法!

三、KNN算法的Python实现

3.1 步骤 1:导入库

        我们首先导入必要的库。我们将使用 NumPy 进行数值计算、使用 Matplotlib 进行数据可视化、使用 mlxtend 绘制决策区域,并使用 scikit-learn 生成合成数据并将其拆分为训练集和测试集。

import numpy as np
import matplotlib.pyplot as plt
from mlxtend.plotting import plot_decision_regions
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

3.2 第 2 步:定义 KNN 类

        该类KNN是我们 K-最近邻 (KNN) 算法实现的核心组件。在这个类中,我们封装了实例化、训练和利用 KNN 模型进行预测所需的所有基本功能。该类中的关键方法包括:

  • __init__:使用默认值初始化 KNN 分类器k,表示预测期间要考虑的邻居数量。
  • fit:将模型与提供的训练数据进行拟合,存储训练特征(X_train)和相应的标签(y_train)。
  • euclidean_distance:计算两个数据点之间的欧几里得距离,这是确定实例之间相似性的关键指标。
  • predict:获取一组输入数据点并返回基于 k 个最近邻的预测标签数组。
  • _predictpredict:通过识别 k 个最近邻并应用多数投票方案来计算单个数据点的预测所使用的内部方法。

        通过将这些功能封装在KNN类中,我们创建了 KNN 算法的模块化和可重用实现,使其更易于理解、维护和扩展。这是 KNN 类:

class KNNClassification:
    def __init__(self, k=3):
        self.k = k

    def fit(self, X, y):
        self.X_train = X
        self.y_train = y

    def euclidean_distance(self, x1, x2):
        return np.sqrt(np.sum((x1 - x2)**2))

    def predict(self, X):
        y_pred = [self._predict(x) for x in X]
        return np.array(y_pred)

    def _predict(self, x):
        distances = [self.euclidean_distance(x, x_train) for x_train in self.X_train]
        k_indices = np.argsort(distances)[:self.k]
        k_nearest_labels = [self.y_train[i] for i in k_indices]
        most_common = max(set(k_nearest_labels), key=k_nearest_labels.count)
        return most_common

def plot_data(X, y):
    # (o==circle, for train) and (s==square for test)
    plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, s=100, cmap='jet', marker='o')
    plt.scatter(x_test[:, 0], x_test[:, 1], c=y_test, s=100, cmap='jet', marker='s')
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.title('Generated Data')
    plt.show()

        这部分代码是主要执行部分,我们在其中实例化 KNN 算法,在合成数据上对其进行训练,可视化决策边界,评估其性能,并绘制训练和测试数据点以及预测标签。

class KNN:
    def __init__(self, k=3):
        self.k = k

    def fit(self, X, y):
        self.X_train = X
        self.y_train = y

    def euclidean_distance(self, x1, x2):
        return np.sqrt(np.sum((x1 - x2)**2))

    def predict(self, X):
        y_pred = [self._predict(x) for x in X]
        return np.array(y_pred)

    def _predict(self, x):
        distances = [self.euclidean_distance(x, x_train) for x_train in self.X_train]
        k_indices = np.argsort(distances)[:self.k]
        k_nearest_labels = [self.y_train[i] for i in k_indices]
        most_common = max(set(k_nearest_labels), key=k_nearest_labels.count)
        return most_common

def plot_data(X, y):
    plt.scatter(X[:, 0], X[:, 1], c=y, 
                s=100, cmap='jet', marker='o')
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.title('Generated Data')
    plt.show()

if __name__ == "__main__":
    # Generate synthetic data using scikit-learn's make_classification function
    X, y = make_classification(n_samples=20, n_features=2, 
                               n_informative=2, n_redundant=0,
                               n_clusters_per_class=1, 
                               class_sep=1., random_state=23)
    plot_data(X, y)

    # Split data into training and testing sets
    x_train, x_test, y_train, y_test = train_test_split(X, y, 
                                                        test_size=0.5, 
                                                        random_state=19)

    knn = KNN(k=3)
    knn.fit(x_train, y_train)

    plot_decision_regions(X, y, clf=knn, legend=2)
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.title('KNN Decision Boundary')
    plt.show()

    # Predict classes for the test data
    y_pred = knn.predict(x_test)

    # Calculate accuracy
    accuracy = np.mean(y_pred == y_test) * 100
    print(f"Accuracy: {accuracy:.2f}%")

    # Plot the training and test data (o==circle, for train) and (s==square for test)
    plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, s=100, 
                cmap='jet', marker='o', label='Train (Circle)')
    
    plt.scatter(x_test[:, 0], x_test[:, 1], c=y_test, s=100, 
                cmap='jet', marker='s', label='Test (Square)')
    
    plt.scatter(x_test[:, 0], x_test[:, 1], c=y_pred, s=150, 
                cmap='jet', marker='x', label='Predicted Test (Cross)')
    
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.title(f'Train-Test, Predicted, accuracy = {accuracy}')
    plt.legend()
    plt.show()

四、K 最近邻的挑战

        虽然 K-最近邻 (KNN) 是一种简单有效的算法,但它也面临挑战。在本次讨论中,我们将探讨使用 KNN 时遇到的一些常见问题,并通过代码示例进行演示。通过研究 KNN 如何处理不断增加的数据集大小,我们旨在说明该算法的可扩展性限制,并阐明计算时间和内存使用之间的权衡。让我们深入研究代码以揭示这些挑战。

import time

# Define a range of sample sizes to test
sample_sizes = [1000, 5000, 10000, 50000, 100000]

# Lists to store time and memory usage for each sample size
execution_times = []
memory_usages = []

# Loop through each sample size
for sample_size in sample_sizes:
    print(f"Testing with {sample_size} data points...")
    
    # Generate synthetic data
    X, y = make_classification(n_samples=sample_size, n_features=2, n_informative=2, n_redundant=0,
                               n_clusters_per_class=1, class_sep=1., random_state=42)
    
    # Split data into training and testing sets
    x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=42)
    
    # Calculate memory usage for training data
    train_memory_usage = x_train.size * x_train.itemsize
    memory_usages.append(train_memory_usage)
    
    # Instantiate and fit the KNN model
    model = KNN(3)
    model.fit(x_train, y_train)
    
    # Measure prediction time for a single test data point
    start_time = time.time()
    model.predict(x_test[[0], :])
    end_time = time.time()
    execution_time = end_time - start_time
    execution_times.append(execution_time)

# Plotting the results
plt.figure(figsize=(10, 5))

# Plot execution time
plt.subplot(1, 2, 1)
plt.plot(sample_sizes, execution_times, marker='o')
plt.xlabel('Number of Data Points')
plt.ylabel('Execution Time (seconds)')
plt.title('Execution Time vs. Number of Data Points')

# Plot memory usage
plt.subplot(1, 2, 2)
plt.plot(sample_sizes, memory_usages, marker='o')
plt.xlabel('Number of Data Points')
plt.ylabel('Memory Usage (bytes)')
plt.title('Memory Usage vs. Number of Data Points')

plt.tight_layout()
plt.show()

运行上述代码后,我们可以看到如下图所示的情节:

图 1. KNN 的计算时间和内存使用情况

五、KNN 回归:使用 K 最近邻进行预测建模

        KNN 回归涉及通过确定每个数据点的最近邻居目标值的平均值来预测连续目标值。我想改变以前的代码并将其转换为适合回归的代码。

回归和分类代码之间的差异:

  1. 数据集生成:
  • 上一个(分类):使用“make_classification”生成数据集以创建合成分类数据。
  • 当前(回归):使用“make_regression”生成合成回归数据。

2.模型预测:

  • KNN 模型预测每个数据点的离散类标签而不是连续的目标值。

3.评估指标:

  • 由于这是一项回归任务,因此可以使用传统评估指标(例如均方误差(MSE)或 R 平方)来代替准确度。

4.可视化:

  • 之前:使用“plot_decision_regions”可视化决策边界以说明分类区域。
  • 当前:回归线可视化,以展示 KNN 模型对回归任务的预测能力。

5.标签编码:

  • 回归不需要标签编码,因为目标值是连续的而不是整数。

以下是使用 KNN 进行回归的修改后的代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split

class KNNRegression:
    def __init__(self, k=3):
        self.k = k

    def fit(self, X, y):
        self.X_train = X
        self.y_train = y

    def euclidean_distance(self, x1, x2):
        return np.sqrt(np.sum((x1 - x2)**2))

    def predict(self, X):
        y_pred = [self._predict(x) for x in X]
        return np.array(y_pred)

    def _predict(self, x):
        distances = [self.euclidean_distance(x, x_train) for x_train in self.X_train]
        k_indices = np.argsort(distances)[:self.k]
        k_nearest_labels = [self.y_train[i] for i in k_indices]
        return np.mean(k_nearest_labels)

if __name__ == "__main__":
    # Generate synthetic regression data
    X, y = make_regression(n_samples=100, n_features=1, noise=10, random_state=42)

    # Instantiate and fit the KNN regression model
    knn = KNNRegression(k=3)
    knn.fit(X, y)

    # Generate a range of feature values for plotting the regression line
    x_range = np.linspace(min(X), max(X), num=100).reshape(-1, 1)
    # Predict target values for the feature range
    y_pred = knn.predict(x_range)

    # Plot the data points and the regression line
    plt.scatter(X, y, color='blue', s=30, marker='o', label='Data')
    plt.plot(x_range, y_pred, color='red', linewidth=2, label='KNN Regression')
    plt.xlabel('Feature')
    plt.ylabel('Target')
    plt.title('KNN Regression')
    plt.legend()
    plt.show()

通过平滑的回归线描绘特征和目标变量之间的关系:

图 2. KNN 回归线

六、探索 K 最近邻 (KNN) 算法中 K 的影响:平衡过度拟合和欠拟合

        在本分析中,我们深入探讨了 K 最近邻 (KNN) 算法中参数“K”的意义及其对分类准确性、决策边界、过度拟合和欠拟合的影响。

        在 K 最近邻 (KNN) 算法中,如果 K 值太小,通常就会发生过拟合。如果 K 值较小,模型就会对数据中的噪声和异常值过于敏感,从而导致决策边界过于复杂,与训练数据拟合得过于紧密。这可能会导致模型对未知数据的泛化能力较差,因为模型可能会捕捉噪声而不是底层模式。

        相反,当K 值过大时,可能会出现欠拟合。如果 K 值过大,模型会对过多的邻居进行平均,导致决策边界过于简单,无法捕捉数据的真实结构。这会导致训练和测试数据集的准确率都很低,表明模型无法充分捕捉底层模式的复杂性。

        通过系统地改变 K 的值并观察其对模型性能和行为的影响,我们可以深入了解所涉及的权衡,并发现基于 KNN 任务中参数选择的最佳策略,确保过度拟合和欠拟合之间的平衡。

from sklearn.metrics import mean_squared_error

def test_k_values(X_train, y_train, X_range, k_values):
    fig, axs = plt.subplots(2, 2, figsize=(12, 10))
    axs = axs.flatten()
    for i, k in enumerate(k_values):
        
        # The regression class that we wrote in the previous codes
        knn_reg = KNNRegression(k=k)

        # Fit the KNN regression model to the training data
        knn_reg.fit(X_train, y_train)

        # Predict target values for the range of feature values
        y_pred = knn_reg.predict(X_range)
        mse = mean_squared_error(y_train, knn_reg.predict(X_train))
        axs[i].scatter(X_train, y_train, color='blue', s=30, marker='o', label='Data')
        axs[i].plot(X_range, y_pred, color='red', linewidth=2, label=f'KNN Regression (K={k}), MSE: {mse:.2f}')
        axs[i].set_xlabel('Feature')
        axs[i].set_ylabel('Target')
        axs[i].set_title(f'KNN Regression (K={k})')
        axs[i].legend()
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    # Generate synthetic regression data
    X, y = make_regression(n_samples=100, n_features=1, noise=10, random_state=42)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=42)
    
    # Generate a range of feature values for plotting the regression line
    X_range = np.linspace(min(X), max(X), num=100).reshape(-1, 1)
    
    # Test different values of K and plot the results
    k_values = [1, 3, 5, 7]
    test_k_values(X_train, y_train, X_range, k_values)

这是代码的输出:

图 3. 在网格中绘制结果

概述 K 最近邻 (KNN) 的优缺点:

图 4. KNN 的优缺点

七、K 最近邻 (KNN) 算法的增强和扩展:解决限制并提高性能

        K 最近邻 (KNN) 算法虽然简单直观,但也有其缺点。然而,研究人员和从业者已经开发出各种改进和扩展来解决这些限制。这些方法中的每一种都旨在解决算法缺点的特定方面。例如,加权 KNN 或距离加权 KNN等技术试图通过根据距离为相邻点分配不同的权重来减轻算法对不相关特征的敏感性。同样,KD 树Ball 树等方法通过将数据组织成空间数据结构来优化算法对大型数据集的计算效率。在我们机器学习系列的下一篇中,我们将深入研究这些改进和扩展,探索它们如何增强 KNN 算法的性能和多功能性。

八、结论 

        在第 15 天,我们讨论了 KNN 的概念以及从头开始的分类和回归实现。在即将发布的文章《ML 系列:第 16 天 — 提高 KNN 效率:使用 KD 树和 Ball 树实现更快的算法》中,我将探讨 KNN、KD 树和 Ball 树的一些改进。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2202111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp开发 使用vscode代替hbuilderx开发工具

前言:最近接手了一个uniapp项目,很久没有使用hbuilderx了,用着实在难受,并且还总报奇奇怪怪的错误,首次运行要下载很多插件,搜索文件也用不习惯,加上很多鼠标操作,实在是太影响开发效…

【MATLAB源码-第241期】基于simulink的异步buck降压电路仿真,输出UL IL UR以及PWM波形。

操作环境: MATLAB 2022a 1、算法描述 Buck变换器是一种直流降压转换器,旨在将较高的输入电压转换为较低的输出电压,并广泛应用于各种电源管理系统中。图中的Buck变换器模型通过一系列的电力电子元件和控制信号实现降压功能。 首先&#xf…

QT:“提升为“使用(自定义控件)

目录 一.步骤与作用 1.步骤 2.作用 二.使用 1.mainwindow.ui ->拖一个 Push Button 控件到画布->右击Push Button弹出对话框->单击"提升为" 2.输入提升类名称MyButton->点击添加 3.选择基类名称为QPushButton,点击提升 4.新建MyButton文件 5.在…

《Java基础》变量和数据类型

综述 在开始学习变量之前,我们思考一下为什么需要使用变量。 首先我们从小开始学习加法减法的时候,后来我们再学更难的东西就是代数,其中的x和y是我们要求解的内容,这些内容就是变量。 变量是人的思维的提升,没有变量…

无人机侦测:手提式无线电侦测设备技术详解

手提式无线电侦测设备在无人机侦测中扮演着重要角色,它主要通过侦测无人机与遥控器或地面站之间的无线电信号来实现对无人机的监测和定位。以下是对手提式无线电侦测设备技术的详细解析: 一、技术原理 手提式无线电侦测设备通过无线电侦测技术&#xf…

vue基础(总结很详细)

vue 基础 1. vue 是什么? Vue.js (读音 /vju ː /, 类似于 view ) 是一套构建用户界面的渐进式框架。 Vue 只关注视图层, 采用自底向上增量开发的设计。 Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图…

Day2 IDEA

使用IDEA开发第一个程序 代码结构:Project - Module - Package - Class 作用:便于管理代码 例如: 创建一个空工程 创建module模块 创建package,一般以公司域名倒写技术名称 例如:com.test.hello 创建类 class He…

使用QFileInfo类判断同名文件存在

问题描述:使用QT保存文件时,如果文件名相同,保存后原来的文件会被覆盖引用博客 首先在QT项目中新建一个按钮 再右击选择转到槽,这时候就会跳转到刚刚生成的槽函数 代码 void MainWindow::on_pushButton_5_clicked() {QString fileName QF…

nginx 资料整理(一)

nginx 1. 简介2. web服务器2.1 相关文件2.2 主配置文件1. 全局块(events之前的部分)2. events块(events{...})3. http块(http{...}) 2.3 子配置文件 官网 https://nginx.org/ 1. 简介 nginx (engine x) 是…

DeerPlus 2.85| 外语学习利器,趣味闯关学习外语

DeerPlus 是一款非常专业的外语学习应用,支持多种语言的学习,如汉语、英语、日语、韩语、法语、西班牙语、德语等,从单词、发音、语法等各个方面全面系统地教你学习,让你快速掌握多种语言。特点:- 10多种游戏模式- 100…

计算机毕业设计 基于Python音乐平台的设计与实现 Python毕业设计 Python毕业设计选题 Vue 前后端分离【附源码+安装调试】

博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…

C++核心编程和桌面应用开发 第九天(静态多态 动态多态 纯虚函数 抽象类 虚析构 纯虚析构 向上向下类型转换 重载重写重定义)

目录 1.1静态多态 1.2动态多态 1.2.1满足条件 1.2.2动态多态的使用条件 1.3纯虚函数和抽象类 1.3.1纯虚函数 1.3.2抽象类 1.4虚析构/纯虚析构 1.5向上向下类型转换 1.5.1向下类型转换 1.5.2向上类型转换 1.5.3多态中的类型转换 1.6重载重写重定义 1.6.1重载 1.6…

中国工业大模型行业发展研究报告

**工业大模型伴随着大模型技术的发展,逐渐渗透至工业,处于萌芽阶段。**就大模型的本质而言,是由一系列参数化的数学函数组成的计算系统,且是一个概率模型,其工作机制是基于概率和统计推动进行的,而非真正的…

leetcode 93.复原ip地址

1.题目要求&#xff1a; 2.题目代码: class Solution { public:vector<string> result;// 记录结果// startIndex: 搜索的起始位置&#xff0c;pointNum:添加逗点的数量void backtracking(string& s, int startIndex, int pointNum) {if (pointNum 3) { // 逗点数…

线性回归逻辑回归-笔记

一、线性回归&#xff08;Linear Regression&#xff09; 1. 定义 线性回归是一种用于回归问题的算法&#xff0c;旨在找到输入特征与输出值之间的线性关系。它试图通过拟合一条直线来最小化预测值与真实值之间的误差。 2. 模型表示 线性回归模型假设目标变量&#xff08;输…

Vue3浮动按钮(FloatButton)

效果如下图&#xff1a;在线预览 APIs FloatButton 参数说明类型默认值left按钮定位的左边距&#xff0c;单位 pxnumber | stringundefinedright按钮定位的右边距&#xff0c;单位 pxnumber | string24top按钮定位的上边距&#xff0c;单位 pxnumber | stringundefinedbottom…

优惠点餐api接口对接的具体步骤是什么?

优惠点餐API接口对接的具体步骤通常包括以下几个阶段&#xff1a; 需求分析&#xff1a;明确对接的目标和需求&#xff0c;例如实现在线点餐、订单管理、支付集成等 。选择API服务提供商&#xff1a;根据业务需求选择合适的点餐API服务提供商 。注册和获取API密钥&#xff1a;…

channel是pypi与 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-f的区别。

pypi.org 是 PyPI 的默认源https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-f 是清华大学开源软件镜像站提供的 Anaconda 镜像源之一。 pypi.org&#xff1a;通常通过 pip 工具来使用。 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-f&#xff1a;…

力扣HOT100合集

力扣HOT100 - 1. 两数之和 解题思路&#xff1a; 解法一&#xff1a;暴力 class Solution {public int[] twoSum(int[] nums, int target) {int n nums.length;for (int i 0; i < n; i)for (int j i 1; j < n; j) {if (target nums[i] nums[j])return new int[] …

docker compose一键部署容器监控 CAdvisor+InfluxDB+Granfana

docker compose一键部署容器监控 CAdvisorInfluxDBGranfana CAdvisor监控收集InfluxDB存储数据Granfana展示图表 1、原生命令 通过docker stats 命令可以查看当前宿主机上所有创建的容器的CPU,内存和网络流量等信息 docker stats 缺点&#xff1a;只能查看当前宿主机的全部…