
8种距离度量Python实战从欧式到马氏5行代码对比KNN分类准确率在机器学习的世界里距离度量就像一把无形的尺子决定了算法如何看待数据点之间的关系。想象一下如果你用错误的尺子测量世界会得出多么荒谬的结论——这正是许多机器学习项目失败的原因。本文将带你用Python实战8种核心距离度量方法揭示它们在KNN分类中的表现差异让你掌握选择合适尺子的艺术。1. 距离度量机器学习的隐形裁判距离度量是机器学习中衡量样本相似性的数学工具直接影响聚类、分类和异常检测等算法的表现。在KNNK-最近邻算法中距离度量的选择尤为关键——它直接决定了哪些样本会被视为邻居。为什么距离度量如此重要考虑一个简单的例子在电商推荐系统中如果用欧式距离计算用户偏好可能会忽略用户对不同品类关注度的差异而用余弦相似度则能更好地捕捉兴趣方向上的相似性。以下是8种我们将重点探讨的距离度量distance_metrics [ euclidean, # 欧式距离 manhattan, # 曼哈顿距离 chebyshev, # 切比雪夫距离 cosine, # 余弦距离 hamming, # 汉明距离 jaccard, # 杰卡德距离 minkowski, # 闵可夫斯基距离 mahalanobis # 马氏距离 ]每种距离都有其独特的数学特性和适用场景。欧式距离是我们最熟悉的直线距离而曼哈顿距离则像在城市街区中行走的路径距离。余弦相似度关注向量间的角度而非大小马氏距离则考虑了数据分布的特性。提示距离度量的选择应基于数据特性和问题需求。没有放之四海而皆准的最佳选择只有最适合特定场景的选择。2. Python实现8种距离的代码封装让我们构建一个Python类来封装这8种距离计算。我们将使用NumPy进行高效数值运算并利用scipy.spatial.distance中的优化实现作为基准。import numpy as np from scipy.spatial.distance import cdist class DistanceMetrics: def __init__(self, dataNone): 初始化时可选择传入数据计算协方差矩阵用于马氏距离 self.data data if data is not None: self.cov_inv np.linalg.inv(np.cov(data.T)) def euclidean(self, x1, x2): 欧式距离平方差之和的平方根 return np.sqrt(np.sum((x1 - x2)**2, axis-1)) def manhattan(self, x1, x2): 曼哈顿距离绝对差之和 return np.sum(np.abs(x1 - x2), axis-1) def chebyshev(self, x1, x2): 切比雪夫距离各维度最大绝对差 return np.max(np.abs(x1 - x2), axis-1) def cosine(self, x1, x2): 余弦距离1减余弦相似度 dot_product np.sum(x1 * x2, axis-1) norm_product np.linalg.norm(x1, axis-1) * np.linalg.norm(x2, axis-1) return 1 - (dot_product / norm_product) def hamming(self, x1, x2): 汉明距离不同元素的比例要求输入已二值化 return np.mean(x1 ! x2, axis-1) def jaccard(self, x1, x2): 杰卡德距离1减杰卡德相似度 intersection np.sum(x1 * x2, axis-1) union np.sum((x1 x2) 0, axis-1) return 1 - (intersection / union) def minkowski(self, x1, x2, p3): 闵可夫斯基距离欧式和曼哈顿的推广 return np.sum(np.abs(x1 - x2)**p, axis-1)**(1/p) def mahalanobis(self, x1, x2): 马氏距离考虑数据协方差结构的距离 diff x1 - x2 return np.sqrt(np.sum(diff self.cov_inv * diff, axis-1)) def calculate_all(self, x1, x2): 一次性计算所有距离度量 return { euclidean: self.euclidean(x1, x2), manhattan: self.manhattan(x1, x2), chebyshev: self.chebyshev(x1, x2), cosine: self.cosine(x1, x2), hamming: self.hamming(x1, x2), jaccard: self.jaccard(x1, x2), minkowski: self.minkowski(x1, x2), mahalanobis: self.mahalanobis(x1, x2) if hasattr(self, cov_inv) else None }这个类不仅实现了各种距离的计算还提供了批量计算的方法。注意到马氏距离需要数据的协方差矩阵因此我们在初始化时可以传入参考数据集。使用示例# 创建距离计算器为马氏距离准备数据 dm DistanceMetrics(dataX_train) # 计算两个样本点间的各种距离 point1 X_test[0] point2 X_test[1] distances dm.calculate_all(point1, point2) # 打印结果 for name, value in distances.items(): print(f{name:10}: {value:.4f})3. KNN分类准确率对比实验现在让我们在经典的Iris数据集上测试这些距离度量对KNN分类准确率的影响。我们将使用scikit-learn的KNeighborsClassifier通过改变metric参数来比较不同距离的效果。from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler from sklearn.metrics import accuracy_score # 加载数据 iris load_iris() X, y iris.data, iris.target # 数据标准化对距离度量很重要 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 划分训练测试集 X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.3, random_state42 ) # 定义要测试的距离度量 metrics [ euclidean, manhattan, chebyshev, cosine, minkowski ] # 存储结果 results {} # 测试每种距离 for metric in metrics: knn KNeighborsClassifier(n_neighbors5, metricmetric) knn.fit(X_train, y_train) y_pred knn.predict(X_test) acc accuracy_score(y_test, y_pred) results[metric] acc print(f{metric:10}: {acc:.4f}) # 自定义马氏距离scikit-learn不直接支持 from sklearn.neighbors import DistanceMetric mahalanobis_dist DistanceMetric.get_metric(mahalanobis, VInp.linalg.inv(np.cov(X_train.T))) knn KNeighborsClassifier(n_neighbors5, metricmahalanobis_dist) knn.fit(X_train, y_train) y_pred knn.predict(X_test) results[mahalanobis] accuracy_score(y_test, y_pred) print(f{mahalanobis:10}: {results[mahalanobis]:.4f})实验结果可能如下表所示具体数值会随随机种子变化距离度量准确率特点描述euclidean0.9778默认选择适合各向同性数据manhattan0.9556对异常值更鲁棒chebyshev0.9333只考虑最大差异维度cosine0.9778适合文本等稀疏数据minkowski0.9778p3时的表现mahalanobis0.9889考虑数据分布表现最佳从结果可以看出马氏距离在这个数据集上表现最好因为它考虑了特征间的相关性。欧式距离和余弦距离表现相当而切比雪夫距离表现相对较差。注意马氏距离计算需要满足样本数大于特征数且协方差矩阵可逆。在高维小样本情况下可能不适用。4. 距离度量的选择策略面对多种距离度量如何做出明智选择以下是基于数据特性和问题类型的决策指南数据尺度敏感性欧式距离、曼哈顿距离等对特征尺度敏感解决方案标准化或归一化数据余弦距离、相关距离对幅度不敏感数据分布特性当特征高度相关时马氏距离是理想选择对于稀疏数据如文本余弦距离或杰卡德距离更合适二进制数据常用汉明距离或杰卡德距离计算效率考虑欧式距离计算量最小马氏距离需要计算协方差矩阵的逆代价较高汉明距离在二进制数据上效率极高鲁棒性需求曼哈顿距离对异常值比欧式距离更鲁棒当数据含有大量零值如推荐系统余弦距离更稳定实际选择时可以遵循以下流程分析数据特性尺度、分布、稀疏性等根据问题类型筛选候选距离分类、聚类、推荐等进行交叉验证比较不同距离的效果考虑计算成本和可解释性的平衡# 距离度量选择流程图代码示例 def select_distance_metric(data, target): 自动化距离度量选择的简化示例 from sklearn.model_selection import cross_val_score candidates [euclidean, manhattan, cosine] if data.shape[0] data.shape[1]: # 满足马氏距离条件 candidates.append(mahalanobis) best_metric, best_score None, 0 for metric in candidates: if metric mahalanobis: dist DistanceMetric.get_metric(mahalanobis, VInp.linalg.inv(np.cov(data.T))) knn KNeighborsClassifier(metricdist) else: knn KNeighborsClassifier(metricmetric) scores cross_val_score(knn, data, target, cv5) mean_score np.mean(scores) if mean_score best_score: best_score mean_score best_metric metric return best_metric, best_score在真实项目中距离度量的选择往往需要结合领域知识。例如在基因序列分析中汉明距离可能比欧式距离更有生物学意义在时间序列分析中动态时间规整DTW距离可能比传统距离更合适。