(一)站稳脚:用Scikit-learn跑通第一条Pipeline

📅 2026/6/23 16:06:25
(一)站稳脚:用Scikit-learn跑通第一条Pipeline
从CSV到模型一个完整的ML项目长什么样你装好了Python跑通了Ollama手上有一本《动手学深度学习》的克隆。但你可能还在想一个问题机器学习项目到底是怎么从零跑到尾的网上有大量教程教你怎么调fit()和predict()但很少有教程把你从有一个CSV文件带到训练出一个模型并知道它好不好用。这篇就是来补这个缺的。选一个最经典的入门数据集——泰坦尼克号。不是因为它有趣是因为它小只有891行、干净不需要分布式处理、而且足够展示一条完整的ML Pipeline长什么样。从Pandas开始把数据拿过来import pandas as pd import numpy as np import os import urllib.request from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, confusion_matrix # 下载到本地跨平台路径 data_dir os.path.expanduser(~/data) os.makedirs(data_dir, exist_okTrue) file_path os.path.join(data_dir, titanic.csv) if not os.path.exists(file_path): url https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv urllib.request.urlretrieve(url, file_path) df pd.read_csv(file_path) df.head()如果你能看到五列数据——Survived、Pclass、Sex、Age、Fare——说明环境没问题。终端里应该出现这样的表格PassengerId Survived Pclass ... Fare Cabin Embarked 0 1 0 3 ... 7.2500 NaN S 1 2 1 1 ... 71.2833 C85 C 2 3 1 3 ... 7.9250 NaN S 3 4 1 1 ... 53.1000 C123 S 4 5 0 3 ... 8.0500 NaN S这里有个很多人会忽略的点先看数据长什么样别急着建模。看看每一列有多少空值、数据类型是什么、数值分布怎么样print(df.info()) # df.describe()df.info()会告诉你每一列有多少非空值class pandas.core.frame.DataFrame RangeIndex: 891 entries, 0 to 890 Data columns (total 12 columns): PassengerId 891 non-null int64 Survived 891 non-null int64 Pclass 891 non-null int64 Name 891 non-null object Sex 891 non-null object Age 714 non-null float64 SibSp 891 non-null int64 Parch 891 non-null int64 Fare 891 non-null float64 Cabin 204 non-null object Embarked 889 non-null object你会发现Age列有177个空值891 - 714 177Cabin列更是惨不忍睹只有204个非空。这些空值会在训练时报错所以必须处理。特征工程把原始数据变成模型能消化的格式机器学习的核心不是算法是你怎么把原始信息变成数字。# 选几列能用的特征 features [Pclass, Sex, Age, SibSp, Parch, Fare] # 填充空值——用中位数比用均值更鲁棒 df[Age] df[Age].fillna(df[Age].median()) # 把性别从文字变成数字 df[Sex] df[Sex].map({male: 0, female: 1}) X df[features] y df[Survived] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42)这一步的思考过程比代码重要为什么用中位数而不是均值填Age因为年龄分布可能偏斜中位数不受极端值影响。为什么Sex要变成0/1因为模型只能处理数字。为什么Pclass不需要归一化因为它是排序特征123树模型对尺度不敏感。如果你不理解这些为什么建议停下来翻一下 d2l-zh 第4章线性回归和第5章多层感知机。那些公式在这里会变成哦原来是干这个用的。训练和评估看模型实际表现model RandomForestClassifier(n_estimators100, max_depth5, random_state42) model.fit(X_train, y_train) y_pred model.predict(X_test) print(classification_report(y_test, y_pred))输出大概是这样的你的数字可能略有浮动因为随机种子不同precision recall f1-score support 0 0.78 0.88 0.83 105 1 0.79 0.65 0.71 74 accuracy 0.78 179 macro avg 0.78 0.76 0.77 179 weighted avg 0.78 0.78 0.78 179一个不到20行代码的模型准确率 78% 左右。这对第一个模型来说是完全正常的。怎么看这四个指标precision模型说这个人没活时78% 是对的。说这个人活了时79% 是对的。recall所有没活的人里模型找出了 88%。所有活下来的人里模型找出了 65%。f1-scoreprecision 和 recall 的调和平均越接近 1 越好。support测试集里实际的人数。这里 105 人没活、74 人活下来了。更重要的不是分数本身而是你理解了整个流程读数据 → 清理 → 选特征 → 训练 → 评估。这条Pipeline在以后无论你做什么ML项目甚至做大模型微调都会反复出现。现在你该做什么跑通上面的代码之后你有三条路可以选第一条把同样的流程用在其他数据集上。Kaggle上搜索Titanic的同类比赛或者换一个更现代的数据集——比如Wine Quality、Iris。用同样的代码结构跑一遍你会发现90%的代码不用改改的只是数据列名和特征选择。第二条理解你刚用了什么算法。随机森林是很多棵决策树投票——听起来简单但里面涉及Bagging、特征随机采样、OOB评估。去 ML-From-Scratch 打开random_forest.py你会发现核心逻辑只有几十行循环。读一遍你就能说清楚随机森林和单棵决策树的区别是什么——面试高频题。第三条了解Scikit-learn的全貌。Scikit-learn官方文档 的中文翻译版在 sklearn-doc-zh。不用从头读到尾。遇到新问题时去搜——怎么分训练集→train_test_split怎么调参数→GridSearchCV怎么评估分类器→classification_report。用多了自然记住。验收标准跑通了吗你的脚本应该能无报错地输出classification_report。逐条检查df.head()显示了表格数据5 行 x 12 列df.info()中的 Age 空值已被填充显示 891 non-nullX_train和X_test的行数加起来等于 891713 179classification_report的 accuracy 在 0.75-0.85 之间没有报错ValueError: Input contains NaN——说明空值处理干净了如果你的准确率低于 0.7检查特征选择是不是只用了Sex和Pclass两列加上Age、Fare、SibSp通常能提升 5-10%。参考项目这条 Pipeline 涉及的工具和进一步学习资源d2l-zh 第4-6章 线性回归、MLP、模型选择的理论基础ML-From-Scratch读懂随机森林和决策树的纯NumPy实现sklearn-doc-zhScikit-learn API的中文速查手册三个够了。重点是把上面那条Pipeline跑通、理解、复用而不是再去Star另外十个仓库。这篇之后你从有一个 CSV 文件走到了训练出一个模型并知道它好不好用。下一篇文章进入深度学习和 Transformer——从调 sklearn API 到手写一个神经网络理解梯度下降到底在做什么以及Attention Is All You Need那一张图到底在画什么。本系列 Notebook 及配套文章github.com/ASPIRINH/hands-on-llm常见问题Q:pandas.read_csv报SSL: CERTIFICATE_VERIFY_FAILEDA: 把 URL 换成本地文件。先下载 CSV浏览器打开https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv保存为~/data/titanic.csv然后df pd.read_csv(~/data/titanic.csv)。Q:classification_report出来的全是 0 的分数A: 检查y_test的取值分布——如果用print(y_test.value_counts())发现全是同一类说明 train/test 切分出了问题。检查random_state42是否写对了或者把test_size从 0.2 改成 0.3。Q: 准确率只有 60% 多A: 正常。只用 Pclass 和 Sex 两列做特征就是这样。把 Age、Fare、SibSp 都加进去上面的代码已包含应该能到 80% 左右。如果你加了所有特征仍然低检查 Age 的空值有没有处理干净。Q:ValueError: Input contains NaN, infinity or a value too large for dtypeA: 特征里还有空值。运行df[features].isna().sum()看看哪一列还有 NaN。Titanic 的 Age 和 Cabin 都有空值——代码里只填充了 Age如果使用了其他列也可能有空值。Q: 运行df.head()只显示了 3 列而不是 12 列A: 终端宽度不够。加一行pd.set_option(display.max_columns, 20)再运行df.head()。