{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Stable Production — LR-TFIDF + 5-Fold CV\n", "\n", "Production settings from `configs/stable_training.yaml`:\n", "- **TF-IDF:** `max_features=800`, bigrams, `sublinear_tf`\n", "- **LR:** `C=0.05` with grid search until train–test gap < 5 pp\n", "- **Augmentation:** toxic-only back-translation (EN→ES→EN) + cosine dedup\n", "- **Evaluation:** stratified 5-fold CV on the train+val pool\n", "\n", "Run the full pipeline from repo root:\n", "```bash\n", "uv sync --extra hf --extra train\n", "uv run python -m src.pipeline.run_stable_pipeline\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 0. Setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loaded: stable_run_20260524_190417.json (run_id=20260524_190417)\n" ] } ], "source": [ "import json\n", "from pathlib import Path\n", "\n", "import pandas as pd\n", "import yaml\n", "\n", "PROJECT_ROOT = Path.cwd().resolve()\n", "if not (PROJECT_ROOT / \"configs\").exists() and (PROJECT_ROOT.parent / \"configs\").exists():\n", " PROJECT_ROOT = PROJECT_ROOT.parent\n", "\n", "cfg = yaml.safe_load(open(PROJECT_ROOT / \"configs\" / \"stable_training.yaml\"))\n", "reports_dir = PROJECT_ROOT / \"reports\" / \"stable\"\n", "runs = sorted(reports_dir.glob(\"stable_run_*.json\"))\n", "assert runs, \"No stable_run_*.json — run the pipeline first\"\n", "latest = runs[-1]\n", "metrics = json.loads(latest.read_text())\n", "run_id = metrics[\"run_id\"]\n", "print(f\"Loaded: {latest.name} (run_id={run_id})\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Augmentation summary" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "enabled True\n", "strategy back_translation\n", "train_size_before 677\n", "train_size_after 877\n", "added_samples 200\n", "dtype: object" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "aug = metrics.get(\"augmentation\", {})\n", "pd.Series(aug)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. LR gap search (holdout test)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
metricvalue
0F1 weighted (test)0.6546
1F1 weighted (train, orig)0.7721
2Train–test gap (pp)11.74
3ROC-AUC (test)0.7312
4Chosen C0.005
5max_features800
6Gap OK (<5pp)False
\n", "
" ], "text/plain": [ " metric value\n", "0 F1 weighted (test) 0.6546\n", "1 F1 weighted (train, orig) 0.7721\n", "2 Train–test gap (pp) 11.74\n", "3 ROC-AUC (test) 0.7312\n", "4 Chosen C 0.005\n", "5 max_features 800\n", "6 Gap OK (<5pp) False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lr = metrics[\"logistic_regression\"]\n", "gap_search = metrics.get(\"lr_gap_search\", {})\n", "\n", "rows = [\n", " {\"metric\": \"F1 weighted (test)\", \"value\": lr[\"f1_weighted\"]},\n", " {\"metric\": \"F1 weighted (train, orig)\", \"value\": lr[\"f1_train\"]},\n", " {\"metric\": \"Train–test gap (pp)\", \"value\": lr[\"train_test_gap_pp\"]},\n", " {\"metric\": \"ROC-AUC (test)\", \"value\": lr[\"roc_auc\"]},\n", " {\"metric\": \"Chosen C\", \"value\": lr.get(\"C\", gap_search.get(\"C\"))},\n", " {\"metric\": \"max_features\", \"value\": lr.get(\"max_features\", gap_search.get(\"max_features\"))},\n", " {\"metric\": \"Gap OK (<5pp)\", \"value\": lr.get(\"gap_ok\", gap_search.get(\"gap_ok\"))},\n", "]\n", "display(pd.DataFrame(rows))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Stratified 5-fold CV (LR)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "F1: 0.6636 ± 0.0223 | fold gap max: 14.66 pp | stable: False\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
foldf1_weightedtrain_val_gap_pproc_auc
000.68068511.050.7239
110.65043912.590.7341
220.6972927.120.7277
330.65393013.110.6728
440.63553914.660.6856
\n", "
" ], "text/plain": [ " fold f1_weighted train_val_gap_pp roc_auc\n", "0 0 0.680685 11.05 0.7239\n", "1 1 0.650439 12.59 0.7341\n", "2 2 0.697292 7.12 0.7277\n", "3 3 0.653930 13.11 0.6728\n", "4 4 0.635539 14.66 0.6856" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cv = metrics[\"cv_logistic_regression\"]\n", "print(\n", " f\"F1: {cv['f1_mean']} ± {cv['f1_std']} | \"\n", " f\"fold gap max: {cv['gap_max']*100:.2f} pp | \"\n", " f\"stable: {cv['stable_across_folds']}\"\n", ")\n", "fold_df = pd.DataFrame(cv[\"folds\"])\n", "fold_df[[\"fold\", \"f1_weighted\", \"train_val_gap_pp\", \"roc_auc\"]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "This notebook summarizes **LR-TFIDF** from the latest stable production run.\n", "Check `reports/stable/integrated_report_{run_id}.md` for the combined LR + DistilBERT + ensemble report.\n", "The 5-fold CV **F1 std** measures stability across data segments; the **train–val gap** per fold tracks overfitting within each split.\n", "Target rubric: |train − test| < 5 pp and test F1 > 0.80 — tune `logistic_regression.gap_search.param_grid` if gaps remain high." ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.7" } }, "nbformat": 4, "nbformat_minor": 5 }