基于JavaScript 实现近邻算法以及优化方案

前言

近邻算法(K-Nearest Neighbors,简称 KNN)是一种简单的、广泛使用的分类和回归算法。它的基本思想是:给定一个待分类的样本,找到这个样本在特征空间中距离最近的 k 个样本,这 k 个样本的多数类别作为待分类样本的类别。

本教程文章将详细讲解如何使用 JavaScript 实现一个简单的 KNN 算法,我们会从理论出发,逐步从零开始编写代码。

理论基础

距离度量

KNN 算法的核心是计算两个样本之间的距离,常用的距离度量方法有:

  • 欧氏距离(Euclidean Distance)
  • 曼哈顿距离(Manhattan Distance)

在本教程中,我们将使用最常见的欧氏距离来计算样本之间的距离。

欧氏距离公式如下:

[ d(p, q) = \sqrt{\sum_{i=1}^{n} (p_i - q_i)^2} ]

其中 ( p ) 和 ( q ) 是两个 n 维空间中的点, ( p_i ) 和 ( q_i ) 是这两个点在第 ( i ) 维的坐标。

算法步骤

  1. 计算距离:计算待分类样本与训练样本集中所有样本的距离。
  2. 排序:按距离从小到大对所有距离进行排序。
  3. 选择最近的 k 个样本:从排序后的结果中选择距离最近的 k 个样本。
  4. 投票:多数投票决定待分类样本的类别。

实现步骤

初始化数据

首先,我们需要一些样本数据来进行分类。假设我们有以下二维数据集:

const trainingData = [{ x: 1, y: 2, label: 'A' },{ x: 2, y: 3, label: 'A' },{ x: 3, y: 3, label: 'B' },{ x: 6, y: 5, label: 'B' },{ x: 7, y: 8, label: 'B' },{ x: 8, y: 8, label: 'A' },
];

计算距离

编写一个函数来计算两个点之间的欧氏距离:

function euclideanDistance(point1, point2) {return Math.sqrt(Math.pow(point1.x - point2.x, 2) +Math.pow(point1.y - point2.y, 2));
}

排序并选择最近的 k 个样本

编写一个函数,根据距离对样本进行排序,并选择距离最近的 k 个样本:

function getKNearestNeighbors(trainingData, testPoint, k) {const distances = trainingData.map((dataPoint) => ({...dataPoint,distance: euclideanDistance(dataPoint, testPoint)}));distances.sort((a, b) => a.distance - b.distance);return distances.slice(0, k);
}

多数投票

编写一个函数,通过多数投票来决定类别:

function majorityVote(neighbors) {const voteCounts = neighbors.reduce((acc, neighbor) => {acc[neighbor.label] = (acc[neighbor.label] || 0) + 1;return acc;}, {});return Object.keys(voteCounts).reduce((a, b) => voteCounts[a] > voteCounts[b] ? a : b);
}

主函数

最后,编写一个主函数来整合上述步骤,完成 KNN 算法:

function knn(trainingData, testPoint, k) {const neighbors = getKNearestNeighbors(trainingData, testPoint, k);return majorityVote(neighbors);
}

测试

现在我们来测试一下这个 KNN 实现:

const testPoint = { x: 5, y: 5 };
const k = 3;const result = knn(trainingData, testPoint, k);
console.log(`The predicted label for the test point is: ${result}`);

运行这个代码,你会得到预测的类别。

优化方案

虽然我们已经实现了一个基本的 KNN 算法,但在实际应用中,我们还可以进行一些优化和扩展,使其更加高效和实用。

优化距离计算

在大数据集上,计算每个点之间的欧氏距离可能会很耗时。我们可以通过一些高效的数据结构如 KD 树(K-Dimensional Tree)来进行快速邻近搜索。以下是一个简单的 KD 树的实现示例:

class KDTree {constructor(points, depth = 0) {if (points.length === 0) {this.node = null;return;}const k = 2; // 2Dconst axis = depth % k;points.sort((a, b) => a[axis] - b[axis]);const median = Math.floor(points.length / 2);this.node = points[median];this.left = new KDTree(points.slice(0, median), depth + 1);this.right = new KDTree(points.slice(median + 1), depth + 1);}nearest(point, depth = 0, best = null) {if (this.node === null) {return best;}const k = 2;const axis = depth % k;let nextBranch = null;let oppositeBranch = null;if (point[axis] < this.node[axis]) {nextBranch = this.left;oppositeBranch = this.right;} else {nextBranch = this.right;oppositeBranch = this.left;}best = nextBranch.nearest(point, depth + 1, best);const dist = euclideanDistance(point, this.node);if (best === null || dist < euclideanDistance(point, best)) {best = this.node;}if (Math.abs(point[axis] - this.node[axis]) < euclideanDistance(point, best)) {best = oppositeBranch.nearest(point, depth + 1, best);}return best;}
}const points = trainingData.map(point => [point.x, point.y, point.label]);
const kdTree = new KDTree(points);const nearestPoint = kdTree.nearest([testPoint.x, testPoint.y]);
console.log(`The nearest point is: ${nearestPoint[2]}`);

考虑不同距离度量

不同的距离度量方法在不同的场景下可能会有不同的效果。除了欧氏距离外,还可以尝试以下几种距离度量方法:

  • 曼哈顿距离(Manhattan Distance)
  • 闵可夫斯基距离(Minkowski Distance)
  • 切比雪夫距离(Chebyshev Distance)

我们可以编写一些函数来实现这些距离度量方法,并在主函数中进行选择:

function manhattanDistance(point1, point2) {return Math.abs(point1.x - point2.x) + Math.abs(point1.y - point2.y);
}function minkowskiDistance(point1, point2, p) {return Math.pow(Math.pow(Math.abs(point1.x - point2.x), p) +Math.pow(Math.abs(point1.y - point2.y), p),1 / p);
}function chebyshevDistance(point1, point2) {return Math.max(Math.abs(point1.x - point2.x), Math.abs(point1.y - point2.y));
}

调整 k 值

选择合适的 k 值对算法的性能至关重要。过小的 k 值可能导致过拟合,而过大的 k 值可能导致欠拟合。一个常见的做法是通过交叉验证来选择最优的 k 值。

处理多维数据

在实际应用中,数据通常是多维的。我们的算法已经可以处理二维数据,但对于多维数据,只需稍微调整距离计算函数即可:

function euclideanDistanceND(point1, point2) {let sum = 0;for (let i = 0; i < point1.length; i++) {sum += Math.pow(point1[i] - point2[i], 2);}return Math.sqrt(sum);
}

代码重构

为了更好地组织代码,我们可以将不同的功能模块化:

class KNN {constructor(k = 3, distanceMetric = euclideanDistance) {this.k = k;this.distanceMetric = distanceMetric;}fit(trainingData) {this.trainingData = trainingData;}predict(testPoint) {const neighbors = this.getKNearestNeighbors(testPoint);return this.majorityVote(neighbors);}getKNearestNeighbors(testPoint) {const distances = this.trainingData.map((dataPoint) => ({...dataPoint,distance: this.distanceMetric(dataPoint, testPoint)}));distances.sort((a, b) => a.distance - b.distance);return distances.slice(0, this.k);}majorityVote(neighbors) {const voteCounts = neighbors.reduce((acc, neighbor) => {acc[neighbor.label] = (acc[neighbor.label] || 0) + 1;return acc;}, {});return Object.keys(voteCounts).reduce((a, b) => voteCounts[a] > voteCounts[b] ? a : b);}
}// 测试代码
const knnClassifier = new KNN(3, euclideanDistance);
knnClassifier.fit(trainingData);
const predictedLabel = knnClassifier.predict(testPoint);
console.log(`The predicted label for the test point is: ${predictedLabel}`);

通过这种方式,我们不仅提高了代码的可读性和可维护性,还为将来更复杂的扩展和优化打下了基础。

结语

KNN 算法简单易懂,适用于很多分类问题,特别是在数据规模不大时。然而,KNN 的计算复杂度较高,尤其在高维数据和大规模数据集上,因此在实际应用中常常需要结合其他技术进行优化。

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

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

相关文章

【C++】<知识点> C++11新特性

文章目录 一、auto关键字 二、decltype关键字 三、nullptr关键字 四、智能指针 五、 无序容器&#xff08;哈希表&#xff09; 六、统一的初始化方法 七、成员变量默认初始值 八、范围for循环 九、右值引用与移动语义 十、lambda表达式 一、auto关键字 1. 作用&#…

Linux shell编程学习笔记58:cat /proc/mem 获取系统内存信息

0 前言 在开展系统安全检查的过程中&#xff0c;除了收集cpu信息&#xff0c;我们还需要收集内存信息。在Linux中&#xff0c;获取内存信息的命令很多&#xff0c;这里我们着重研究 cat /proc/mem命令。 1 cat /proc/mem命令 /proc/meminfo 文件提供了有关系统内存的使用情况…

280 基于matlab的摇号系统GUI界面仿真MATLAB程序

基于matlab的摇号系统GUI界面仿真MATLAB程序&#xff0c;输入总数量及摇号需求&#xff0c;进行随机性摇号&#xff0c;并对摇取的号码进行双重随机性数据检测&#xff0c;确定是否符合要求。程序已调通&#xff0c;可直接运行。 280 GUI人机交互 摇号系统GUI界面仿真 - 小红书…

技术前沿 |【大模型InstructBLIP进行指令微调】

大模型InstructBLIP进行指令微调 一、引言二、InstructBLIP模型介绍三、指令微调训练通用视觉语言模型的应用潜力四、InstructBLIP的指令微调训练步骤五、实验结果与讨论六、结论与展望 一、引言 随着人工智能技术的快速发展&#xff0c;视觉语言模型&#xff08;Vision-Langu…

使用SourceTree切换不同的托管平台

背景&#xff1a;sourcetree一开始绑定了gitee&#xff0c;想拉取github的项目时拉取不了 原因&#xff1a;git绑定的账号&#xff08;邮箱&#xff09;、密码不一致 解决办法&#xff1a; 重新设置账号密码 在windows种可找到下面的文件夹&#xff0c;进行删除 C:\Users\US…

5.1 实体完整性

一个表只能有一个主键约束&#xff0c;且主键约束不能取空值。 通过unique约束定义唯一性&#xff0c;为了保证一个表非主键列不输入重复值&#xff0c;可在该列定义unique约束。 primary key约束与unique约束主要区别如下。 (1)一个表只能创建一个primary key约束&#xff0…

让GNSSRTK不再难【第一天】

第1讲 GNSS系统组成以及应用 北斗导航科普动画_哔哩哔哩_bilibili 1.1 GNSS系统 1.1.1 基本概念 全球卫星导航系统&#xff08;Global Navigation Satellite System, GNSS&#xff09;&#xff0c;是能在地球表面或近地空间的任何地点为用户提供全天候的三维坐标、速度以及…

STM32-电灯,仿真

目录 前言: 一. 配置vscode 二. 新创建软件工程 三. 仿真 1.新建工程想到,选择名称和路径 2.从选中的模板创建原理图 3.不创建PCB布版设计 4.选择没有固件项目 5.完成 四.源码 五. 运行效果 六. 总结 前言: 这篇主要是配置vscode和创建仿真,和点灯的完整代码,欢迎大…

在Windows上用Llama Factory微调Llama 3的基本操作

这篇博客参考了一些文章&#xff0c;例如&#xff1a;教程&#xff1a;利用LLaMA_Factory微调llama3:8b大模型_llama3模型微调保存-CSDN博客 也可以参考Llama Factory的Readme&#xff1a;GitHub - hiyouga/LLaMA-Factory: Unify Efficient Fine-Tuning of 100 LLMsUnify Effi…

美琳莱卡:创新消费模式引领新零售时代

公司成立时间与定位 美琳莱卡自创立之初,便以独特的视角和前瞻性的战略定位,立足于消费市场的变革前沿。公司成立于2024年,正值全球数字化浪潮蓬勃兴起,消费升级趋势日益明显之际。美琳莱卡敏锐地捕捉到这一时代机遇,将自身定位为创新消费模式的引领者,致力于通过线上线下高度…

攻防演练之-网络集结号

每一次的网络安全攻防演练都是各个安全厂商期待的网络安全盛会&#xff0c;因为目前的安全生态导致了只有在网络安全攻防演练期间&#xff0c;网络安全的价值才会走向台前&#xff0c;收到相关方的重视。虽然每一次都会由于各种原因不能如期举行&#xff0c;但是这一次的推迟总…

idea最新专业版安装+maven配置教程!

本教程适用于 J B 全系列产品&#xff0c;包括 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、CLion、AppCode 等。 &#xff08;直接复制&#xff0c;拿走不谢&#xff09; 9H1390TRAK-eyJsaWNlbnNlSWQiOiI5SDEzOTBUUkFLIiwibGljZW5zZWVOYW1lIjoi5rC45LmF5rA5rS7I…

MySQL之查询性能优化(七)

查询性能优化 排序优化 无论如何排序都是一个成本很高的操作&#xff0c;所以从性能角度考虑&#xff0c;应尽可能避免排序或者尽可能避免对大量数据进行排序。前面已经提到了&#xff0c;当不能使用索引生成排序结果的时候&#xff0c;MySQL需要自己进行排序&#xff0c;如果…

【传知代码】上下位关系自动检测方法(论文复现)

前言&#xff1a;在信息爆炸的时代&#xff0c;我们每天都沉浸在海量的数据和信息中。随着互联网技术的飞速发展&#xff0c;如何从这些信息中准确、高效地提取出有用的知识&#xff0c;成为了当下研究的热点。其中&#xff0c;上下位关系&#xff08;也称为层级关系或种属关系…

vscode 中 eslint 无效?npm init 是什么?

vscode 中 eslint 无效 我想要给一个项目添加 eslint&#xff0c;按照 eslint 官方指南操作&#xff1a; npm init eslint/configlatest自动安装了相关依赖并创建配置文件 eslint.config.mjs。 按理说&#xff0c;此刻项目应该已经配置好 eslint 了。但是我的编辑器 vscode …

《python程序语言设计》2018版第5章第36题改造4.17 石头 剪刀 布某一方超过2次就结束。

代码编写记录 2024.05.04 05.36.01version 换一个什么数代替剪子 我先建立一个函数judgement condition 石头3 剪子2 布1 如何构建一个循环进行的架构&#xff0c;是我们最需要的想法 循环以什么条件开始呢 是小于2个还是大于2个。 guess_num random.randint(1, 3) computer…

Python 和 Java 实现云计算的最终年项目

1、问题背景 目前&#xff0c;我正在进行我的最终年项目&#xff0c;计划用 Python 编写一个云计算系统&#xff0c;而云客户端将由我的团队成员使用 Java 来编写。这个云客户端将具有一个带有标签的界面&#xff0c;并提供文本编辑器、媒体播放器、几个基于 Java 的小游戏以及…

按键精灵在Win11中弹窗出现乱码并且自带的部分系统插件不能使用的解决方法

按键精灵中出现以下问题&#xff1a; 提示信息的弹窗出现乱码&#xff1a; 系统自带的部分像 plugin. 开头的插件不能使用&#xff0c;如下&#xff1a;s Plugin.Sys.GetDateTime() screenX Plugin.GetSysInfo.GetScreenResolutionX screenY Plugin.GetSysInfo.GetScreenRe…

⌈ 传知代码 ⌋ 记忆大师

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

IDEA去除代码和XML中的波浪线(黄色警告线)

通常情况下&#xff0c;IDE自带的侦测功能会帮我们提示一些错误、警告等。但这对于强迫症患者来说并不友好。以下是去除IDE代码和XML文件中的波浪线&#xff08;黄色警告线&#xff09;、拯救强迫症患者的有效方案 1、去除XML中的波浪线 2、去除代码中的波浪线 关爱强迫症患者…