简介:这份《1. 机器学习前置知识》配套数据集面向刚进入机器学习领域、需要动手熟悉数据处理流程的学习者,也可供教学演示与课程练习使用。压缩包共收录6个文件,以4个CSV表格和1个HDF5文件为主,另含1个JSON文本,整体约2.76MB,覆盖结构化表格、层级数据与文本样本等常见格式,便于练习读取、清洗、转换与可视化等前置操作。目前已有4371人学习下载,说明其在入门练习场景中具有一定参考价值。数据集围绕真实业务与文本样本组织,读者可据此熟悉不同格式的加载方式、字段理解、缺失值处理与基础统计,并为后续特征工程和模型训练打下基础。文件体量轻巧,适合快速导入课程实验或自学项目,帮助建立从原始数据到建模准备的基本链路。
1. 从一堆 CSV 到可跑的前置知识:这份配套数据集到底解决哪几步
很多人拿到《机器学习前置知识》配套数据集,第一反应是打开 CSV 看列名,然后直接丢进模型。我的习惯相反:先把这份机器学习数据集当成前置知识的练习场,确认它覆盖矩阵运算、梯度、概率分布和表格数据预处理中的哪几块,再决定哪些文件进训练、哪些只做校验。
它不是拿来刷榜单的大数据集,而是把线代、微积分、概率统计和 Python 数据处理串起来的小样本集合。每一份表都能对应一个可验证的动作:矩阵乘法、数值梯度、分布采样、缺失值填充、训练集测试集切分。
适合刚补完基础课、准备做第一个端到端练习的人,也适合带新人时找一套能反复回放的数据。先跑通加载和校验,再谈模型,后面会省掉很多返工。
2. 数据切片与选型:矩阵、梯度、分布、回归四类文件怎么分工
这份配套数据集最容易踩的坑,是把所有 CSV 都当成“训练数据”。前置知识类数据通常混着练习表和任务表,前者用来验证公式,后者才适合进模型。先按逻辑分层,再决定读取顺序,比一上来pd.concat所有文件稳得多。
2.1 先看数据分层:四类文件各管什么
我拿到这类包,一般先按用途分成四层。文件名可能不同,但职责基本跑不出下面这几类:
| 逻辑分层 | 典型内容 | 主要用途 | 是否直接进模型 |
|---|---|---|---|
| 线性代数练习 | 小矩阵、向量、转置与乘法样例 | 手算结果和 NumPy 结果对照 | 否 |
| 微积分与梯度 | 一维/二维采样点、loss 曲线、参数点 | 数值梯度、梯度下降轨迹 | 部分 |
| 概率统计采样 | 正态、均匀、伯努利样本 | 均值方差、分位数、采样偏差 | 否 |
| 表格任务数据 | 回归或分类小表,含缺失值和类别列 | Pipeline、切分、评估 | 是 |
线性代数练习表通常只有几十行,列名可能是a11、a12、b1这类,拿它训练模型没有意义。它的价值在于让你用X @ w、np.linalg.inv、np.linalg.eig复算一遍,确认矩阵维度、转置方向和广播规则没有记反。微积分采样表也一样,重点是把解析梯度和数值梯度对齐,而不是追求多高的准确率。
概率统计采样表适合做“分布体检”:用样本估计均值和方差,画直方图,检查分位数是否贴近理论值。表格任务数据才是后面 Pipeline 的主角,通常会有连续目标列或离散标签列,还会故意留一些缺失值、类别列和 ID 列,用来练切分和防泄漏。
2.2 选型判断:先看任务类型,再看行数和缺失率
确定分层后,第二步是判断哪份表值得进训练。我的顺序是:先看目标列是连续还是离散,再看行数够不够做一次可靠切分,最后看缺失率和唯一值。行数太少时,交叉验证的方差会大得离谱,调参基本是玄学。
| 判断项 | 我常用的阈值 | 动作 |
|---|---|---|
| 行数 | 少于 200 行 | 只做冒烟测试,不做正式调参 |
| 缺失率 | 单列高于 30% | 先标记缺失指示,不直接均值填充 |
| 类别数 | 单列高于 50 类 | 先合并稀有类别或做哈希,不硬上 OneHot |
| 单列唯一值 | 接近总行数 | 重点查 ID 泄漏 |
| 与目标相关性 | 数值列高于 0.98 | 查是否是后验字段或标签派生列 |
行数在 500 到 5000 之间的表格任务数据最适合做 baseline:切分稳定,训练时间短,改一个参数能立刻看到反馈。少于 200 行不是不能用,而是只能用来验证流程通不通,不能拿准确率高低下结论。缺失率高的列也不要急着填,先保留缺失模式,很多时候“是否缺失”本身就是有效特征。
类别数多的列要特别小心。OneHotEncoder在类别数上百时会迅速把特征矩阵撑大,小数据上很容易过拟合。常见做法是先把出现次数少于某个阈值的类别合并成rare,再进编码管道。单列唯一值接近行数的,大概率是 ID、订单号或时间戳,这类列在切分前就要剔掉。
2.3 读取前的检查表:文件清单和字段字典
真正开始读文件前,我会先建一个很轻的字段字典。字段字典至少写清四件事:列名、dtype、缺失率、业务含义。没有业务含义时,至少写清“这是特征、标签还是 ID”。这份字典后面排查标签泄漏时能救命。
文件清单也要先列出来,不要边读边猜。用Path.rglob("*.csv")扫一遍,把相对路径打印出来,确认有没有重复文件、临时导出文件、带copy后缀的副本。很多“数据不一致”不是模型问题,而是读到了上一次导出的旧表。
提示:前置知识数据里的小矩阵表、采样表不要和表格任务数据合并。它们的列语义不同,合并后只会得到一堆无法解释的特征。
3. 环境与加载:用 pandas 和 numpy 做入口校验的具体命令
选好文件后,先隔离环境,再做入口校验。小数据集最容易让人跳过环境检查,结果后面报错时,分不清是数据问题、依赖问题还是代码问题。我一般用一个干净虚拟环境跑加载脚本,确认每个文件都能被稳定读进来。
3.1 环境准备:虚拟环境与最小依赖
先建虚拟环境,再装最小依赖。不要在一个已经装了几百个包的环境里排查数据问题,那样报错信息会变得非常吵。
python -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip python -m pip install numpy pandas scikit-learn matplotlibpython -m venv .venv在当前目录创建隔离环境,.venv是目录名,可以改,但后面激活命令要跟着改。source .venv/bin/activate适用于常见 Unix shell,Windows 下换成.venv\Scripts\activate。先升级pip是为了避免旧解析器在依赖冲突时给出误导性报错。numpy和pandas负责数据读取与矩阵运算,scikit-learn负责切分、预处理和 baseline,matplotlib只用于画分布和 loss 曲线。
依赖不需要一次装全。先保证加载和校验能跑,再按报错补包。版本也不要在这一步锁死,先让环境自己解析;如果后面出现 API 差异,再按报错回退到兼容版本。这样比一开始抄一份来路不明的版本清单更可控。
3.2 入口校验:批量读取 CSV 并打印 schema
下一步是批量扫描 CSV。这里只做预览读取,不直接全量拼接。每个文件的列名、列数、dtype 先打印出来,确认结构再往下走。
from pathlib import Path import pandas as pd DATA_ROOT = Path("data") csv_files = sorted(DATA_ROOT.rglob("*.csv")) for path in csv_files: try: # 先只读前 5 行,避免大文件拖慢扫描 df = pd.read_csv(path, nrows=5) except UnicodeDecodeError: # 常见兜底:部分导出文件使用 gbk df = pd.read_csv(path, nrows=5, encoding="gbk") except pd.errors.ParserError: # 分隔符不是逗号时,让 pandas 自动推断 df = pd.read_csv(path, nrows=5, sep=None, engine="python") print( f"{path.relative_to(DATA_ROOT)} | " f"preview_shape={df.shape} | " f"columns={list(df.columns)}" )DATA_ROOT指向数据根目录,实际包内目录名不同就改这一行。rglob("*.csv")会递归扫描子目录,适合配套数据集这种按知识点分层的结构。nrows=5只读预览,打印出来的preview_shape不是全量行数,别拿它当数据规模。encoding="gbk"是兜底,不是默认;如果所有文件都强制gbk,UTF-8 文件反而会乱码。sep=None, engine="python"用于分隔符不是逗号的情况,速度会比默认 C 引擎慢,但扫描阶段可以接受。
这个脚本的目标不是“读完所有数据”,而是回答三个问题:文件能不能打开、列名是否一致、有没有明显编码问题。只要有一个文件读失败,就先停下来修读取参数,不要带着坏文件继续跑模型。
3.3 单文件深读:dtype、缺失率、重复列
批量扫描通过后,再挑出表格任务数据做单文件深读。这个阶段看 dtype、缺失率和唯一值,目的是提前发现 ID 列、异常类型和缺失模式。
import pandas as pd def profile_table(path, **read_kwargs): df = pd.read_csv(path, **read_kwargs) report = { "shape": df.shape, "columns": list(df.columns), "dtypes": {c: str(t) for c, t in df.dtypes.items()}, "missing_rate": df.isna().mean().round(4).to_dict(), "nunique": df.nunique(dropna=False).to_dict(), } return df, report df, report = profile_table( "data/tabular/classification_demo.csv", # 如果导出时带了索引列,优先在这里处理 # index_col=0, ) print(report) print(df.head())profile_table里的missing_rate按列计算缺失比例,round(4)只是让输出别太长。nunique(dropna=False)会把缺失值也当成一个取值统计,方便判断某列是不是几乎每行都不同。df.head()用来肉眼检查列值是否符合预期,尤其是类别列有没有前后空格、标签列有没有被读成字符串。
如果输出里出现Unnamed: 0,大概率是导出时把索引写进了 CSV。可以在读取时加index_col=0,也可以读完后drop(columns=["Unnamed: 0"]),但不要把它当特征。ID 列同理,唯一值接近行数、和标签没有业务关系的列,切分前就要剔除。
4. 预处理与特征:把标准化、缺失值、切分写进 Pipeline 的参数表
表格任务数据进入模型前,最容易出错的是顺序:先切分还是先填充,先标准化还是先拼列。正确顺序是先切分,再在训练集上拟合预处理器,最后用同一个预处理器转换测试集。手工对全量数据做填充和标准化,看起来省事,实际会把测试集信息漏进训练过程。
4.1 先切分再预处理:避免标签泄漏的顺序
下面这段代码把切分、数值管道、类别管道和分类器串成一个Pipeline。重点看切分发生的位置:它在任何fit之前。
import pandas as pd from sklearn.model_selection import train_test_split from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.linear_model import LogisticRegression df = pd.read_csv("data/tabular/classification_demo.csv") target_col = "target" X = df.drop(columns=[target_col]) y = df[target_col] num_cols = X.select_dtypes(include=["int64", "float64"]).columns.tolist() cat_cols = X.select_dtypes(include=["object", "category", "bool"]).columns.tolist() # 先切分,后面的 imputer/scaler 只在训练集上 fit X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) num_pipe = Pipeline([ ("imputer", SimpleImputer(strategy="median")), ("scaler", StandardScaler()), ]) cat_pipe = Pipeline([ ("imputer", SimpleImputer(strategy="most_frequent")), ("onehot", OneHotEncoder(handle_unknown="ignore")), ]) preprocess = ColumnTransformer([ ("num", num_pipe, num_cols), ("cat", cat_pipe, cat_cols), ]) model = Pipeline([ ("preprocess", preprocess), ("clf", LogisticRegression(max_iter=1000)), ]) model.fit(X_train, y_train) print("test accuracy:", model.score(X_test, y_test))test_size=0.2表示留出 20% 做测试,小数据上不要低到 10%,否则测试集波动太大。random_state=42是为了让切分可复现,不是模型参数。stratify=y在类别不平衡时很重要,它能保证训练集和测试集的标签比例接近。SimpleImputer(strategy="median")对数值列用中位数,比均值更抗极端值;类别列用most_frequent,比填一个固定字符串更稳。OneHotEncoder(handle_unknown="ignore")让测试集出现新类别时不直接报错,而是编码成全零。
ColumnTransformer的价值在于显式声明哪一列走哪条管道。它默认会丢弃没被列出的列,这个行为在小数据上反而安全,因为 ID、索引列和临时列不会被偷偷带进模型。LogisticRegression(max_iter=1000)只是 baseline,max_iter调大是为了避免小数据上因为迭代不足出现收敛警告。
4.2 用 ColumnTransformer 组装数值与类别管道
手工写df[num_cols].fillna(...)再pd.concat不是不行,但很难保证训练集和测试集用同一套统计量。ColumnTransformer把“列选择”和“变换”绑在一起,fit时记住训练集的统计量,transform时复用,后面保存模型也更干净。
数值管道里,填充和标准化的顺序不要反。先填充再标准化,标准化器看到的才是完整数组;先标准化再填充,缺失值会参与均值和方差计算,结果会偏。类别管道里,填充要放在编码前,否则OneHotEncoder会把缺失值当成一个新类别,语义上不清晰。
如果数据里还有日期列,不要直接丢进StandardScaler。常见做法是先转成时间戳或拆出年、月、日、星期几,再决定进数值管道还是类别管道。前置知识数据里的日期列通常是为了练特征工程,不是为了让你把字符串硬编码成数字。
4.3 参数怎么改:缺失值、标准化、未知类别
参数不要照抄,先看数据分布再改。下面这张表是我在小数据上最常调的几项:
| 参数 | 常用值 | 什么时候改 |
|---|---|---|
test_size | 0.2 | 样本少于 500 时改 0.25,保证测试集不过小 |
random_state | 42 | 需要多次回放时换 3 个固定种子 |
stratify | y | 分类任务默认开,回归任务不需要 |
| 数值填充 | median | 分布偏斜明显时优先中位数 |
| 类别填充 | most_frequent | 类别缺失不是随机时,改成单独类别 |
handle_unknown | ignore | 测试集可能出现新类别时必须开 |
max_iter | 1000 | 出现收敛警告时调大,不要盲目调学习率 |
改完参数后,建议加一段目标相关性检查。它不是正式的泄漏检测,但能快速发现异常高的相关列。
corr = ( X_train.select_dtypes(include="number") .corrwith(y_train) .abs() .sort_values(ascending=False) ) print(corr.head(10))select_dtypes(include="number")只对数值列计算相关,类别列要先编码或跳过。corrwith(y_train)计算每个数值列和目标的相关性,.abs()取绝对值,方便同时看正相关和负相关。head(10)只打印前十个。相关性高不一定是泄漏,但如果某一列相关接近 1,且业务上不像特征,优先查它是不是标签派生列或后验字段。
5. 常见问题排查:编码、分隔符、标签泄漏与梯度异常的五个现场
这份数据集不大,但小数据更容易被错误操作放大。下面五个现场是我在类似资源包里反复遇到的,按“现象 → 原因 → 解决”记录,方便下次直接对照。
5.1 加载阶段的三个现场
- 现象:
UnicodeDecodeError,或者中文列名变成乱码。
原因:CSV 来自不同系统,UTF-8、带 BOM 的 UTF-8 和 GBK 混在一起。解决:逐文件探测编码,先nrows=5试读,失败再换编码,不要全局强制一种编码。
import pandas as pd def read_csv_safely(path, **kwargs): for enc in ("utf-8", "utf-8-sig", "gbk"): try: return pd.read_csv(path, encoding=enc, **kwargs) except UnicodeDecodeError: continue raise ValueError(f"无法识别编码: {path}")utf-8-sig用来处理带 BOM 的文件,gbk是常见兜底。**kwargs可以把nrows、sep、index_col继续传进去。这个函数只解决编码,不解决分隔符;如果读出来仍然只有一列,继续看下一条。
现象:读进来只有一列,所有字段挤在同一个字符串里;或者多出
Unnamed: 0。
原因:分隔符不是逗号,或者导出时把索引写进了文件。解决:先看原始文件前 3 行,确认分隔符;再用sep=";"、sep="\t"或sep=None, engine="python"试读,索引列用index_col=0处理。这个现场不要靠猜。打开文件头看一眼前三行,比反复改
read_csv参数快。索引列如果已经读进来,读完后drop掉,但不要把它当数值特征,否则模型会学到行号顺序。现象:dtype 全是
object,数值列无法参与计算。
原因:列里混了空字符串、单位符号、千分位逗号或中文数字。解决:先用pd.to_numeric(errors="coerce")定位坏值,再决定清洗规则;不要直接astype(float),那样只会得到一个不指明位置的报错。
5.2 建模阶段的三个现场
现象:训练集准确率 0.99,测试集掉到 0.6。
原因:ID、时间戳、后验字段泄漏,或者预处理在切分前用了全量统计量。解决:切分前剔除 ID 类列,时间序列按时间切分,填充和标准化只在训练集fit。再跑一次单列唯一值检查,唯一值接近行数的列重点看。现象:
OneHotEncoder报未知类别,或者梯度下降 loss 变成NaN。
原因:测试集出现训练集没见过的类别;或者学习率过大、特征未标准化。解决:类别编码加handle_unknown="ignore",并统计被忽略的比例;数值特征先标准化,学习率从更小的值试,打印每一步 loss,必要时做梯度裁剪。这两个问题经常一起出现:类别列没处理好,特征矩阵稀疏且尺度混乱,模型系数很容易爆掉。先把列类型和缺失率查清,再谈调参,能省掉很多无效尝试。
注意:排查顺序建议固定为“原始文件前 3 行 → dtype 与缺失率 → 切分位置 → 预处理 fit 位置 → loss 曲线”。跳步排查容易把数据问题误判成模型问题。
6. 进阶验证:用梯度检验和小样本回放确认数据可用
数据集能不能用,不看它有多少行,而看同一份输入能否稳定复现同一条 loss 曲线。前置知识数据里通常有梯度练习表,我习惯用数值梯度给解析梯度打分,再用小样本回放检查切分稳定性。
6.1 梯度检验:用数值梯度给解析梯度打分
import numpy as np def loss(w, X, y): residual = X @ w - y return 0.5 * np.mean(residual ** 2) def grad_analytic(w, X, y): residual = X @ w - y return X.T @ residual / len(y) def grad_numeric(w, X, y, eps=1e-6): g = np.zeros_like(w) for i in range(len(w)): w_pos = w.copy() w_neg = w.copy() w_pos[i] += eps w_neg[i] -= eps g[i] = (loss(w_pos, X, y) - loss(w_neg, X, y)) / (2 * eps) return g rng = np.random.default_rng(42) X = rng.normal(size=(32, 3)) y = rng.normal(size=32) w = rng.normal(size=3) ga = grad_analytic(w, X, y) gn = grad_numeric(w, X, y) rel_error = np.linalg.norm(ga - gn) / (np.linalg.norm(ga) + np.linalg.norm(gn)) print("relative error:", rel_error)这里用的是中心差分,eps=1e-6是常用起点。rel_error在 float64 下通常能落到1e-6到1e-4量级;如果明显大于1e-4,优先检查矩阵方向、mean和sum是否混用、有没有漏掉偏置项。这个检查不依赖具体数据集大小,很适合拿来验证前置知识里的梯度推导有没有记反。
6.2 小样本回放:固定随机种子做三次一致性检查
再取 200 到 500 行表格任务数据,用三个固定random_state跑同一个 Pipeline,记录每次的测试集准确率。三次结果的波动如果超过 0.05,说明数据量太小或切分不稳定,别急着调模型,先增加样本或改用分层切分。从那以后我每次拿到新的机器学习数据集,都会先跑一遍安全读取、切分前检查和梯度检验,再决定要不要把它塞进完整训练。希望帮到你。
本文还有配套的精品资源,点击获取