
简介一套面向机器学习初学者的入门代码包覆盖 BP 神经网络MLP、KNN 回归、SVM 超平面、决策树企鹅数据集、朴素贝叶斯与逻辑回归等常用模型适合正在学习分类与回归基础、希望结合代码理解算法原理的数据科学初学者。资源共 14 个文件主体为 13 个 Python 脚本并附带 1 个 penguins_raw.csv 数据集便于直接运行决策树等分类示例整个压缩包仅 24KB轻量易下载可快速对照源码练习。目前已有 343 人学习。代码按模型单独组织每个脚本聚焦一个算法并包含 Iris 与企鹅等经典数据集的应用可帮助读者直观看到 KNN、逻辑回归、SVM 软间隔及朴素贝叶斯在真实数据上的处理方式。通过动手运行这些示例能逐步建立对模型训练、参数选择与分类/回归流程的初步认知为后续深入机器学习打下基础。1. 一套能直接跑的机器学习入门代码为什么值得你花一个晚上过一遍刚接触机器学习的人最容易卡住的不是算法原理而是“书上讲得头头是道打开 IDE 却不知道第一行 import 写什么”。这份标题里罗列的代码恰好覆盖了入门阶段最常用的六类模型——BP 神经网络MLP、KNN 回归、SVM 超平面、决策树、朴素贝叶斯、逻辑回归并且自带数据集企鹅数据集属于打开就能跑、跑完能看效果的那类资源。你不需要先啃完一本 500 页的教材也不需要自己造数据照着代码把每个模型的 fit、predict、score 走一遍就对“机器学习到底在干什么”有了体感。它适合两类人一是刚上完网课、想动手验证一遍理论的初学者二是已经会调 sklearn、但想回过头把模型背后的决策边界和参数含义梳理清楚的从业者。2. 先把地基打好环境、数据与训练/测试划分2.1 最小依赖环境只需要 pandas、numpy、matplotlib 和 scikit-learn这份代码不像深度学习框架那样需要 GPU 和 CUDA它跑在最常见的科学计算栈上。建议用 Python 3.9 以上的版本装一个虚拟环境再开始避免把系统 Python 搞乱。创建环境并安装依赖的命令如下Windows 用户把source activate换成conda activate即可。python -m venv ml_env # Windows: ml_env\Scripts\activate ; macOS/Linux: source ml_env/bin/activate pip install scikit-learn pandas numpy matplotlib seabornscikit-learn是主角六个模型全部由它提供seaborn用来加载企鹅数据集sns.load_dataset(penguins)比手动下 CSV 省事很多matplotlib只用来画决策边界和损失曲线没有它不影响训练。建议跟着做的时候把版本锁定一下pip install scikit-learn1.3.* pandas2.0.*因为新版 sklearn 对SVC的某些参数默认值有调整老教程的代码未必兼容。2.2 加载企鹅数据集并做数据清洗企鹅数据集Palmer Penguins是 iris 鸢尾花数据集的现代替代品包含 344 条样本、7 个特征喙长、喙深、鳍长、体重、性别、岛屿、物种。它比 iris 多了真实数据里常见的脏东西——缺失值、分类文本、量纲差异正好用来练手数据预处理。import pandas as pd import seaborn as sns from sklearn.model_selection import train_test_split # 加载数据集 df sns.load_dataset(penguins) print(df.head()) # 删除缺失值344条里缺失约10条直接drop不影响大局 df df.dropna().reset_index(dropTrue) # 把分类文本转成数值物种、岛屿、性别 df[species] df[species].astype(category).cat.codes df[island] df[island].astype(category).cat.codes df[sex] df[sex].astype(category).cat.codes # 特征列与标签列 feature_cols [bill_length_mm, bill_depth_mm, flipper_length_mm, body_mass_g, island, sex] X df[feature_cols] y df[species] # 训练/测试划分7:3固定随机种子 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.3, random_state42, stratifyy) print(X_train.shape, X_test.shape)这里的代码做了三件关键事丢弃缺失样本、把文本标签转成整数、按物种分层划分训练集和测试集。stratifyy这一行容易被新手忽略但它保证训练集和测试集里三个物种的比例一致避免某一类物种全被分到测试集导致模型“没见过”这类样本。2.3 为什么不先做特征缩放——留到每个模型自己决定很多入门代码喜欢一上来就StandardScaler一把梭但这不是所有模型都需要的。决策树和朴素贝叶斯对特征缩放不敏感KNN、SVM、MLP 和逻辑回归则对量纲敏感。这份代码的做法是把缩放器放到每个模型的 Pipeline 里而不是全局处理这样你能直观看到“哪个模型换了数据尺度后结果变好”。我的建议是KNN、SVM、MLP 必须缩放逻辑回归建议缩放决策树和朴素贝叶斯无所谓。后面每个模型小节里我会注明是否在代码前加了缩放。3. 三个最容易理解的模型逻辑回归、KNN 回归与朴素贝叶斯3.1 逻辑回归拿它当分类的基线别指望它拟合非线性边界逻辑回归虽然名字里带“回归”实际是个线性分类器。它做的事情是把特征的加权和扔进 sigmoid 函数输出一个 0 到 1 之间的概率。在企鹅数据上它相当于画三条直线多分类是一对多把三个物种分开。对于入门来说它是“跑通 sklearn 流程”成本最低的模型——没有超参数要调默认值就能出结果。from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression # 标准缩放 逻辑回归max_iter 调高避免收敛警告 pipe_lr make_pipeline( StandardScaler(), LogisticRegression(max_iter2000, random_state42) ) pipe_lr.fit(X_train, y_train) print(Logistic Regression Test Acc: {:.4f}.format(pipe_lr.score(X_test, y_test)))StandardScaler()对每个特征做零均值单位方差处理因为逻辑回归用梯度下降求解特征量纲差太大会让收敛变慢甚至不收敛max_iter2000是新手必踩的坑默认 100 在部分数据上会报 “ConvergenceWarning”调大不代表模型变好只是给了优化器更多迭代机会输出准确率约为 0.97随随机种子波动这个分数可以作为其他模型的参照线——如果一个模型连逻辑回归都打不过那多半是特征工程或数据预处理出了问题。实际项目里逻辑回归的地位不低金融风控、医疗诊断这类看重可解释性的场景逻辑回归的系数直接告诉你“体重每增加一单位属于某个物种的概率变化多少”。但其线性本质意味着它处理不了异或类问题遇到非线性边界要立刻想到换 SVM 或决策树。3.2 KNN 回归不需要训练的模型调 k 值就是全部学问K-近邻KNN的逻辑极其朴素预测一个样本时找它周围最近的 k 个样本回归任务取这 k 个样本标签的平均值分类任务取投票。它没有显式的训练过程所谓的“训练”只是把数据存起来。这份代码里做的是 KNN 回归数据显示的是企鹅的体重预测问题——用喙长、喙深、鳍长等特征预测body_mass_g。from sklearn.neighbors import KNeighborsRegressor from sklearn.metrics import mean_squared_error # KNN回归输入特征去掉体重列标签设为体重 X_knn df[[bill_length_mm, bill_depth_mm, flipper_length_mm, island, sex]] y_knn df[body_mass_g] Xk_train, Xk_test, yk_train, yk_test train_test_split(X_knn, y_knn, test_size0.3, random_state42) # 先试 k5缩放后效果更好 pipe_knn make_pipeline( StandardScaler(), KNeighborsRegressor(n_neighbors5, weightsdistance) ) pipe_knn.fit(Xk_train, yk_train) yk_pred pipe_knn.predict(Xk_test) print(KNN RMSE: {:.2f} g.format(mean_squared_error(yk_test, yk_pred, squaredFalse)))n_neighbors5是经验默认值但最佳 k 需要暴力搜索从 1 到 30 循环画出 k 与 RMSE 的关系曲线选曲线的“肘部”weightsdistance意味着离预测点越近的邻居权重越大比默认的uniform等权重在多数回归任务上更平滑、更准RMSE 在 250g 到 350g 之间是合理水平企鹅体重的标准差约 800g说明模型误差控制了单位数的量级。做 KNN 时最容易翻车的点是用未缩放的特征直接算欧氏距离。bill_length_mm的取值范围是 30~60body_mass_g是 2700~6300如果不缩放距离几乎完全被体重这一个特征主导其他特征形同虚设。缩放之后每个特征对距离的贡献才均等。3.3 朴素贝叶斯假设特征独立的分类器最适合文本但也能做表格朴素贝叶斯基于贝叶斯定理加上“特征之间相互独立”的强假设。在企鹅数据上它假设喙长、喙深、鳍长等特征在给定物种的条件下互不相关——这个假设在真实数据里几乎不成立但它的分类效果经常出人意料地好尤其在文本分类垃圾邮件过滤领域。它训练时只需要统计每个特征在每个类别下的均值和方差因此训练极快、所需数据量小。from sklearn.naive_bayes import GaussianNB from sklearn.metrics import accuracy_score # 高斯朴素贝叶斯连续特征默认假设服从正态分布 gnb GaussianNB() gnb.fit(X_train, y_train) y_pred gnb.predict(X_test) print(Naive Bayes Test Acc: {:.4f}.format(accuracy_score(y_test, y_pred)))GaussianNB()是 sklearn 里最省心的模型不需要任何参数调优fit 完就能用朴素贝叶斯极少出现“过拟合”问题因为它的模型复杂度低每个特征只学一个高斯分布在企鹅数据上准确率略低于逻辑回归约 0.95原因正是特征独立性假设不成立——比如喙长和体重明显正相关这被模型忽略了。一个反直觉的结论是即便条件独立假设被严重违反分类结果依然可用因为分类只需比较后验概率的大小而不是预测精确概率。但当特征之间相关性极强时比如同一个信息被拆成多个特征朴素贝叶斯会重复计算这个信息导致置信度虚高。在真实项目里我一般用它做“快速基线”看看数据是否线性可分再决定是否上更复杂的模型。3.4 调参经验KNN 的 k 不是越大越好很多人误以为 k 越大模型越稳定实际不一定。k 太大时远距离的邻居被拉进来模型变得过于平滑丢失局部结构k 太小比如 1模型对单个噪声点极度敏感。经验规律是k 大约等于训练样本数的平方根这里约 sqrt(240)≈15但要用验证集去试。另一个参数是距离度量默认的minkowskip2 即欧氏距离适合连续特征如果特征是高维稀疏向量比如 one-hot 编码改用cosine相似度往往更好。4. 三个“画边界”的模型SVM 超平面、决策树与 BP 神经网络4.1 SVM 超平面核函数是它的灵魂C 与 gamma 是两个命门支持向量机SVM做的事是在特征空间中找一个超平面让不同类别的样本间隔margin最大化。所谓“超平面”在二维里是一条线三维里是一个面高维里就是那个“平面”。这份代码用线性核演示超平面的可视化再切换到 RBF 核演示非线性分类能力。SVM 的核心优势在于即便原始特征不可分通过核函数映射到高维空间后可能变得线性可分。from sklearn.svm import SVC from sklearn.inspection import DecisionBoundaryDisplay import matplotlib.pyplot as plt # 只取两个特征鳍长和喙深便于画二维决策边界 X_svm df[[flipper_length_mm, bill_depth_mm]] y_svm df[species] Xs_train, Xs_test, ys_train, ys_test train_test_split(X_svm, y_svm, test_size0.3, random_state42) # RBF核SVMC1.0, gammascale 是sklearn的默认值先不动 pipe_svm make_pipeline( StandardScaler(), SVC(kernelrbf, C1.0, gammascale, random_state42) ) pipe_svm.fit(Xs_train, ys_train) print(SVM Test Acc (2 features): {:.4f}.format(pipe_svm.score(Xs_test, ys_test))) # 画决策边界subplot 里放训练集散点 边界 fig, ax plt.subplots(figsize(6, 5)) DecisionBoundaryDisplay.from_estimator( pipe_svm, Xs_train, response_methodpredict, alpha0.4, axax ) ax.scatter(Xs_train[flipper_length_mm], Xs_train[bill_depth_mm], cys_train, edgecolork) plt.show()C是误分类惩罚系数C 越大模型越努力把训练集分对边界越曲折越容易过拟合C 越小边界越平滑可能欠拟合。默认 1.0 是个中庸值gamma是 RBF 核的带宽参数控制了“单个训练样本的影响半径”。gammascale让 sklearn 根据特征方差自动计算gamma0.1这类手动值需要网格搜索只取两个特征做可视化是常见做法能让决策边界画在二维平面上看个明白但代价是精度下降企鹅数据用两个特征精度也够高就是运气好。用 SVM 的典型翻车点是数据量超过 10 万条时RBF 核的拟合速度会肉眼可见地变慢因为核矩阵是 N×N 的。大批量数据请换 LinearSVC线性核专用或直接改用随机森林别和 SVM 死磕。4.2 决策树企鹅数据集上的规则提取比准确率更重要的是可解释性决策树的训练过程就是递归地选择“哪个特征、哪个阈值能把数据分得最纯”。在企鹅数据上它第一条分裂规则通常落在喙长或鳍长上这直接告诉你“喙长的某个阈值是区分物种的关键”。这份代码除了训练模型还特别适合把训练出的树可视化出来因为真正的价值不在于预测而在于生成一条条可读的 if-else 规则。from sklearn.tree import DecisionTreeClassifier, plot_tree # 限制深度防止过拟合限制最小叶节点样本数 clf_tree DecisionTreeClassifier( max_depth4, min_samples_leaf5, random_state42 ) clf_tree.fit(X_train, y_train) print(Decision Tree Test Acc: {:.4f}.format(clf_tree.score(X_test, y_test))) # 可视化树结构 plt.figure(figsize(16, 8)) plot_tree( clf_tree, feature_namesfeature_cols, class_names[Adelie, Chinstrap, Gentoo], filledTrue, fontsize9 ) plt.show()max_depth4限制树的层数防止它把每个训练样本都包进一个叶节点里那叫极限过拟合min_samples_leaf5确保每个叶节点至少有 5 个样本噪声数据不会孤立成一个叶子可视化输出是一棵树每个节点上写着“bill_length_mm 42.35”这种判断条件这就是模型的可解释性——医生、风控员可以直接看懂规则而 SVM 的权重系数很难对人解释。决策树最大的坑在于它对特征尺度的变化完全免疫缩放与否不影响树结构但对数据中的细微扰动极为敏感。训练数据里加一个离群点根的切分点可能就从 42.35 跳到 41.80整棵树面目全非。随机森林解决了这个问题用多棵树投票但单个决策树的“不稳定”不是 bug而是它的数学本质。我一般建议用决策树做分析要规则用随机森林做预测要精度。4.3 BP 神经网络MLP三层网络调参比你想的少但看损失曲线是必须的BP反向传播神经网络在三个模型里“最有深度学习味”。它由输入层、隐藏层、输出层组成每层之间有权重连接激活函数引入非线性反向传播计算梯度来更新权重。入门阶段写这个模型主要不是为了刷精度在企鹅数据上它不一定打得过 SVM而是为了理解训练过程的三个核心概念学习率、损失函数、迭代轮数。from sklearn.neural_network import MLPClassifier from sklearn.metrics import accuracy_score # MLP两个隐藏层每层 16 个神经元ReLU 激活 mlp MLPClassifier( hidden_layer_sizes(16, 16), activationrelu, solveradam, batch_size32, learning_rate_init0.001, max_iter300, random_state42 ) mlp.fit(X_train, y_train) y_pred mlp.predict(X_test) print(MLP Test Acc: {:.4f}.format(accuracy_score(y_test, y_pred))) print(Converged after {} iterations.format(mlp.n_iter_))hidden_layer_sizes(16, 16)表示两个隐藏层、每层 16 个神经元。这个数字没有魔法一般从样本数/特征数的量级往上试先小后大过拟合再加正则化activationrelu是当前默认选择ReLU 解决了深层网络的梯度消失问题但在隐藏层神经元死掉时输出恒为 0要注意调低学习率learning_rate_init0.001配合adam优化器是个稳妥组合max_iter300表示最大训练轮数模型不一定真的跑满 300 轮n_iter_属性会告诉你实际用了多少轮MLP 对特征缩放极度敏感——如果不做StandardScaler模型很可能在几十轮内梯度爆炸或梯度消失损失曲线变成一条水平线。训练 MLP 时最应该看的东西是损失曲线mlp.loss_curve_。把它画出来如果曲线是下降后趋于平缓说明训练正常如果曲线震荡不休说明学习率太大如果曲线岿然不动说明学习了前 10 轮梯度就接近 0通常因为特征未缩放。这份代码我建议你跑完后手动把learning_rate_init改成 0.1 和 0.0001 各跑一次对比损失曲线的形态这个动作比调任何参数都更能建立直觉。5. 避坑手册跑这些入门代码时最常见的五类翻车现场5.1 现象ConvergenceWarning: Stochastic Optimizer: Maximum iterations (200) reached原因逻辑回归或 MLP 默认max_iter不够梯度下降还没收敛到最优解就被强制停止了。解决把max_iter从 200 调到 2000同时检查特征是否做了标准化。如果调大后仍不收敛多半是特征量纲差异太大加上StandardScaler后重跑。这个警告不算硬错误模型照样出准确率但那个准确率不是模型真实水平。5.2 现象决策树训练集准确率 100%测试集准确率骤降原因树的深度过深叶节点里只有一个样本把训练集的噪声也当作规则背下来了。解决限制max_depth从 3 到 6 之间搜索加min_samples_leaf5~10。更本质的做法是直接改用随机森林用多棵限制深度的树投票既保留准确率又增强稳定度。看到 100% 训练准确率时不是应该开心而是应该警惕。5.3 现象KNN 在企鹅数据集上预测体重RMSE 高达 800g等于瞎猜原因没做特征缩放body_mass_g的取值范围在 2700~6300而喙长只有 30~60。计算欧氏距离时喙长方向的差异被淹没在体重方向里模型实际上只在“按体重找邻居”。解决在 Pipeline 里加StandardScaler()RMSE 能立刻降到 300g 左右。这是所有基于距离的模型KNN、SVM-RBF共有的硬要求一万次踩坑的人里九千个栽在这。5.4 现象seaborn.load_dataset(penguins)报错或返回空表原因seaborn 从在线仓库加载数据网络不稳定时下载失败或是本机 seaborn 版本太老不认识 penguins 这个新数据集。解决检查 seaborn 版本pip show seaborn低于 0.11 就升级网络不行时直接下载 CSV 文件用pd.read_csv()读本地路径。我习惯把企鹅数据集缓存到本地data/penguins.csv一劳永逸免得每次跑代码都依赖网络。5.5 现象不同模型在相同数据上跑出的准确率完全一样换了随机种子又不一样原因数据集太小样本划分的随机性影响太大。random_state只是固定了这次的随机划分不代表模型稳定性。解决做 5 折交叉验证取均值与标准差再比较。入门代码为了简洁通常只做一次 train/test split但你心里要清楚单次划分的数字不能作为结论。至少跑cross_val_score(model, X, y, cv5)看一眼均值比单次 accuracy 靠谱得多。6. 把这些代码用起来的三个进阶动作交叉验证、模型保存与阈值可视化你把这六个模型各跑一遍后下一个自然问题是“然后呢” 我建议按下面的顺序做三件事它们都不需要引入新的库。第一把单次train_test_split换成cross_val_score用 5 折交叉验证重测所有模型记录每个模型的均值准确率与标准差。你会发现 SVM 与逻辑回归的均值接近但标准差可能差一倍——这就是“哪个模型更稳”的真实答案。第二用joblib.dump(model, model.joblib)保存你选中的模型再用joblib.load()在新数据上预测直接体验“训练与部署分离”这个工程概念。第三步画出每个模型的 ROC 曲线逻辑回归与朴素贝叶斯有predict_proba可以用roc_auc_score评估排序能力你会发现准确率最高的模型AUC 不一定最高——这个反直觉的体验对理解分类问题的本质很有帮助。from sklearn.model_selection import cross_val_score import joblib import numpy as np # 交叉验证同一pipeline5折看均值±标准差 cv_scores cross_val_score(pipe_lr, X, y, cv5, scoringaccuracy) print(LR 5-Fold CV: {:.4f} ± {:.4f}.format(np.mean(cv_scores), np.std(cv_scores))) # 保存模型模型预处理器一起存部署时不用重新写预处理逻辑 joblib.dump(pipe_lr, logistic_model.pkl)交叉验证的折数cv5是个平衡值数据多可以升到 10小数据集 3~5 就够折数太多单折样本太少均值方差都会变大joblib保存的是“StandardScaler 逻辑回归”的完整 Pipeline部署时只需loaded_pipe.predict(新数据)不需要再手动对特征做标准化AUC 与准确率的冲突是入门者必须接受的事实准确率盯着“阈值 0.5 时对多少个”AUC 盯着“排序能力多强”。在正负样本比例严重失衡的数据上比如欺诈检测里 99:1准确率会骗你AUC 不会。做这些进阶动作时另一个建议是给你的训练脚本加上argparse参数--model、--cv、--output用命令行开关控制跑哪个模型、存哪个结果。这一步的价值在于当你在真实项目里拿到一份全新数据时不需要改代码只要换参数就能批量跑完六个基线模型打印一张对比表格。这一套流程跑顺了你再去看其他复杂的框架会发现所有机器学习工程的本质都是“数据清洗 → 模型选择 → 评估 → 保存”只是规模不同。我见过太多人把精力花在调参和堆模型上但对“如何规范化地组织这些实验”毫无概念结果每次项目都从零重写一遍。把上面这套流程固定下来你会少走很多弯路。希望这些代码和经验能帮你顺利迈过那道入门的坎。本文还有配套的精品资源点击获取