别再傻傻分不清了!用Python手把手教你区分KNN、K-means和SVM(附实战代码)

张开发
2026/4/5 5:31:27 15 分钟阅读

分享文章

别再傻傻分不清了!用Python手把手教你区分KNN、K-means和SVM(附实战代码)
机器学习三剑客KNN、K-means与SVM的深度解析与Python实战刚接触机器学习时面对各种算法缩写就像走进了一个满是相似面孔的派对——KNN、K-means、SVM这些名字听起来像三胞胎却各自有着截然不同的性格和专长。本文将带您穿透术语迷雾通过生活化类比、数学直觉和实战代码彻底掌握这三个基础但强大的算法。1. 算法本质与核心思想对比1.1 KNN数据世界的近邻社交想象你搬到一个新社区想了解这个区域的特点。你会怎么做自然是通过观察最近的几户邻居来推断整个社区的特征。这正是K近邻算法(K-Nearest Neighbors)的核心哲学——基于局部相似性做出判断。KNN的关键特性监督学习需要带标签的训练数据惰性学习训练阶段仅存储数据不做复杂计算距离驱动欧氏距离是常用度量标准数学表达上对于一个新样本点x其预测结果ŷ可表示为ŷ mode(y_i | x_i ∈ N_k(x))其中N_k(x)表示x的k个最近邻。1.2 K-means数据自组织的艺术如果把KNN比作社交网络那么K-means更像是组织一场高效的团队建设——把相似的人分到同一小组并选出每个小组的代表人物。这是一种典型的无监督聚类方法。算法运作流程随机初始化K个质心将每个点分配给最近的质心重新计算质心位置重复2-3步直到收敛目标函数畸变函数为J ΣΣ ||x - μ_i||²其中μ_i是第i个簇的质心。1.3 SVM寻找最佳分界线的几何学家支持向量机(Support Vector Machine)则像一位严谨的城市规划师致力于在不同社区间划出最宽阔的缓冲带。其核心是最大化间隔的超平面。关键概念支持向量决定分界面的关键样本点核技巧通过非线性映射处理复杂边界软间隔允许一定程度的分类错误优化目标min ½||w||² CΣξ_i s.t. y_i(w·x_i b) ≥ 1-ξ_i, ξ_i ≥ 02. 算法对比矩阵与应用场景特性KNNK-meansSVM学习类型监督学习无监督学习监督学习主要任务分类/回归聚类分类/回归距离度量欧氏距离为主欧氏距离核函数距离参数选择K值K值核函数、C参数计算复杂度O(nd)预测时O(nkdt)训练时O(n²)~O(n³)训练时最佳场景小数据集、局部模式明显客户分群、数据探索高维数据、清晰边界典型应用案例KNN推荐系统喜欢这个商品的人也喜欢...、异常检测K-means市场细分、图像压缩、文档聚类SVM文本分类、生物信息学、手写识别3. Python实战从零实现三大算法3.1 KNN完整实现与优化基础版本虽然直观但效率低下。我们使用KD树进行优化from collections import Counter from sklearn.neighbors import KDTree import numpy as np class AdvancedKNN: def __init__(self, k5, leaf_size30): self.k k self.leaf_size leaf_size def fit(self, X, y): self.tree KDTree(X, leaf_sizeself.leaf_size) self.y y def predict(self, X): distances, indices self.tree.query(X, kself.k) votes [Counter(self.y[i]).most_common(1)[0][0] for i in indices] return np.array(votes) # 示例鸢尾花数据集 from sklearn.datasets import load_iris iris load_iris() X, y iris.data, iris.target knn AdvancedKNN(k3) knn.fit(X, y) print(预测结果:, knn.predict(X[:5]))3.2 K-means优化K-means初始化原始K-means对初始质心敏感改进版本如下import numpy as np class KMeansPlusPlus: def __init__(self, k3, max_iter300): self.k k self.max_iter max_iter def _initialize_centroids(self, X): centroids [X[np.random.choice(len(X))]] for _ in range(1, self.k): dists np.array([min(np.linalg.norm(x-c)**2 for c in centroids) for x in X]) probs dists / dists.sum() centroids.append(X[np.random.choice(len(X), pprobs)]) return np.array(centroids) def fit(self, X): self.centroids self._initialize_centroids(X) for _ in range(self.max_iter): clusters [[] for _ in range(self.k)] for x in X: closest np.argmin([np.linalg.norm(x-c) for c in self.centroids]) clusters[closest].append(x) new_centroids [np.mean(cluster, axis0) for cluster in clusters] if np.allclose(self.centroids, new_centroids): break self.centroids new_centroids return self def predict(self, X): return [np.argmin([np.linalg.norm(x-c) for c in self.centroids]) for x in X] # 测试聚类效果 from sklearn.datasets import make_blobs X, _ make_blobs(n_samples300, centers3, random_state42) kmeans KMeansPlusPlus(k3).fit(X) print(质心位置:\n, kmeans.centroids)3.3 SVM实现SMO算法简化版完整实现SVM需要复杂的优化方法这里展示简化版import numpy as np class SimpleSVM: def __init__(self, C1.0, tol0.01, max_iter1000): self.C C self.tol tol self.max_iter max_iter def fit(self, X, y): n_samples, n_features X.shape y np.where(y 0, -1, 1) self.alpha np.zeros(n_samples) self.b 0 for _ in range(self.max_iter): for i in range(n_samples): Ei self._decision_function(X[i]) - y[i] if (y[i]*Ei -self.tol and self.alpha[i] self.C) or \ (y[i]*Ei self.tol and self.alpha[i] 0): j np.random.choice([x for x in range(n_samples) if x ! i]) Ej self._decision_function(X[j]) - y[j] alpha_i_old, alpha_j_old self.alpha[i], self.alpha[j] L, H self._compute_L_H(y[i], y[j], alpha_i_old, alpha_j_old) eta 2 * X[i].dot(X[j]) - X[i].dot(X[i]) - X[j].dot(X[j]) if eta 0: continue self.alpha[j] alpha_j_old - y[j]*(Ei - Ej)/eta self.alpha[j] np.clip(self.alpha[j], L, H) self.alpha[i] alpha_i_old y[i]*y[j]*(alpha_j_old - self.alpha[j]) b1 self.b - Ei - y[i]*(self.alpha[i]-alpha_i_old)*X[i].dot(X[i]) \ - y[j]*(self.alpha[j]-alpha_j_old)*X[i].dot(X[j]) b2 self.b - Ej - y[i]*(self.alpha[i]-alpha_i_old)*X[i].dot(X[j]) \ - y[j]*(self.alpha[j]-alpha_j_old)*X[j].dot(X[j]) self.b (b1 b2)/2 self.w np.sum((self.alpha * y).reshape(-1,1) * X, axis0) def _decision_function(self, x): return np.dot(x, self.w) - self.b def _compute_L_H(self, yi, yj, ai, aj): if yi ! yj: L max(0, aj - ai) H min(self.C, self.C aj - ai) else: L max(0, ai aj - self.C) H min(self.C, ai aj) return L, H def predict(self, X): return np.sign(self._decision_function(X)) # 测试线性可分数据 X np.array([[1,2], [2,3], [2,1], [3,2], [6,7], [7,8], [8,7], [7,6]]) y np.array([-1,-1,-1,-1,1,1,1,1]) svm SimpleSVM().fit(X, y) print(预测:, svm.predict([[4,4], [5,5]]))4. 算法选择与调优指南4.1 何时选择哪种算法选择KNN当数据规模较小10K样本需要解释预测结果时数据具有明显的局部模式选择K-means当需要探索数据内在结构预处理阶段的特征工程数据压缩或降维前步骤选择SVM当特征维度较高如文本数据类别边界需要精确划分数据量中等100K样本4.2 参数调优实战技巧KNN调优from sklearn.model_selection import GridSearchCV from sklearn.neighbors import KNeighborsClassifier param_grid { n_neighbors: range(3, 15), weights: [uniform, distance], metric: [euclidean, manhattan] } grid GridSearchCV(KNeighborsClassifier(), param_grid, cv5) grid.fit(X_train, y_train) print(最佳参数:, grid.best_params_)K-means调优from sklearn.metrics import silhouette_score scores [] for k in range(2, 10): kmeans KMeans(n_clustersk).fit(X) scores.append(silhouette_score(X, kmeans.labels_)) optimal_k np.argmax(scores) 2 # 因为range从2开始SVM核函数选择from sklearn.svm import SVC kernels [linear, poly, rbf, sigmoid] for kernel in kernels: svm SVC(kernelkernel).fit(X_train, y_train) score svm.score(X_test, y_test) print(f{kernel}核准确率: {score:.2f})4.3 常见陷阱与解决方案KNN的维度灾难现象随着特征增加性能急剧下降方案特征选择、降维PCA、距离加权K-means的局部最优现象不同运行得到不同结果方案多次初始化选择最佳、K-meansSVM的过拟合现象在训练集完美但在测试集差方案调整C参数、尝试不同核函数

更多文章