
简介这是一套面向机器学习初学者与数据分析师的 Python 建模与算法实践资源整合了广义可加模型GAM、梯度提升决策树GBDT、决策树CART、BP神经网络与深度学习DNN等常用模型的完整训练记录每个模型均配有对应数据集便于边学边练。压缩包共184个文件以117个 Jupyter Notebookipynb为主体另含35个CSV数据文件、13个TXT说明、8个PNG图表、5个XLSX表格、5个ZIP子包及1个JSON配置整体约304MB目录按模型与数据组织可快速定位。从广义可加模型的非线性拟合、GBDT的集成提升到CART的规则解释、BP与DNN的深层建模均有覆盖能帮助读者系统理解不同算法的适用场景与调优思路。已有109人学习下载适合希望从线性模型拓展到非线性建模、集成学习与深度学习应用的实践者尤其有助于掌握数据预处理、模型构建、参数调优与效果评估的完整流程。1. 一套能跑的机器学习练习代码先弄清里面装着什么拿到“Python机器学习全套代码-数据建模与分析”这个名字很多人第一反应是又一个塞满 PDF 讲义和残缺 demo 的文件夹。实际拆开之后里面的东西比预期实在一批 CSV 训练记录加上按模型拆开的 Python 脚本覆盖 GAM、CART、GBDT、BP、DNN 五类模型每套代码配对应数据。也就是说这不是教你怎么读教材而是直接把数据喂给你、让你把模型跑通、把结果调出来。这个定位决定了两件事一是适合刚刚学完机器学习理论、但还没完整跑过一遍建模流程的人二是适合已经写过模型、但想快速拿现成数据验证某个算法效果的人。数据字段不算规整模型也有调参余地所以照着一行行跑只是第一步真正有价值的是理解每套数据为什么配这个模型以及模型参数在什么范围内才算合理。我用一个下午把压缩包里的数据全部过了一遍下面按我实际操作的顺序来拆。2. 从 CSV 到训练集数据读取、字段对齐与第一道坎2.1 数据清单先理清哪些是特征、哪些是标签压缩包里的 CSV 文件我整理了一下大致分两类。一类是业务数据比如浏览记录.csv、广告.csv、用户.csv、叶子形状.csv文件名能直接看出业务含义另一类是 d2p01F.csv 到 d2p17F.csv 这种编号命名的数据只看文件名看不出用途必须打开看一眼表头才能判断。这里建议你拿到手第一件事就是写个脚本批量打印每个 CSV 的前五行和 shape而不是急着建模。import pandas as pd import os csv_dir ./data for f in os.listdir(csv_dir): if f.endswith(.csv): df pd.read_csv(os.path.join(csv_dir, f)) print(f文件: {f}, 形状: {df.shape}) print(df.head(3).to_string()) print(- * 50)这段代码做的事情很简单遍历目录下所有 CSV输出形状和前三条记录。逻辑上需要注意的是os.listdir的顺序不固定如果想让输出可复现可以加一个sorted()。实际跑的时候我发现有些 CSV 的列名是英文有些是中文有些甚至没有列名——后面这种需要headerNone读入再手动命名否则第一行会被当成表头特征数量和标签全部错位。2.2 缺失值不用慌先看比例再决定怎么填我检查了所有 CSV 的缺失情况发现广告.csv和用户.csv里都有明显的缺失列而且不是随机缺失是某种业务逻辑导致整段为空。很多新手上来就dropna()这在数据量充足时问题不大但当你后面要调 GBDT 时直接删行会丢掉大量样本而且如果缺失和标签分布有关会引入选择偏差。更稳妥的做法是先统计缺失比例按列决定策略比例低于 5% 的用中位数填充高于 30% 的列直接删掉中间的用模型可接受的占位值。特征类型不同填充方式也不同——数值型用中位数分类型用众数时间型用前向填充。下面这段是我处理用户.csv时用的方式import pandas as pd import numpy as np df pd.read_csv(data/用户.csv, encodinggbk) missing_ratio df.isnull().mean() # 数值列中位数填充类别列众数填充 num_cols df.select_dtypes(include[np.number]).columns cat_cols df.select_dtypes(include[object]).columns for col in num_cols: if missing_ratio[col] 0.3: df[col] df[col].fillna(df[col].median()) for col in cat_cols: if missing_ratio[col] 0.3: df[col] df[col].fillna(df[col].mode()[0]) # 缺失超过 30% 的列直接删 df df.drop(columnsmissing_ratio[missing_ratio 0.3].index)这里有个容易被忽略的点fillna(df[col].mode()[0])里的[0]不能省因为mode()返回的是一个 Series哪怕只有一个众数也是 Series 类型直接传给fillna有时会按索引对齐出问题。我一开始没加[0]结果部分行没被填充、静默保留 NaN后面模型训练时报了一堆不明不白的错。2.3 标签藏在最后一列别把整体数据当无监督这批数据里比较坑的一个设计是标签列统一放在最后一列文件名里完全不提示。打开d2p01F.csv一看最后一列是 0/1 二分类标签前面全是特征而叶子形状.csv的最后一列是三个类别名称属于多分类。如果你直接拿整张表去跑聚类或者无监督降维等于把标签也当特征喂进去评估指标会虚高得离谱。我的处理惯例是先看列名有明确标签名的直接引用没有列名或者列名是target/label/class这种通用词的就用iloc[:, -1]取最后一列。判断标准是标签列的取值空间——二分类看是否只有两个值多分类看取值个数是否远小于特征维度回归任务则看是否为连续浮点数。import pandas as pd df pd.read_csv(data/d2p01F.csv, headerNone) X df.iloc[:, :-1] y df.iloc[:, -1] print(特征矩阵形状:, X.shape) print(标签类别:, y.unique())这段代码的逻辑就是按位置切分特征和标签。iloc[:, :-1]取除最后一列外的所有列iloc[:, -1]取最后一列。注意此时特征列名是整数索引后续如果要做特征筛选或者模型可解释性分析最好自己重新命名为f1, f2, ...这样有规律的格式否则画特征重要性图时 X 轴全是数字根本没法解读。3. GBDT 与 CART树模型的参数边界和过拟合防线3.1 为什么这两类模型适合先跑GBDT 全称是梯度提升决策树核心思路是串行训练多棵弱决策树每一棵都在拟合前一棵的残差。CART 则是单棵决策树既支持分类也支持回归通过二分递归分割特征空间。资源里把二者放在一起逻辑很顺先跑通 CART 理解单棵树的决策规则再跑 GBDT 理解集成带来的精度提升两者共用树结构的概念从 CART 到 GBDT 的过渡成本很低。我在看数据时发现浏览记录.csv里的特征是用户浏览行为统计比如浏览次数、停留时长、点击率这类特征和标签之间大概率是非线性关系——高浏览次数不一定代表高转化只有结合时间段和频次才能判断。这种情况下线性模型很难拟合而 CART 可以自动切分出“晚上浏览超过 5 次且停留超过 3 分钟”这类规则区间GBDT 再在这个基础上做残差修正。3.2 CART 参数树深、叶子大小和剪枝的实际影响CART 的默认参数跑出来的树往往很庞大训练集准确率接近 100%测试集一塌糊涂。打开资源里的 CART 脚本里面应该能看到max_depth、min_samples_leaf这类参数。我一般会把max_depth控制在 3 到 6 之间min_samples_leaf设到 10 以上。原因很简单叶子节点里的样本太少统计意义就弱模型学到的多半是噪声。from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) clf DecisionTreeClassifier( max_depth5, min_samples_leaf15, criteriongini, random_state42, ) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(测试集准确率:, accuracy_score(y_test, y_pred))这套参数是多数二分类任务比较稳的起点max_depth5让树不过深min_samples_leaf15保证叶子节点至少包含 15 个样本避免落入纯噪声区间。stratifyy在数据标签不平衡时尤其关键它保证训练集和测试集的类别比例和原始数据一致否则二分类里 90% 是负样本时你随机切分可能把正样本全分到训练集或测试集评估结果完全失真。3.3 GBDT 调参n_estimators 不是越大越好GBDT 的脚本里通常会有n_estimators、learning_rate、subsample三个核心参数。很多新手误以为树越多精度越高实际跑出来的结果往往是树堆到几百棵之后过拟合训练集准确率继续涨、测试集开始掉。正确做法是先用小学习率加大树数量再用早停确定最优树数。from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import roc_auc_score gbdt GradientBoostingClassifier( n_estimators200, learning_rate0.05, max_depth3, subsample0.8, random_state42, ) gbdt.fit(X_train, y_train) y_prob gbdt.predict_proba(X_test)[:, 1] print(AUC:, roc_auc_score(y_test, y_prob))learning_rate0.05是比较保守的起点每棵树只贡献很小一部分预测剩下的交给后续树逐步修正。subsample0.8的意思是每棵树只随机抽 80% 的样本训练这是 GBDT 对抗过拟合的关键——它不只用特征随机性还引入样本随机性相当于给集成模型加了一层天然正则化。注意这里评估用的是 AUC 而不是准确率因为浏览记录这类数据通常正负样本不平衡准确率会被多数类带偏。4. BP 与 DNN网络结构怎么搭、loss 不降该查哪里4.1 BP 神经网络的完整流程从结构定义到权重更新资源里的 BP 代码走的是标准流程定义网络层数、初始化权重、前向传播计算输出、反向传播更新权重、迭代多轮直到收敛。用 sklearn 的MLPClassifier跑 BP 虽然是封装好的但能帮助理解“网络宽度和深度分别影响什么”。隐藏层节点数太少模型学不到复杂模式太多参数量暴涨且容易过拟合。from sklearn.neural_network import MLPClassifier mlp MLPClassifier( hidden_layer_sizes(64, 32), activationrelu, solveradam, alpha0.001, max_iter500, random_state42, ) mlp.fit(X_train, y_train) print(训练集准确率:, mlp.score(X_train, y_train)) print(测试集准确率:, mlp.score(X_test, y_test))hidden_layer_sizes(64, 32)表示两层隐藏层第一层 64 个神经元、第二层 32 个。这个结构对千级样本量、几十个特征的数据集是合理的起点——第一层拓宽提取特征组合第二层压缩成高层抽象。alpha0.001是 L2 正则化系数值越大权重被压缩得越狠对防止过拟合很管用。如果训练集和测试集准确率差距超过 10 个百分点优先调大alpha而不是继续加深网络。4.2 DNN 训练loss 不降时的排查顺序资源里的 DNN 代码应该有 TensorFlow 或 PyTorch 版本不管哪个框架训练时日志里 loss 不降都是常见问题。我跑d2p15F.csv时遇到过 loss 前几轮还在降、后面直接持平的情况。排查顺序很重要先看学习率再看数据标准化最后看网络初始化。import numpy as np from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) print(训练集特征均值:, X_train_scaled.mean(axis0)[:5]) print(训练集特征标准差:, X_train_scaled.std(axis0)[:5])这段标准化的逻辑是fit_transform在训练集上计算均值和标准差并完成转换transform在测试集上用同一套参数做转换。关键点是你不能对测试集单独fit_transform否则测试集被映射到了和训练集不同的特征空间模型预测的意义就丢了。我见过有人在这个环节踩坑训练集标准化后均值接近 0、标准差为 1测试集却完全不是这个分布模型输出乱七八糟但代码不报错属于典型的数据泄漏问题。4.3 学习率与批大小的配对经验DNN 训练日志里还有一个让人困惑的现象loss 曲线剧烈震荡每轮都在 0.6 到 0.9 之间来回跳。这通常是学习率偏大导致的——模型在最优解附近来回摆动收敛不到低点。解决办法并不是直接把学习率除以 10而是先调批大小再动学习率。import tensorflow as tf model tf.keras.Sequential([ tf.keras.layers.Dense(128, activationrelu), tf.keras.layers.Dropout(0.3), tf.keras.layers.Dense(64, activationrelu), tf.keras.layers.Dense(1, activationsigmoid), ]) model.compile( optimizertf.keras.optimizers.Adam(learning_rate0.001), lossbinary_crossentropy, metrics[accuracy], ) history model.fit( X_train_scaled, y_train, validation_data(X_test_scaled, y_test), batch_size32, epochs50, verbose1, )Dropout(0.3)是训练时随机丢弃 30% 的神经元连接推理时自动恢复这是 DNN 对抗过拟合最常用的手段。batch_size32决定了每次参数更新的样本数数据量大时批大小可以提到 64 或 128但要注意批大小越大梯度估计越平滑同样的学习率就可以适当调大批大小小则梯度噪声大学习率必须相应调小否则训练震荡。这是经验和理论结合的地方没有绝对正确的固定值。5. GAM 与模型选型非参数建模逻辑与五个避坑实录5.1 GAM 的适用场景非线性但不黑盒广义可加模型 GAM 在这套资源里相对冷门但它在结构化数据建模里价值很高。GAM 的核心思路是把每个特征单独映射到一个平滑函数上再加总起来做预测。换句话说它不假设特征和标签是线性关系但保留了每个特征单独贡献的可解释性——这对业务方要求“告诉我哪个因素最影响转化率”的场景非常友好。资源里的 GAM 代码配合叶子形状.csv使用这个数据集的三个类别对应不同叶形特征特征与类别之间的关系明显非线性。用线性模型去拟合会丢失交互规律用 DNN 又过拟合GAM 正好卡在中间。如果你用的是pyGAM库核心参数是n_splines——样条基函数个数太少了平滑过度太多了又回到过拟合。5.2 避坑实录一数据泄漏归一化放在切分前现象GBDT 和 DNN 训练集 AUC 都超过 0.95测试集 AUC 只有 0.55差距大得离谱。原因在train_test_split之前就做了全量标准化测试集的分布信息已经混入训练集中。GBDT 本身对单调变换不敏感所以影响小但 DNN 的梯度计算对特征尺度变化极其敏感数据泄漏在你标准化的时候已经发生后面怎么调参都救不回来。解决先切分再在训练集上fit标准化器最后用同一个标准化器transform测试集。这套顺序是铁的纪律。5.3 避坑实录二标签不平衡准确率骗了你现象d2p03F.csv上跑 CART测试集准确率 0.93看起来效果很好但类别分布检查发现正样本只占 7%。原因准确率评估的是“猜对所有样本的比例”模型只要把所有样本都预测为负类准确率就有 93%。这个 0.93 毫无意义。解决换评估指标。二分类任务用 AUC 或 F1-score多分类任务用 macro-F1。同时训练时给少数类加大权重sklearn 里用class_weightbalanced即可一个参数解决。5.4 避坑实录三GBDT 训练日志里的坑n_estimators 过大导致的记忆效应现象浏览记录.csv上 GBDT 树数加到 500 之后训练集 AUC 接近 1测试集反而下降。原因树数量越多模型对训练集中个别样本的噪声模式记忆越深这就是典型的“记忆效应”。GBDT 虽然每棵树都很弱但几百棵树累加起来拟合能力足够强到记住训练集的噪声。解决加早停机制或者直接观察验证集 loss 曲线选择最佳树数。实操中我一般把n_estimators设为 500 但配合validation_fraction0.1和n_iter_no_change10让模型自己决定何时停。5.5 避坑实录四DNN 训练时没有划分验证集现象DNN 每轮打印的 loss 持续下降但没有任何指标能反映泛化能力。跑完之后去测试集上验证效果远差于预期。原因训练过程中只看了训练集 loss模型是否过拟合完全没有监控信号。训练曲线只能说明拟合能力不能说明泛化能力。解决训练时固定比例的数据做验证集每个 epoch 都打印验证集 loss 和准确率。如果验证集 loss 连续 10 轮不下降就该早停了。这个做法在任何深度学习框架里都是标配但新手特别容易忽略。5.6 避坑实录五时间序列类数据直接随机切分现象广告.csv里带日期字段直接随机切分训练集和测试集模型在测试集上的表现忽高忽低极不稳定。原因随机切分会把时间上相邻的记录打散训练集中混入未来数据、测试集中混入过去数据等于是让模型“偷看未来”评估结果完全不可信。解决按时间字段顺序切分比如前 80% 时间段的记录作为训练集后 20% 作为测试集或者用TimeSeriesSplit交叉验证。6. 把模型跑起来最小验证清单与一套通用评估脚本6.1 快速验证每套代码是否跑通对于这套资源里的每个模型我建议你先把跑通作为第一目标先不追求精度。最小验证清单是数据能读入、特征和标签能正确切分、模型能完成训练、训练日志里有 loss 输出、测试集上能产生预测结果。任何一个环节卡住先查数据格式再查参数配置不要急着换模型。6.2 一份通用评估脚本覆盖五类模型下面这组代码可以直接套用到资源里任何一套数据上它会同时跑出 CART、GBDT、BP 三个模型的 AUC 分数DNN 因为需要标准化可以单独处理。我习惯用这个脚本做基线对比先看哪个模型在不调参的情况下就有不错表现再针对它重点调优。import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import GradientBoostingClassifier from sklearn.neural_network import MLPClassifier from sklearn.metrics import roc_auc_score df pd.read_csv(data/d2p17F.csv, headerNone) X df.iloc[:, :-1].values y df.iloc[:, -1].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) models { CART: DecisionTreeClassifier(max_depth5, random_state42), GBDT: GradientBoostingClassifier(n_estimators100, learning_rate0.1, random_state42), BP: MLPClassifier(hidden_layer_sizes(32,), max_iter300, random_state42), } for name, model in models.items(): model.fit(X_train, y_train) prob model.predict_proba(X_test)[:, 1] print(f{name} AUC: {roc_auc_score(y_test, prob):.4f})这段脚本的价值在于对照。random_state统一固定为 42保证三个模型在完全相同的训练集和测试集上比较否则模型间的性能差异可能只是数据切分的随机性造成的。stratifyy在标签不平衡时尤其重要它能保证训练集和测试集的类别比例一致。6.3 GAM 的快速拟合与稳定性检查最后说 GAM。如果你用的是pyGAM拟合的代码很短但验证要单独跑一下确认平滑项没有过度弯曲。我一般在拟合后打印每个特征的置信区间如果区间宽度过大说明该特征样本量不足这时候要么减少样条数量要么直接去掉这个特征。from pygam import LogisticGAM, s gam LogisticGAM(s(0) s(1) s(2)).fit(X_train, y_train) for i, term in enumerate(gam.terms): if term.isintercept: continue coef_ci gam.confidence_intervals(termterm) width coef_ci[:, 1] - coef_ci[:, 0] print(f特征 {i}: 置信区间平均宽度 {width.mean():.4f})这套检查的逻辑是置信区间宽度代表了模型对该特征效应估计的不确定性宽度太大说明这个特征提供的信息有限或者分布极端。从那以后我每次跑 GAM 都会先做这个稳定性检查区间过宽的平滑项再好看我也删掉能少走不少弯路。从拿到这套资源到现在我把每个 CSV 的表头、缺失、标签分布都记录成了一个核对表再跑模型。这个过程养成习惯之后换任何一份新数据都能在十分钟之内判断该用什么模型、参数从多少起步。希望这份拆解能帮你在自己的建模任务里少踩几个坑。本文还有配套的精品资源点击获取