【学习】《算法图解》第十二章学习笔记:K近邻算法
前言
《算法图解》第十二章介绍了一种简单而强大的机器学习算法——K近邻算法(K-Nearest Neighbors,简称KNN)。这是一种基于实例的学习方法,也是机器学习领域中最基础、最直观的算法之一。本章不仅讲解了KNN的基本原理和实现方式,还探讨了特征提取、归一化等重要概念,为读者打开了机器学习的大门。本笔记将梳理KNN算法的核心思想、实现步骤以及应用场景。
一、K近邻算法概述
(一)基本思想
K近邻算法的核心思想非常简单:物以类聚,人以群分。它基于一个假设:相似的事物通常具有相似的特征,并且在特征空间中彼此靠近。
具体来说,KNN算法的基本思路是:
- 对于一个待分类的新实例,在训练数据集中找到与它最相似(距离最近)的K个实例
- 这K个实例中出现最多的类别,就作为新实例的预测类别
(二)算法特点
KNN算法具有以下特点:
- 非参数化方法:不对数据分布做任何假设,完全依赖于数据本身
- 惰性学习:没有显式的训练过程,只在需要预测时才进行计算
- 直观易懂:算法思想简单,容易理解和实现
- 计算复杂度高:预测时需要计算新实例与所有训练实例的距离
二、KNN算法步骤详解
(一)算法流程
KNN算法的基本流程如下:
- 收集数据:准备训练数据集,每个实例包含特征向量和类别标签
- 选择距离度量:确定如何计算实例之间的相似度(通常使用欧几里得距离)
- 对新实例进行分类:
- 计算新实例与训练集中所有实例的距离
- 选择距离最近的K个实例
- 统计这K个实例中各类别的频次
- 将出现频次最高的类别作为新实例的预测类别
(二)距离度量
KNN算法中,距离度量是衡量两个实例相似度的关键。常用的距离度量方法包括:
欧几里得距离:最常用的距离计算方法
$$d(x, y) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2}$$曼哈顿距离:沿坐标轴方向的距离总和
$$d(x, y) = \sum_{i=1}^{n}|x_i - y_i|$$闵可夫斯基距离:欧几里得距离和曼哈顿距离的一般化形式
$$d(x, y) = \left(\sum_{i=1}^{n}|x_i - y_i|^p\right)^{1/p}$$余弦相似度:计算两个向量的夹角余弦值,常用于文本分析
$$\cos(\theta) = \frac{x \cdot y}{||x|| \times ||y||}$$
在《算法图解》中,主要使用欧几里得距离作为度量标准。
(三)K值的选择
K值的选择对KNN算法的性能有重要影响:
- K值过小(如K=1):算法对噪声敏感,容易过拟合
- K值过大:可能会忽略局部特征,导致欠拟合
- 经验法则:一般选择训练样本数量的平方根作为K值的参考
- 实践建议:通常通过交叉验证等方法从多个候选值中选择最优的K值
另外,为了避免平局情况,K值通常选择奇数。
三、特征工程与数据预处理
(一)特征提取
在应用KNN算法之前,需要将原始数据转换为特征向量。《算法图解》中提到了几种常见的特征提取方法:
- 数值型特征:直接使用原始数值,如身高、体重等
- 分类特征:通过独热编码(One-Hot Encoding)等方法转换为数值
- 文本特征:可以使用词袋模型(Bag of Words)或TF-IDF等方法提取特征
- 图像特征:可以提取颜色直方图、纹理特征等
特征提取的质量直接影响KNN算法的性能,因此需要根据具体问题选择合适的特征表示方法。
(二)特征归一化
由于KNN算法基于距离计算,不同特征的量纲(单位和范围)差异会对结果产生不公平的影响。例如,如果一个特征的取值范围是0-1,另一个特征的取值范围是0-1000,那么第二个特征将在距离计算中占据主导地位。
为了解决这个问题,需要对特征进行归一化处理,常用的方法包括:
最小-最大归一化(Min-Max Scaling):将特征缩放到[0, 1]区间
$$x’ = \frac{x - \min(x)}{\max(x) - \min(x)}$$Z-score标准化:将特征转换为均值为0、标准差为1的分布
$$x’ = \frac{x - \mu}{\sigma}$$
在《算法图解》中,作者强调了归一化的重要性,并建议在实际应用中始终对特征进行适当的归一化处理。
四、Python实现KNN算法
(一)基本实现
以下是KNN算法的简单Python实现:
import numpy as np
from collections import Counter
def knn_classify(training_data, training_labels, new_instance, k=3, distance_fn=None):
"""
使用KNN算法对新实例进行分类
参数:
training_data -- 训练数据集,每行是一个实例的特征向量
training_labels -- 训练数据的类别标签
new_instance -- 待分类的新实例
k -- 近邻数量
distance_fn -- 距离计算函数,默认为欧几里得距离
返回:
predicted_label -- 预测的类别标签
"""
# 如果没有提供距离函数,使用欧几里得距离
if distance_fn is None:
distance_fn = lambda x, y: np.sqrt(np.sum((x - y) ** 2))
# 计算新实例与所有训练实例的距离
distances = []
for i, instance in enumerate(training_data):
dist = distance_fn(instance, new_instance)
distances.append((dist, training_labels[i]))
# 按距离排序并选择前k个
distances.sort(key=lambda x: x[0])
k_nearest = distances[:k]
# 统计这k个近邻中各类别的频次
k_nearest_labels = [label for _, label in k_nearest]
most_common = Counter(k_nearest_labels).most_common(1)
return most_common[0][0]
(二)示例应用
以《算法图解》中的电影分类例子为例,我们可以使用KNN算法对电影进行分类:
# 电影数据:[动作场景数, 浪漫场景数]
movies = np.array([
[3, 104], # "爱情片"
[2, 100], # "爱情片"
[1, 81], # "爱情片"
[101, 10], # "动作片"
[99, 5], # "动作片"
[98, 2] # "动作片"
])
# 电影类别标签
labels = ["爱情片", "爱情片", "爱情片", "动作片", "动作片", "动作片"]
# 对特征进行归一化
def normalize(data):
min_vals = np.min(data, axis=0)
max_vals = np.max(data, axis=0)
ranges = max_vals - min_vals
normalized_data = np.zeros(np.shape(data))
m = data.shape[0]
normalized_data = (data - np.tile(min_vals, (m, 1))) / np.tile(ranges, (m, 1))
return normalized_data
# 归一化后的电影数据
normalized_movies = normalize(movies)
# 待分类的新电影:[动作场景数, 浪漫场景数]
new_movie = np.array([18, 90])
normalized_new_movie = (new_movie - np.min(movies, axis=0)) / (np.max(movies, axis=0) - np.min(movies, axis=0))
# 使用KNN算法进行分类
predicted_category = knn_classify(normalized_movies, labels, normalized_new_movie, k=3)
print(f"这部新电影可能是: {predicted_category}")
五、KNN算法的优缺点
(一)优点
- 简单直观:算法思想容易理解,实现简单
- 无需训练:不需要构建模型,可以直接用于分类
- 适用性广:可用于分类和回归问题
- 理论成熟:有完善的数学理论支持
- 对数据分布无假设:不需要对数据分布做任何假设
(二)缺点
- 计算复杂度高:预测时需要计算与所有训练实例的距离,时间复杂度为O(n),其中n是训练集大小
- 存储开销大:需要存储全部训练数据
- 对特征缩放敏感:不同特征的量纲差异会影响结果
- 维度灾难:在高维空间中,距离度量的区分能力下降
- 对噪声敏感:异常值可能对结果产生较大影响
六、KNN的实际应用
(一)应用场景
KNN算法在许多领域都有广泛应用:
- 推荐系统:基于用户相似度推荐商品、电影等
- 图像识别:通过图像特征进行分类
- 文本分类:对文档进行主题分类
- 医疗诊断:基于病人症状和历史病例进行疾病诊断
- 金融风控:信用评分和风险评估
(二)KNN的改进
为了解决KNN算法的一些缺点,研究人员提出了多种改进方法:
- KD树:使用KD树等数据结构加速近邻搜索
- 加权KNN:根据距离对近邻的投票进行加权
- 局部加权回归:在回归问题中使用加权平均
- 降维技术:使用PCA等方法降低特征维度
- 特征选择:选择最相关的特征子集
七、总结
K近邻算法是一种简单而强大的机器学习方法,它通过比较新实例与已知实例的相似度来进行分类或回归。尽管KNN算法有计算复杂度高、存储开销大等缺点,但其简单直观的特性使其成为机器学习入门的理想算法,也是实际应用中的重要工具之一。
在实践中,特征工程(特别是特征提取和归一化)对KNN算法的性能至关重要。此外,K值的选择也需要根据具体问题进行调整,通常通过交叉验证等方法确定最优值。
《算法图解》通过生动的例子和清晰的解释,帮助读者理解了KNN算法的基本原理和应用方法,为进一步学习更复杂的机器学习算法奠定了基础。
八、参考资料
- 《算法图解》(Grokking Algorithms)by Aditya Y. Bhargava
- 周志华《机器学习》
- Peter Harrington《机器学习实战》
- scikit-learn KNN文档
- K近邻算法 - 维基百科