Week 05:sklearn Baselines、数据划分与指标

发布于 2026-07-26 05:00 4743 字 24 min read

Week 05:sklearn Baselines、数据划分与指标。fish-first 终端教学,面向 CachyOS、VS Code、uv 和 Python 学习路线。
Oh My Pi / weekly tutorial / week05-sklearn-baselines
miku@cachyos:~/Code/python-learning$ omp teach week05-sklearn-baselines --fish-first --step-by-step
source638 行教学文档
weekWeek 05
shellfish-first 命令版
backlink20 周计划

Week 05:sklearn Baselines、数据划分与指标

返回主线计划:USTC 统计学:AI / 量化金融 20 周终端式成长计划

本周进入 Phase 2:从“会清洗数据”转向“能建立一个可信的机器学习 baseline”。这里的重点不是追求最高分,而是建立一套以后每个项目都会复用的最小训练流程:固定数据划分、训练多个经典模型、记录指标、写下第一轮误差分析。

本文默认你使用 CachyOS、fish shell、VS Code、uv、Python、Git。命令都按 fish 写;如果不是 fish,需要自己转换激活命令。

0. 本周详细教学:语法、规范、验收

本节不是追加在尾部的复习,而是本周正文的入口。先读这里,再做后面的命令和项目。

0.1 本周真正要学会什么

维度要求
知识点train/test、Dummy baseline、LogisticRegression、指标
代码语法能从空文件写出本周核心脚本,而不是只复制运行
程序规范函数拆分、路径清楚、输入输出明确、错误能解释
交付物src/train_baselines.py
验收方式从 fish 终端运行命令,得到可复查的文件或指标

0.2 代码语法精讲

下面的代码不是最终答案,而是本周必须理解的最小骨架:

omppython
from sklearn.dummy import DummyClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report

models = { “dummy”: DummyClassifier(strategy=“most_frequent”), “logreg”: LogisticRegression(max_iter=1000), } for name, model in models.items(): model.fit(X_train, y_train) pred = model.predict(X_valid) print(name) print(classification_report(y_valid, pred))

读代码时按四步检查:输入从哪里来;中间变量的类型和 shape 是什么;函数或脚本输出什么;哪些错误应该显式报出来。

0.3 本周程序规范

  • 所有路径用相对路径或 `pathlib.Path`,不要写死 `/home/miku/...`。
  • 核心逻辑进 `src/`,notebook 只做探索和解释。
  • 每个脚本能从 fish 终端运行,并在 README 写出命令。
  • 输出必须落盘到 `reports/`、`figures/` 或 `outputs/`,不能只在屏幕上看。

0.4 本周练习分层

层级任务不合格表现合格验收
最小练习手写上面的最小骨架只在 notebook 里运行终端运行成功
标准练习把逻辑拆成函数/模块一个大脚本从头写到尾至少 2 个函数,职责清楚
项目练习生成本周交付物 src/train_baselines.py只有屏幕输出文件落盘,可复查
复盘练习写 3 个错误和修复只写“已解决”写清报错、原因、修复、预防

0.5 本周和主线的连接

1. 本周目标

完成一个可以重复运行的 src/train_baselines.py

  • 读取一个表格数据集。
  • 区分特征 X 与目标 y
  • train / validation / test 三份划分。
  • 训练至少 3 个 baseline 模型。
  • 对分类或回归任务输出合适指标。
  • 生成 reports/week05_baseline_results.md
  • 写下初步误差分析,而不是只贴一个分数。

你最终应理解:

ompprompt
baseline 不是“低级模型”,而是以后所有改进的比较基准。
没有 baseline,就不知道复杂模型到底有没有带来价值。

2. 前置条件

你应该已经完成 Week 01-04:

  • 会在终端进入项目目录。
  • 会用 uv 创建虚拟环境。
  • 会用 VS Code 打开项目。
  • 会用 pandas 读取 CSV。
  • 会做基础 EDA:缺失值、分布、相关性、简单图表。
  • 会用 Git 保存阶段性成果。

检查工具:

ompfish
python --version
uv --version
git --version
code --version

如果 python 指向系统 Python,不要急着安装包;先进入项目目录并激活 .venv

3. 建议项目位置

如果你前几周已经有学习仓库,可以继续使用:

ompfish
cd ~/Code/python-learning

为 Week 05 单独建一个小项目:

ompfish
mkdir -p week05-sklearn-baselines
cd week05-sklearn-baselines
pwd

解释:

命令 作用
mkdir -p 创建目录;目录已存在也不报错
cd 进入项目目录
pwd 确认当前路径,避免在错误目录里初始化项目

建议输出类似:

ompprompt
/home/miku/Code/python-learning/week05-sklearn-baselines

4. 初始化 uv 项目

ompfish
uv init
uv venv
source .venv/bin/activate.fish

逐句解释:

命令 含义
uv init 生成 pyproject.tomlREADME.md 等项目文件
uv venv 在当前目录创建 .venv/ 虚拟环境
source .venv/bin/activate.fish fish shell 专用激活命令

激活后检查 Python 路径:

ompfish
command -v python
python --version

你希望看到路径里包含当前项目:

ompprompt
/home/miku/Code/python-learning/week05-sklearn-baselines/.venv/bin/python

如果看到 /usr/bin/python,说明环境没有激活成功。

5. 安装依赖

Week 05 需要 pandas、numpy、scikit-learn,以及一个简单制表工具。

ompfish
uv add pandas numpy scikit-learn tabulate

检查:

ompfish
python -c "import pandas, sklearn; print('pandas', pandas.__version__); print('sklearn', sklearn.__version__)"

这里用 python -c 运行一行小检查,不是写项目代码。真正的训练逻辑要放进 src/train_baselines.py

6. 建立文件布局

创建目录:

ompfish
mkdir -p data/raw data/processed src reports

建议布局:

ompprompt
week05-sklearn-baselines/
├── .venv/                    # 当前项目虚拟环境,不提交到 Git
├── data/
│   ├── raw/                  # 原始数据,只读保存
│   └── processed/            # 处理后的数据,可重复生成
├── reports/
│   └── week05_baseline_results.md
├── src/
│   └── train_baselines.py
├── pyproject.toml
├── uv.lock
└── README.md

注意:data/raw/ 里的原始数据不要反复手工修改。你可以复制一份到 data/processed/ 做清洗输出,但原始文件最好保留。

7. 选择一个练习数据集

为了让脚本可直接运行,本周可以先用 sklearn 自带的 breast cancer 分类数据集。它适合练习:

  • 二分类。
  • 数值特征。
  • 样本量不大,运行很快。
  • 不需要联网下载。
  • 可直接比较 Logistic Regression、KNN、Decision Tree、Random Forest。

真实项目中,你可以换成 Kaggle、UCI 或课程数据。baseline 结构保持不变。

8. 理解 train / validation / test

不要只划分 train/test。Week 05 应开始养成三份数据意识:

ompprompt
train      用来拟合模型参数
validation 用来比较模型、调参数、做选择
test       最后只使用一次,用来估计最终泛化表现

常见错误是:

ompprompt
在 test 上反复试模型,哪个分数高就选哪个。

这会让 test 变成新的 validation,最终分数偏乐观。正确流程是:

ompprompt
1. train 上训练多个模型
2. validation 上比较模型
3. 选定一个模型
4. 只在最后对 test 评估一次

对于分类任务,划分时使用 stratify=y,保证每份数据里的类别比例接近整体比例。

9. 创建 baseline 脚本

在 VS Code 中打开项目:

ompfish
code .

创建文件:

ompprompt
src/train_baselines.py

把下面代码放进去:

omppython
from pathlib import Path

import pandas as pd from sklearn.datasets import load_breast_cancer from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, roc_auc_score from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.tree import DecisionTreeClassifier

RANDOM_STATE = 42 REPORT_PATH = Path(“reports/week05_baseline_results.md”)

def load_data(): dataset = load_breast_cancer(as_frame=True) X = dataset.data y = dataset.target target_names = list(dataset.target_names) return X, y, target_names

def split_data(X, y): X_train_valid, X_test, y_train_valid, y_test = train_test_split( X, y, test_size=0.2, random_state=RANDOM_STATE, stratify=y, ) X_train, X_valid, y_train, y_valid = train_test_split( X_train_valid, y_train_valid, test_size=0.25, random_state=RANDOM_STATE, stratify=y_train_valid, ) return X_train, X_valid, X_test, y_train, y_valid, y_test

def build_models(): return { “logistic_regression”: Pipeline( steps=[ (“scaler”, StandardScaler()), (“model”, LogisticRegression(max_iter=2000, random_state=RANDOM_STATE)), ] ), “knn”: Pipeline( steps=[ (“scaler”, StandardScaler()), (“model”, KNeighborsClassifier(n_neighbors=5)), ] ), “decision_tree”: DecisionTreeClassifier(max_depth=4, random_state=RANDOM_STATE), “random_forest”: RandomForestClassifier(n_estimators=200, random_state=RANDOM_STATE), }

def evaluate_classifier(model, X, y): predictions = model.predict(X) probabilities = model.predict_proba(X)[:, 1] return { “accuracy”: accuracy_score(y, predictions), “precision”: precision_score(y, predictions), “recall”: recall_score(y, predictions), “f1”: f1_score(y, predictions), “roc_auc”: roc_auc_score(y, probabilities), }

def format_metrics(metrics): return {name: round(value, 4) for name, value in metrics.items()}

def make_report(results, split_sizes, target_names, best_model_name, test_metrics): rows = [] for result in results: rows.append( ”| {model} | {accuracy:.4f} | {precision:.4f} | {recall:.4f} | {f1:.4f} | {roc_auc:.4f} |“.format( **result ) )

plain report = f"""# Week 05 Baseline Results

Dataset

  • Source: sklearn breast cancer dataset
  • Task: binary classification
  • Target names:

Split

SplitRows
Train{split_sizes[‘train’]}
Validation{split_sizes[‘valid’]}
Test{split_sizes[‘test’]}

The validation set is used for model comparison. The test set is used once after selecting the best validation model.

Validation Metrics

ModelAccuracyPrecisionRecallF1ROC AUC
{chr(10).join(rows)}

Selected Model

  • Best validation model by F1:

Final Test Metrics

MetricValue
Accuracy{test_metrics[‘accuracy’]:.4f}
Precision{test_metrics[‘precision’]:.4f}
Recall{test_metrics[‘recall’]:.4f}
F1{test_metrics[‘f1’]:.4f}
ROC AUC{test_metrics[‘roc_auc’]:.4f}

Initial Error Analysis

Write 5-8 bullet points after reading the confusion matrix or wrong predictions. Suggested questions:

  • Which class is easier to miss?
  • Is high accuracy hiding weak recall?
  • Does the model make more false positives or false negatives?
  • Which metric should matter more for this problem?
  • Is the current split stable enough, or do we need cross validation in Week 06? """ return report

def main(): X, y, target_names = load_data() X_train, X_valid, X_test, y_train, y_valid, y_test = split_data(X, y) models = build_models()

plain results = [] fitted_models = {} for name, model in models.items(): model.fit(X_train, y_train) fitted_models[name] = model metrics = evaluate_classifier(model, X_valid, y_valid) results.append({“model”: name, **metrics})

plain results = sorted(results, key=lambda item: item[“f1”], reverse=True) best_model_name = results[0][“model”] best_model = fitted_models[best_model_name] test_metrics = evaluate_classifier(best_model, X_test, y_test)

plain split_sizes = { “train”: len(X_train), “valid”: len(X_valid), “test”: len(X_test), } report = make_report(results, split_sizes, target_names, best_model_name, test_metrics)

REPORT_PATH.parent.mkdir(parents=True, exist_ok=True)
REPORT_PATH.write_text(report, encoding="utf-8")

print("Validation results:")
print(pd.DataFrame(results).round(4).to_string(index=False))
print()
print("Best model:", best_model_name)
print("Test metrics:", format_metrics(test_metrics))
print("Report written to", REPORT_PATH)

if name == “main”: main()

10. 运行 baseline

确认环境已激活:

ompfish
source .venv/bin/activate.fish
command -v python

运行脚本:

ompfish
python src/train_baselines.py

你应该看到类似输出:

ompprompt
Validation results:
              model  accuracy  precision  recall     f1  roc_auc
      random_forest    0.9649     0.9714  0.9714 0.9714   0.9921
logistic_regression    0.9561     0.9706  0.9429 0.9565   0.9954
                knn    0.9474     0.9701  0.9286 0.9489   0.9868
      decision_tree    0.9298     0.9444  0.9444 0.9444   0.9355

Best model: random_forest Test metrics: {‘accuracy’: 0.9561, ‘precision’: 0.9459, ‘recall’: 0.9859, ‘f1’: 0.9655, ‘roc_auc’: 0.9898} Report written to reports/week05_baseline_results.md

具体数值可能因 sklearn 版本略有差异。重要的是:脚本能跑通,报告能生成,模型比较逻辑清楚。

11. 指标怎么读

11.1 Accuracy

ompprompt
accuracy = 预测正确的样本数 / 总样本数

优点:直观。缺点:类别不平衡时可能误导。例如 95% 都是负类,模型全预测负类也有 95% accuracy。

11.2 Precision

ompprompt
precision = 预测为正类且真的为正类 / 所有预测为正类

问题意识:模型说“是”的时候,有多大概率真的“是”?

11.3 Recall

ompprompt
recall = 预测为正类且真的为正类 / 所有真实正类

问题意识:真实正类里,模型抓住了多少?如果漏诊代价高,recall 很重要。

11.4 F1

ompprompt
F1 = precision 和 recall 的调和平均

当你同时关心 precision 与 recall,可用 F1 做第一选择指标。

11.5 ROC AUC

ROC AUC 衡量模型把正类排在负类前面的能力。它不只看一个阈值,而是看不同阈值下的整体区分能力。

12. 为什么 Logistic Regression 和 KNN 要标准化

StandardScaler() 会把每列特征变成大致均值 0、标准差 1。

需要标准化的模型:

  • Logistic Regression:优化过程受特征尺度影响。
  • KNN:距离计算会被大尺度特征支配。
  • SVM:同样强烈依赖尺度。

不一定需要标准化的模型:

  • Decision Tree。
  • Random Forest。
  • Gradient Boosting Tree。

所以脚本里只给 Logistic Regression 和 KNN 放进 Pipeline

13. 怎样替换成自己的 CSV

假设你的数据是:

ompprompt
data/raw/my_dataset.csv

并且目标列叫 target。可以把 load_data() 改成:

omppython
def load_data():
    data = pd.read_csv("data/raw/my_dataset.csv")
    X = data.drop(columns=["target"])
    y = data["target"]
    target_names = sorted(y.astype(str).unique().tolist())
    return X, y, target_names

如果有类别特征,暂时不要手工把字符串硬改成数字。Week 06 会学习 ColumnTransformer 和防泄漏 pipeline。Week 05 可以先选择全数值数据练手。

14. 报告怎么写

打开报告:

ompfish
code reports/week05_baseline_results.md

Initial Error Analysis 下补充你的观察。不要只写:

ompprompt
Random forest is best.

应写成类似:

ompprompt
- Random forest 在 validation F1 上最高,但 Logistic Regression 的 ROC AUC 也很高,说明线性边界已经有不错区分能力。
- 如果任务重视漏检,recall 比 accuracy 更重要。当前最佳模型 test recall 高于 precision,错误更偏向 false positives。
- Validation 集只有一份,结果可能受随机划分影响。Week 06 需要用交叉验证确认排序是否稳定。

15. Git 保存成果

先看状态:

ompfish
git status --short

如果这是独立学习项目,还没有 Git 仓库:

ompfish
git init

创建 .gitignore 可以用 VS Code,新建文件 .gitignore,写入:

ompprompt
.venv/
__pycache__/
.ipynb_checkpoints/
data/raw/*.zip

提交:

ompfish
git add pyproject.toml uv.lock README.md src reports .gitignore
git commit -m "Add week05 sklearn baseline training"

如果数据文件来自公开小数据,也可以提交;如果数据很大或有隐私,不要直接提交。

16. 本周练习

  1. RANDOM_STATE 改成 7、21、100,观察 validation 排名是否稳定。
  2. DecisionTreeClassifier(max_depth=4) 改成 max_depth=2max_depth=None,比较过拟合迹象。
  3. 把 KNN 的 n_neighbors 改成 3、7、11,观察 F1 变化。
  4. 在报告里加一节 Metric Choice,说明为什么本任务不能只看 accuracy。
  5. 换一个 sklearn 自带数据集或你自己的 CSV,复用同一训练流程。

17. 验收检查

在项目根目录执行:

ompfish
source .venv/bin/activate.fish
python src/train_baselines.py
test -f reports/week05_baseline_results.md; and echo "report exists"

然后人工检查:

  • src/train_baselines.py 存在。
  • reports/week05_baseline_results.md 存在。
  • 报告里有模型名称、指标、划分方式、结果表格、初步误差分析。
  • 代码里有固定的 RANDOM_STATE
  • test set 没有参与模型选择,只在最后评估一次。
  • Git 至少有一次提交。

如果这些都满足,Week 05 合格。

18. 常见错误

错误 1:fish 激活命令写错

现象:

ompprompt
Unsupported use of '='

原因:在 fish 里用了 Bash / Zsh 的激活脚本。

修复:

ompfish
source .venv/bin/activate.fish

错误 2:ModuleNotFoundError: No module named 'sklearn'

原因通常是没有激活当前项目虚拟环境,或依赖安装在别的环境。

检查:

ompfish
command -v python
uv pip list

修复:

ompfish
source .venv/bin/activate.fish
uv add scikit-learn

错误 3:忘记 stratify=y

分类任务如果类别不平衡,某一份 split 可能类别比例异常,导致分数不稳定。

修复:

omppython
train_test_split(X, y, stratify=y, random_state=RANDOM_STATE)

错误 4:在 test 上选模型

不要比较所有模型的 test 分数再选最高。这样 test 已经被你用来调参了。

正确做法:validation 选模型,test 做最终确认。

错误 5:把 baseline 当成最终结论

Week 05 的 baseline 只是起点。它告诉你:如果后续特征工程、交叉验证、模型选择没有超过 baseline,就说明复杂度没有带来收益。

19. 下一步

进入 Week 06:交叉验证与模型选择。你会把本周的一次固定划分,升级为 K-fold cross validation、GridSearchCV 和防止数据泄漏的 Pipeline。

plain

喜欢的话,留下你的评论吧~