资讯详情

20种机器学习算法Python实现:从跑通代码到调参避坑

📅 2026/10/3 3:03:44 | 华诺云谱 👁 阅读
20种机器学习算法Python实现:从跑通代码到调参避坑
简介这份资源面向机器学习入门与进阶学习者系统整理了20种常见算法的Python实现代码覆盖线性回归、逻辑回归、BP神经网络、SVM支持向量机、K-Means聚类、PCA主成分分析以及异常检测等经典模型并对各算法的用法、函数测试与运行结果均配有说明适合希望从理论走向动手实践、构建完整算法认知体系的读者。压缩包共102个文件约34.1MB其中13个py脚本承载核心算法实现57个png与2个jpg记录运行结果与可视化效果11个mat、2个npy及4个csv提供实验数据集与中间结果另有txt说明、md文档与license等辅助文件目录组织清晰便于按算法模块检索学习。目前已有193人学习下载。读者可借助可直接运行的代码快速复现各算法流程结合结果图与数据文件理解模型表现并在此基础上修改参数、替换数据集完成从调用到调优的进阶练习是一份兼顾完整性与实操性的机器学习代码合集。1. 20 种机器学习算法 Python 实现从跑通第一行代码到看懂每个参数很多人学机器学习卡在同一个地方理论书翻了三章公式推了一堆打开编辑器却不知道第一行写什么。网上搜「机器学习 Python 代码」出来的要么是调库三行完事要么是几百行不带注释的脚本跑起来报错都不知道去哪查。这个标题指向的东西很具体——一份覆盖 20 种常见机器学习算法的 Python 实现集合每个算法都有能直接运行的完整代码。它解决的不是「机器学习是什么」的问题而是「我能不能亲手把每个算法跑一遍、改参数看效果」的问题。适合两类人刚学完 Python 基础想往机器学习方向走的入门者以及需要快速复现某个算法做对比实验的从业者。下面按「先跑通、再理解、后调参」的顺序拆开讲。2. 环境搭建与代码组织让 20 个算法脚本互不打架2.1 用 conda 建独立环境别在系统 Python 里装包拿到一个包含 20 个算法实现的压缩包第一件事不是急着解压运行而是先把运行环境隔离出来。我见过太多人直接在系统 Python 里 pip install 一堆包结果 numpy 版本冲突导致一半脚本报错。常见做法是用 conda 建一个专用环境conda create -n ml20 python3.10 -y conda activate ml20 pip install numpy pandas matplotlib scikit-learn scipy这里选 Python 3.10 是因为它在 numpy、scipy 等科学计算库上的兼容性最稳3.11 以上某些老版本 scipy 会编译失败。numpy 负责矩阵运算pandas 做数据加载和预处理matplotlib 画图看结果scikit-learn 主要用来生成测试数据集和做结果对比——注意即使代码包里自己实现了算法用 sklearn 的 make_classification、make_regression 生成标准数据仍然是最省事的验证方式。scipy 在部分算法比如高斯混合模型的概率密度计算里会用到。装完之后用一条命令验证环境是否正常python -c import numpy, pandas, matplotlib, sklearn, scipy; print(all ok)如果输出 all ok说明基础依赖没问题。接下来不要把所有脚本放在一个目录里直接跑因为不同算法可能引用同名的工具函数或数据文件。建议按算法类别分目录ml20/ ├── data/ # 共用数据集 ├── utils/ # 共用工具函数 │ ├── metrics.py # 准确率、MSE 等评估指标 │ └── plot.py # 决策边界、损失曲线绘制 ├── supervised/ │ ├── linear_regression.py │ ├── logistic_regression.py │ ├── decision_tree.py │ └── ... ├── unsupervised/ │ ├── kmeans.py │ ├── pca.py │ └── ... └── requirements.txt这样组织的好处是utils 里的评估函数只写一次20 个算法脚本都能调用data 目录统一放 CSV 或 npz 文件避免每个脚本各自下载数据导致路径混乱。requirements.txt 里锁定版本号换机器时 pip install -r requirements.txt 就能复现环境。2.2 每个算法脚本的统一骨架不管实现哪个算法脚本结构保持一致读起来才不费劲。我一般按这个骨架写# supervised/linear_regression.py import numpy as np from utils.metrics import mean_squared_error from utils.plot import plot_regression_line class LinearRegression: def __init__(self, learning_rate0.01, n_iters1000): self.lr learning_rate self.n_iters n_iters self.weights None self.bias None def fit(self, X, y): n_samples, n_features X.shape self.weights np.zeros(n_features) self.bias 0 for _ in range(self.n_iters): y_pred np.dot(X, self.weights) self.bias dw (1 / n_samples) * np.dot(X.T, (y_pred - y)) db (1 / n_samples) * np.sum(y_pred - y) self.weights - self.lr * dw self.bias - self.lr * db def predict(self, X): return np.dot(X, self.weights) self.bias if __name__ __main__: from sklearn.datasets import make_regression X, y make_regression(n_samples200, n_features1, noise15, random_state42) model LinearRegression(learning_rate0.05, n_iters2000) model.fit(X, y) preds model.predict(X) print(fMSE: {mean_squared_error(y, preds):.4f}) plot_regression_line(X, y, preds)这段代码的关键在 fit 方法里的两个梯度计算dw 是权重梯度db 是偏置梯度公式来自对均方误差求导。learning_rate 控制每步走多远n_iters 是迭代次数。参数怎么设学习率从 0.01 开始试如果损失曲线震荡就降到 0.001如果下降太慢就加到 0.1迭代次数看损失曲线什么时候走平一般 1000 到 5000 之间。运行后输出 MSE 并画出拟合直线能直观看到模型有没有欠拟合或过拟合。其他算法的脚本也按这个模式类封装 fit/predictmain里生成数据、训练、评估、画图。这样 20 个脚本读下来注意力集中在算法本身的差异上而不是被不同的代码风格分散精力。3. 监督学习算法实现从线性回归到集成方法3.1 线性模型家族回归、逻辑回归与正则化线性回归是最好理解的起点但实际用的时候很少直接用它做预测更多是作为基线。逻辑回归虽然名字带「回归」实际是分类算法核心是把线性输出通过 sigmoid 函数映射到 0 到 1 之间# supervised/logistic_regression.py import numpy as np class LogisticRegression: def __init__(self, lr0.01, n_iters1000): self.lr lr self.n_iters n_iters self.weights None self.bias None def _sigmoid(self, z): return 1 / (1 np.exp(-np.clip(z, -250, 250))) def fit(self, X, y): n_samples, n_features X.shape self.weights np.zeros(n_features) self.bias 0 for _ in range(self.n_iters): linear np.dot(X, self.weights) self.bias y_pred self._sigmoid(linear) dw (1 / n_samples) * np.dot(X.T, (y_pred - y)) db (1 / n_samples) * np.sum(y_pred - y) self.weights - self.lr * dw self.bias - self.lr * db def predict_proba(self, X): return self._sigmoid(np.dot(X, self.weights) self.bias) def predict(self, X, threshold0.5): return (self.predict_proba(X) threshold).astype(int)这里 np.clip 是防止 exp 溢出当 z 小于 -250 时 exp(-z) 会变成无穷大。threshold 默认 0.5但在类别不平衡场景下可以调比如正样本很少时降到 0.3 能提高召回率。正则化版本L1/L2在损失函数里加惩罚项L2 加 lambda * sum(w^2)L1 加 lambda * sum(|w|)前者让权重平滑后者能做特征选择。3.2 树模型与集成决策树、随机森林、GBDT决策树的核心是递归分裂节点每次选一个特征和阈值让分裂后的子集尽量纯。实现时用基尼系数或信息熵衡量纯度# supervised/decision_tree.py import numpy as np from collections import Counter class DecisionTree: def __init__(self, max_depth10, min_samples_split2): self.max_depth max_depth self.min_samples_split min_samples_split self.tree None def _gini(self, y): counts np.bincount(y) probs counts / len(y) return 1 - np.sum(probs ** 2) def _best_split(self, X, y): best_gain, best_feat, best_thr -1, None, None for feat in range(X.shape[1]): thresholds np.unique(X[:, feat]) for thr in thresholds: left y[X[:, feat] thr] right y[X[:, feat] thr] if len(left) 0 or len(right) 0: continue gain self._gini(y) - (len(left)/len(y))*self._gini(left) \ - (len(right)/len(y))*self._gini(right) if gain best_gain: best_gain, best_feat, best_thr gain, feat, thr return best_feat, best_thr def _build(self, X, y, depth): if depth self.max_depth or len(y) self.min_samples_split \ or len(np.unique(y)) 1: return Counter(y).most_common(1)[0][0] feat, thr self._best_split(X, y) if feat is None: return Counter(y).most_common(1)[0][0] left_mask X[:, feat] thr return { feat: feat, thr: thr, left: self._build(X[left_mask], y[left_mask], depth1), right: self._build(X[~left_mask], y[~left_mask], depth1) } def fit(self, X, y): self.tree self._build(X, y, 0) def _predict_one(self, x, node): if not isinstance(node, dict): return node if x[node[feat]] node[thr]: return self._predict_one(x, node[left]) return self._predict_one(x, node[right]) def predict(self, X): return np.array([self._predict_one(x, self.tree) for x in X])max_depth 控制树的最大深度设太小会欠拟合设太大会过拟合一般从 5 开始试。min_samples_split 限制节点最少样本数防止对噪声过拟合。随机森林在此基础上加两个随机性每棵树用 bootstrap 采样每次分裂只考虑随机子集的特征。GBDT 则是串行训练每棵树拟合前面模型的残差学习率设 0.05 到 0.1 之间比较稳。3.3 支持向量机与 KNN两种不同的分类思路SVM 的核心是找最大间隔超平面用核函数处理非线性。简化版实现用 hinge loss 加梯度下降# supervised/svm.py import numpy as np class SVM: def __init__(self, lr0.001, lambda_param0.01, n_iters1000): self.lr lr self.lambda_param lambda_param self.n_iters n_iters self.w None self.b None def fit(self, X, y): y_ np.where(y 0, -1, 1) n_samples, n_features X.shape self.w np.zeros(n_features) self.b 0 for _ in range(self.n_iters): for idx, x_i in enumerate(X): condition y_[idx] * (np.dot(x_i, self.w) - self.b) 1 if condition: self.w - self.lr * (2 * self.lambda_param * self.w) else: self.w - self.lr * (2 * self.lambda_param * self.w - np.dot(x_i, y_[idx])) self.b - self.lr * y_[idx] def predict(self, X): return np.sign(np.dot(X, self.w) - self.b)lambda_param 是正则化强度越大间隔越宽但容错越少。KNN 更简单不训练预测时找最近的 k 个邻居投票。k 一般取奇数避免平票3 到 10 之间试。距离度量默认欧氏距离高维数据可以换余弦相似度。4. 无监督学习与降维聚类、降维与异常检测4.1 K-Means 与层次聚类把无标签数据分组K-Means 的步骤很固定随机选 k 个中心把每个点分到最近的中心重新计算中心重复到中心不再移动。# unsupervised/kmeans.py import numpy as np class KMeans: def __init__(self, k3, max_iters100, tol1e-4): self.k k self.max_iters max_iters self.tol tol self.centroids None def fit(self, X): idx np.random.choice(len(X), self.k, replaceFalse) self.centroids X[idx] for _ in range(self.max_iters): labels self._assign(X) new_centroids np.array([X[labels i].mean(axis0) for i in range(self.k)]) shift np.linalg.norm(new_centroids - self.centroids) self.centroids new_centroids if shift self.tol: break def _assign(self, X): dists np.linalg.norm(X[:, None] - self.centroids[None], axis2) return np.argmin(dists, axis1) def predict(self, X): return self._assign(X)k 的选择用肘部法画不同 k 对应的簇内平方和找拐点。tol 控制收敛阈值太小会多跑很多轮太大会提前停。层次聚类不需要预设 k用树状图看合并距离在合适的高度切一刀就是聚类数。4.2 PCA 与 t-SNE降维看数据结构PCA 通过特征值分解找方差最大的方向# unsupervised/pca.py import numpy as np class PCA: def __init__(self, n_components2): self.n_components n_components self.components None self.mean None def fit(self, X): self.mean np.mean(X, axis0) X_centered X - self.mean cov np.cov(X_centered.T) eigvals, eigvecs np.linalg.eig(cov) idx np.argsort(eigvals)[::-1] self.components eigvecs[:, idx[:self.n_components]] def transform(self, X): return np.dot(X - self.mean, self.components)n_components 一般选累计方差贡献率到 85% 以上。t-SNE 适合可视化但计算慢perplexity 参数影响簇的紧密度5 到 50 之间调。4.3 异常检测孤立森林与高斯混合孤立森林的思路是异常点更容易被孤立用随机切分看平均路径长度。高斯混合用多个高斯分布拟合数据概率低的是异常。这两个算法在工业质检和日志分析里用得很多。5. 避坑与排查20 个算法跑下来最容易翻车的地方5.1 数据没标准化导致梯度爆炸现象线性回归或 SVM 的损失变成 NaN。原因特征量纲差异大比如一个特征范围 0 到 1另一个 0 到 10000梯度更新时大特征主导学习率稍大就发散。解决fit 之前用 (X - mean) / std 标准化或者用 sklearn 的 StandardScaler。5.2 决策树递归太深导致栈溢出现象运行决策树脚本报 RecursionError。原因数据有连续特征且没限制深度树一直分裂到每个叶子一个样本。解决设 max_depth 不超过 20min_samples_split 至少 2或者改用迭代方式建树。5.3 K-Means 对初始中心敏感现象每次运行聚类结果不一样。原因随机初始化中心可能落到局部最优。解决用 K-Means 初始化或者跑 10 次取簇内平方和最小的结果。5.4 逻辑回归在类别不平衡时全预测多数类现象准确率 95% 但少数类召回为 0。原因损失函数被多数类主导。解决调 threshold或者给少数类加权重或者用 F1 而不是准确率评估。5.5 matplotlib 中文显示方块现象图标题和轴标签中文变问号。原因默认字体不支持中文。解决在 plot 工具里加 plt.rcParams[font.sans-serif] [SimHei] 和 plt.rcParams[axes.unicode_minus] False。6. 用交叉验证和网格搜索把参数调到能用跑通 20 个算法只是第一步真正让模型能用的是调参。我一般用交叉验证加网格搜索而不是手动试。以随机森林为例from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import make_classification X, y make_classification(n_samples1000, n_features20, random_state42) param_grid { n_estimators: [50, 100, 200], max_depth: [5, 10, None], min_samples_split: [2, 5, 10] } grid GridSearchCV(RandomForestClassifier(random_state42), param_grid, cv5, scoringf1, n_jobs-1) grid.fit(X, y) print(grid.best_params_, grid.best_score_)cv5 是 5 折交叉验证scoring 选 f1 是因为类别可能不平衡n_jobs-1 用满 CPU。网格搜索的代价是组合数乘以折数参数多的时候改用 RandomizedSearchCV 随机采样。调完参把 best_params_ 写回脚本默认值下次直接跑就是最优配置。一个我踩过的坑不要用测试集调参。正确做法是训练集分三份——训练、验证、测试网格搜索在验证集上选参数最后用测试集报一次结果。如果数据量小就用交叉验证代替固定验证集。另一个习惯是每次调参前先固定随机种子否则结果波动大分不清是参数变了还是随机性。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑