当前位置: 首页 > news >正文

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通常能提供不错的基准性能。

http://www.jsqmd.com/news/695560/

相关文章:

  • 人生无处不下注:你早就在赌桌上了
  • IDA远程调试Linux ELF实战:从环境搭建到网络排障全解析
  • 不平衡分类问题的采样方法与应用实践
  • 2026年OpenClaw部署新手教程
  • Java智能地址解析架构方案:企业级数据治理的技术实现原理
  • Agent Laboratory:模块化AI研究助理框架,自动化文献、实验与报告全流程
  • 2026年自配送平台技术解析与优质服务商参考 - 优质品牌商家
  • 【前端圭臬】一:写给入坑前端的你
  • 数据驱动决策:商业与技术的融合实践
  • 为什么你的LangChain+LlamaIndex调试总失败?——VSCode多智能体调试黄金配置(含3个已验证的launch.json生产级范例)
  • WMS 2026版深度解析:从成本优化到全链路数字化仓储升级路径
  • 机器学习数据预处理:鲁棒缩放技术解析与实践
  • Python 内置数据结构性能对比基础
  • XGBoost在Apple Silicon上的编译安装与优化指南
  • 用AI写的一个包含web和小程序的个人简历
  • 基于RAG的文档智能问答系统:从原理到工程实践
  • 2026年网红凉皮口碑排行榜TOP10 技术维度解析 - 优质品牌商家
  • ARMv8-A架构系统寄存器与TLBI操作详解
  • 揭秘Claude Code系统提示词:模块化设计、子代理协作与定制化实践
  • 神经系统与深度学习介绍 学习笔记day1
  • Hotkey Detective:Windows热键冲突检测的3大创新方案
  • DeepSeek V4 API调用Agent能力详解与应用场景
  • 怎么确认减速机装上就能用,不用再改接口?哪个品牌安装尺寸和标准最通用、兼容性最好?
  • git使用快速入门
  • AI时代软件开发范式变革:从代码编写到智能体指挥官的转型
  • 大容量企业存储刚需 西数 16TB 机械硬盘 稳定高效全覆盖
  • PowerShell与JSON的精妙转换
  • 2026年中高端婚介选型指南:从核验机制到服务链路的技术拆解 - 优质品牌商家
  • 大模型的探索与实践-课程笔记(八):RAG 技术原理与本地部署
  • Flutter for OpenHarmony 页面导航与动效库适配小记复盘:让 App 又丝滑又灵动✨