news 2026/4/25 1:58:55

Python实现K近邻算法:从原理到实战应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python实现K近邻算法:从原理到实战应用

1. 从零实现K近邻算法:Python实战指南

K近邻(K-Nearest Neighbors,简称KNN)是机器学习中最直观的算法之一,它的核心思想简单却强大:通过比较新数据与历史数据的相似度来进行预测。本文将带你从零开始实现KNN算法,不使用任何机器学习库,完全基于Python基础功能构建。

1.1 KNN算法核心原理

KNN属于"惰性学习"(lazy learning)算法,因为它不会从训练数据中学习一个明确的模型,而是将整个训练集存储起来,直到需要预测时才进行计算。这种特性使得KNN训练阶段非常快,但预测阶段相对较慢。

算法工作流程分为三个关键步骤:

  1. 计算待预测样本与所有训练样本的距离
  2. 选择距离最近的K个邻居
  3. 根据这些邻居的类别进行投票(分类)或取平均值(回归)

提示:K值的选择对算法性能影响很大。较小的K值会使模型对噪声敏感,较大的K值会使模型过于平滑。通常通过交叉验证来确定最佳K值。

1.2 欧氏距离计算实现

欧氏距离是KNN最常用的距离度量方法,它反映了多维空间中两点之间的直线距离。计算公式为:

distance = √(Σ(x_i - y_i)²)

Python实现如下:

from math import sqrt def euclidean_distance(row1, row2): """计算两个向量之间的欧氏距离 参数: row1 -- 第一个数据向量 row2 -- 第二个数据向量 返回: 两个向量之间的欧氏距离 """ distance = 0.0 for i in range(len(row1)-1): # 忽略最后一列的标签 distance += (row1[i] - row2[i])**2 return sqrt(distance)

在实际应用中,我们需要注意:

  1. 数据标准化:不同特征的量纲差异会导致距离计算偏向数值较大的特征
  2. 缺失值处理:需要提前处理或采用特殊距离度量
  3. 高维诅咒:维度很高时,所有样本的距离会趋同,影响算法效果

1.3 寻找最近邻居

有了距离计算函数后,我们需要找到距离最近的K个邻居。实现思路是:

  1. 计算目标样本与所有训练样本的距离
  2. 将距离和对应的样本一起存储
  3. 按距离排序
  4. 取前K个样本作为最近邻居
def get_neighbors(train, test_row, num_neighbors): """查找最近的邻居 参数: train -- 训练数据集 test_row -- 待预测样本 num_neighbors -- K值(邻居数量) 返回: 最近的K个邻居列表 """ distances = [] for train_row in train: dist = euclidean_distance(test_row, train_row) distances.append((train_row, dist)) # 按距离排序 distances.sort(key=lambda tup: tup[1]) # 提取最近的K个邻居 neighbors = [] for i in range(num_neighbors): neighbors.append(distances[i][0]) return neighbors

这个实现的时间复杂度是O(n),对于大数据集可能会比较慢。在实际项目中,可以考虑使用KD树或球树等数据结构来优化搜索效率。

2. KNN分类预测实现

2.1 分类预测函数

找到最近邻居后,我们需要根据这些邻居的类别进行预测。对于分类问题,最简单的策略是多数投票:

def predict_classification(train, test_row, num_neighbors): """使用KNN进行分类预测 参数: train -- 训练数据集 test_row -- 待预测样本 num_neighbors -- K值 返回: 预测的类别 """ neighbors = get_neighbors(train, test_row, num_neighbors) output_values = [row[-1] for row in neighbors] # 提取邻居的类别 # 找出出现次数最多的类别 prediction = max(set(output_values), key=output_values.count) return prediction

2.2 测试分类预测

我们可以用一个小型数据集测试这个实现:

# 测试数据集 dataset = [ [2.7810836,2.550537003,0], [1.465489372,2.362125076,0], [3.396561688,4.400293529,0], [1.38807019,1.850220317,0], [3.06407232,3.005305973,0], [7.627531214,2.759262235,1], [5.332441248,2.088626775,1], [6.922596716,1.77106367,1], [8.675418651,-0.242068655,1], [7.673756466,3.508563011,1] ] # 预测第一个样本的类别 prediction = predict_classification(dataset, dataset[0], 3) print(f"Expected {dataset[0][-1]}, Got {prediction}")

输出应该是:Expected 0, Got 0,表示预测正确。

2.3 距离加权的KNN

基础KNN算法中,所有邻居的投票权重相同。我们可以改进为距离加权投票,使更近的邻居有更大影响力:

def predict_weighted_classification(train, test_row, num_neighbors): neighbors = get_neighbors(train, test_row, num_neighbors) # 为每个邻居计算权重(距离的倒数) weights = {} for neighbor in neighbors: dist = euclidean_distance(test_row, neighbor) label = neighbor[-1] weights[label] = weights.get(label, 0) + (1.0 / (dist + 1e-5)) # 加小常数避免除零 # 找出权重最大的类别 prediction = max(weights.items(), key=lambda x: x[1])[0] return prediction

这种改进通常能提高模型精度,特别是当不同类别的样本分布不均匀时。

3. 在鸢尾花数据集上应用KNN

3.1 数据准备

我们将使用经典的鸢尾花数据集来测试我们的KNN实现。首先需要加载和预处理数据:

from csv import reader from random import seed, randrange def load_csv(filename): """加载CSV文件""" dataset = [] with open(filename, 'r') as file: csv_reader = reader(file) for row in csv_reader: if not row: continue dataset.append(row) return dataset def str_column_to_float(dataset, column): """将字符串列转换为浮点数""" for row in dataset: row[column] = float(row[column].strip()) def str_column_to_int(dataset, column): """将类别字符串转换为整数编码""" class_values = [row[column] for row in dataset] unique = set(class_values) lookup = {} for i, value in enumerate(unique): lookup[value] = i print(f"[{value}] => {i}") # 打印类别映射 for row in dataset: row[column] = lookup[row[column]] return lookup

3.2 交叉验证评估

为了客观评估模型性能,我们使用5折交叉验证:

def cross_validation_split(dataset, n_folds): """将数据集分割为n折""" dataset_split = [] dataset_copy = list(dataset) fold_size = int(len(dataset) / n_folds) for _ in range(n_folds): fold = [] while len(fold) < fold_size: index = randrange(len(dataset_copy)) fold.append(dataset_copy.pop(index)) dataset_split.append(fold) return dataset_split def accuracy_metric(actual, predicted): """计算准确率""" correct = 0 for i in range(len(actual)): if actual[i] == predicted[i]: correct += 1 return correct / float(len(actual)) * 100.0 def evaluate_algorithm(dataset, algorithm, n_folds, *args): """评估算法性能""" folds = cross_validation_split(dataset, n_folds) scores = [] for fold in folds: train_set = list(folds) train_set.remove(fold) train_set = sum(train_set, []) # 合并其他折作为训练集 test_set = [] for row in fold: row_copy = list(row) test_set.append(row_copy) row_copy[-1] = None # 移除测试集的标签 predicted = algorithm(train_set, test_set, *args) actual = [row[-1] for row in fold] accuracy = accuracy_metric(actual, predicted) scores.append(accuracy) return scores

3.3 完整KNN算法实现

将前面的组件组合成完整的KNN算法:

def k_nearest_neighbors(train, test, num_neighbors): """KNN算法完整实现""" predictions = [] for row in test: output = predict_classification(train, row, num_neighbors) predictions.append(output) return predictions

3.4 运行评估

现在我们可以加载鸢尾花数据集并评估我们的KNN实现:

# 加载数据集 filename = 'iris.csv' dataset = load_csv(filename) # 转换数据类型 for i in range(len(dataset[0])-1): str_column_to_float(dataset, i) str_column_to_int(dataset, len(dataset[0])-1) # 评估参数 n_folds = 5 num_neighbors = 5 seed(1) # 固定随机种子确保结果可复现 # 评估算法 scores = evaluate_algorithm(dataset, k_nearest_neighbors, n_folds, num_neighbors) print(f"Scores: {scores}") print(f"Mean Accuracy: {sum(scores)/float(len(scores)):.3f}%")

典型输出结果:

Scores: [96.667, 96.667, 100.0, 90.0, 100.0] Mean Accuracy: 96.667%

这个结果明显优于33%的基准准确率,说明我们的实现是有效的。

4. 实际应用与优化建议

4.1 进行新样本预测

训练好模型后,我们可以预测新样本的类别:

# 定义新样本 new_sample = [5.7, 2.9, 4.2, 1.3] # 花萼长、花萼宽、花瓣长、花瓣宽 # 预测类别 predicted_label = predict_classification(dataset, new_sample, num_neighbors) print(f"Data={new_sample}, Predicted: {predicted_label}")

4.2 性能优化技巧

  1. 数据标准化:不同特征的量纲不同会影响距离计算。可以使用Min-Max标准化或Z-score标准化:
def normalize_dataset(dataset): """Min-Max标准化""" minmax = [] for i in range(len(dataset[0])): col_values = [row[i] for row in dataset] value_min = min(col_values) value_max = max(col_values) minmax.append([value_min, value_max]) for row in dataset: for i in range(len(row)-1): # 不标准化标签列 row[i] = (row[i] - minmax[i][0]) / (minmax[i][1] - minmax[i][0])
  1. KD树优化:对于大数据集,线性搜索最近邻居效率低。可以使用KD树数据结构:
from collections import namedtuple from math import inf class KDNode: def __init__(self, point, left=None, right=None): self.point = point self.left = left self.right = right def build_kdtree(points, depth=0): if not points: return None k = len(points[0]) - 1 # 特征维度(忽略标签) axis = depth % k points.sort(key=lambda x: x[axis]) median = len(points) // 2 return KDNode( point=points[median], left=build_kdtree(points[:median], depth+1), right=build_kdtree(points[median+1:], depth+1) )
  1. 并行计算:预测阶段可以并行处理多个测试样本,大幅提升速度。

4.3 常见问题排查

  1. 准确率低

    • 检查数据是否需要标准化
    • 尝试不同的K值(通过交叉验证选择)
    • 检查数据是否有噪声或异常值
  2. 预测速度慢

    • 考虑使用KD树等数据结构
    • 减少特征维度(特征选择)
    • 对数据进行采样减少训练集大小
  3. 所有预测结果相同

    • 可能是K值设置过大
    • 检查距离计算是否正确
    • 数据可能没有经过适当缩放

4.4 算法局限性

虽然KNN简单有效,但也有以下限制:

  1. 计算复杂度高:预测时需要计算与所有训练样本的距离
  2. 对高维数据效果差("维度诅咒")
  3. 需要大量内存存储整个训练集
  4. 对不平衡数据敏感

在实际应用中,需要根据具体问题和数据特点决定是否使用KNN。对于小型到中型数据集,且特征维度不高的情况下,KNN通常能提供不错的基准性能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 1:58:29

G-SHARP:基于高斯分布的实时手术3D重建技术

1. 项目概述G-SHARP是一项突破性的实时手术场景重建技术&#xff0c;它基于高斯分布&#xff08;Gaussian Splatting&#xff09;原理&#xff0c;专为微创手术中的3D组织建模需求而设计。这项技术的核心价值在于能够在手术过程中实时生成高保真度的可变形组织模型&#xff0c;…

作者头像 李华
网站建设 2026/4/25 1:58:28

REFramework深度解析:RE引擎游戏Mod开发的架构设计与实践方案

REFramework深度解析&#xff1a;RE引擎游戏Mod开发的架构设计与实践方案 【免费下载链接】REFramework Mod loader, scripting platform, and VR support for all RE Engine games 项目地址: https://gitcode.com/GitHub_Trending/re/REFramework REFramework作为专为R…

作者头像 李华
网站建设 2026/4/25 1:58:28

乐迪信息:长时间驾驶船舶AI识别:防爆摄像机疲劳监测

在船舶运输行业中&#xff0c;长时间连续驾驶是一个普遍存在的现象。尤其是对于货运船舶、拖轮或者工程船来说&#xff0c;船员往往需要保持数小时甚至十几小时的持续操作。这样一来&#xff0c;疲劳驾驶的问题就显得尤为突出。事实上&#xff0c;很多水上交通事故的背后&#…

作者头像 李华
网站建设 2026/4/25 1:57:43

oh-my-openagent:开源AI智能体框架的设计、实现与实战指南

1. 项目概述&#xff1a;一个面向开发者的开源AI智能体框架最近在GitHub上闲逛&#xff0c;又发现了一个挺有意思的开源项目&#xff0c;叫oh-my-openagent。这个项目名就挺有“梗”的&#xff0c;熟悉Linux的朋友一看就知道&#xff0c;它是在向经典的oh-my-zsh致敬。不过&…

作者头像 李华
网站建设 2026/4/25 1:56:37

CDLF多级泵品牌推荐:上海上诚泵阀在工程应用中表现如何?

CDLF多级泵品牌推荐&#xff1a;上海上诚泵阀在工程应用中表现如何&#xff1f;在做供水、水处理、循环系统项目时&#xff0c;很多人都会问&#xff1a;&#x1f449; CDLF多级泵品牌怎么选&#xff1f;有没有靠谱推荐&#xff1f;如果只是看资料&#xff0c;很容易陷入一个误…

作者头像 李华
网站建设 2026/4/25 1:56:19

Vercel Skills项目解析:构建标准化AI技能接口的实践指南

1. 项目概述&#xff1a;从“技能”到“可复用的开发者资产”最近在开发者社区里&#xff0c;Vercel Labs 旗下的skills项目引起了我的注意。乍一看这个名字&#xff0c;你可能会联想到某种技能评估或者学习平台&#xff0c;但它的实际内涵要深刻得多。简单来说&#xff0c;ski…

作者头像 李华