{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Phase 5: Expert Aggressive — Toxic-BERT + Hybrid\n", "\n", "**Expert Adaptation** strategy to break the F1 plateau:\n", "\n", "| Change | Setting |\n", "|--------|--------|\n", "| Base model | `unitary/toxic-bert` (head-only fine-tune) |\n", "| LR bottleneck | TF-IDF `max_features=250` |\n", "| Threshold | Val-set search maximizing **F1-toxic** |\n", "| Hybrid weights | **0.7** Toxic-BERT + **0.3** LR |\n", "| Augmentation | EN→**DE**→EN back-translation (higher diversity) |\n", "\n", "Run from repo root (long-running — augmentation + fine-tune):\n", "\n", "```bash\n", "uv sync --extra hf --extra train\n", "uv run python -m src.pipeline.run_expert_pipeline\n", "```\n", "\n", "Or execute the pipeline cell below inside this notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 0. Setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Config: expert_training.yaml\n", "Pivot lang: de\n", "Model: unitary/toxic-bert (head_only)\n" ] } ], "source": [ "import json\n", "import sys\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", "if str(PROJECT_ROOT) not in sys.path:\n", " sys.path.insert(0, str(PROJECT_ROOT))\n", "\n", "cfg_path = PROJECT_ROOT / \"configs\" / \"expert_training.yaml\"\n", "cfg = yaml.safe_load(open(cfg_path))\n", "reports_dir = PROJECT_ROOT / \"reports\" / \"expert\"\n", "print(f\"Config: {cfg_path.name}\")\n", "print(f\"Pivot lang: {cfg['augmentation']['pivot_lang']}\")\n", "print(f\"Model: {cfg['transformer']['model_id']} ({cfg['transformer']['freeze_mode']})\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Run Phase 5 pipeline" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/miraekang/proyectos/ai-nlp/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", " from .autonotebook import tqdm as notebook_tqdm\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "2026-05-24 19:39:47 | INFO | src.pipeline.run_expert_pipeline | ============================================================\n", "2026-05-24 19:39:47 | INFO | src.pipeline.run_expert_pipeline | EXPERT PIPELINE (Phase 5) — run=20260524_193947\n", "2026-05-24 19:39:47 | INFO | src.pipeline.run_expert_pipeline | ============================================================\n", "2026-05-24 19:39:47 | INFO | src.data.loader | Cargando dataset: /Users/miraekang/proyectos/ai-nlp/data/raw/youtoxic_english_1000.csv\n", "2026-05-24 19:39:47 | INFO | src.data.loader | Shape: (1000, 15)\n", "2026-05-24 19:39:47 | INFO | src.data.loader | Columnas validadas ✅\n", "2026-05-24 19:39:47 | WARNING | src.data.loader | 3 duplicados eliminados\n", "2026-05-24 19:39:47 | INFO | src.data.loader | Toxicos: 459 (46.0%)\n", "2026-05-24 19:39:47 | INFO | src.pipeline.run_expert_pipeline | Augmentation EN→DE→EN (toxic only)\n", "2026-05-24 19:39:47 | INFO | src.features.augmentation | Back-translation: 312 toxic samples\n", "2026-05-24 19:42:02 | INFO | src.features.augmentation | Back-translation produced 295 samples\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.\n", "Loading weights: 100%|██████████| 103/103 [00:00<00:00, 9092.34it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "2026-05-24 19:42:08 | INFO | src.features.augmentation | Dedup: kept 209/295 (dropped 86 with cosine > 0.95)\n", "2026-05-24 19:42:08 | INFO | src.features.augmentation | Train size after augmentation: 886 (+209)\n", "2026-05-24 19:42:08 | INFO | src.pipeline.run_expert_pipeline | LR-TFIDF (max_features=250) + gap search\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | Training stable LR — C=0.05\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | LR gap search — C=0.05 max_features=250 min_df=3 train_f1=0.7703 test_f1=0.6563 gap=0.1139\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | Training stable LR — C=0.03\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | LR gap search — C=0.03 max_features=250 min_df=5 train_f1=0.7572 test_f1=0.6563 gap=0.1008\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | Training stable LR — C=0.02\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | LR gap search — C=0.02 max_features=250 min_df=5 train_f1=0.7572 test_f1=0.6563 gap=0.1008\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | Training stable LR — C=0.01\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | LR gap search — C=0.01 max_features=250 min_df=8 train_f1=0.7570 test_f1=0.6563 gap=0.1006\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | Training stable LR — C=0.005\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | LR gap search — C=0.005 max_features=250 min_df=10 train_f1=0.7511 test_f1=0.6509 gap=0.1003\n", "2026-05-24 19:42:08 | WARNING | src.models.hybrid_ensemble | LR gap still 0.1003 after grid search; using best gap C=0.005\n", "2026-05-24 19:42:08 | INFO | src.models.hybrid_ensemble | Stable LR saved: /Users/miraekang/proyectos/ai-nlp/models/expert_lr_tfidf.joblib\n", "2026-05-24 19:42:08 | INFO | src.pipeline.run_expert_pipeline | Toxic-BERT — head-only fine-tune + val threshold tuning\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Map: 100%|██████████| 886/886 [00:00<00:00, 22618.23 examples/s]\n", "Map: 100%|██████████| 120/120 [00:00<00:00, 17820.93 examples/s]\n", "Map: 100%|██████████| 200/200 [00:00<00:00, 23323.72 examples/s]\n", "[transformers] You passed `num_labels=2` which is incompatible to the `id2label` map of length `6`.\n", "Loading weights: 100%|██████████| 201/201 [00:00<00:00, 8948.39it/s]\n", "[transformers] \u001b[1mBertForSequenceClassification LOAD REPORT\u001b[0m from: unitary/toxic-bert\n", "Key | Status | \n", "------------------+----------+---------------------------------------------------------------------------------------\n", "classifier.weight | MISMATCH | Reinit due to size mismatch - ckpt: torch.Size([6, 768]) vs model:torch.Size([2, 768])\n", "classifier.bias | MISMATCH | Reinit due to size mismatch - ckpt: torch.Size([6]) vs model:torch.Size([2]) \n", "\n", "Notes:\n", "- MISMATCH:\tckpt weights were loaded, but they did not match the original empty weight shapes.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "2026-05-24 19:42:09 | INFO | src.models.transformer_trainer | Head-only freeze — trainable 592,130/109,483,778 (0.54%)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[transformers] warmup_ratio is deprecated and will be removed in v5.2. Use `warmup_steps` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "2026-05-24 19:42:10 | INFO | src.models.transformer_trainer | Training unitary/toxic-bert (head_only freeze)...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/Users/miraekang/proyectos/ai-nlp/.venv/lib/python3.12/site-packages/torch/utils/data/dataloader.py:752: UserWarning: 'pin_memory' argument is set as true but not supported on MPS now, device pinned memory won't be used.\n", " super().__init__(loader)\n" ] }, { "data": { "text/html": [ "\n", "
| Epoch | \n", "Training Loss | \n", "Validation Loss | \n", "F1 Toxic | \n", "F1 Weighted | \n", "Precision | \n", "Recall | \n", "Roc Auc | \n", "
|---|---|---|---|---|---|---|---|
| 1 | \n", "0.554757 | \n", "0.559579 | \n", "0.690909 | \n", "0.716667 | \n", "0.690909 | \n", "0.690909 | \n", "0.814545 | \n", "
| 2 | \n", "0.507270 | \n", "0.560950 | \n", "0.690909 | \n", "0.716667 | \n", "0.690909 | \n", "0.690909 | \n", "0.810350 | \n", "
| 3 | \n", "0.558299 | \n", "0.558404 | \n", "0.673077 | \n", "0.714744 | \n", "0.714286 | \n", "0.636364 | \n", "0.812028 | \n", "
| 4 | \n", "0.503684 | \n", "0.564421 | \n", "0.685185 | \n", "0.716190 | \n", "0.698113 | \n", "0.672727 | \n", "0.812308 | \n", "
"
],
"text/plain": [
"\n",
" \n",
"
\n",
"\n",
" \n",
" \n",
" \n",
" \n",
" model \n",
" f1_toxic_test \n",
" f1_toxic_train \n",
" toxic_gap_pp \n",
" gap_ok_<5pp \n",
" f1_target_>0.75 \n",
" threshold \n",
" roc_auc \n",
" \n",
" \n",
" 0 \n",
" Toxic-BERT \n",
" 0.7489 \n",
" 0.7907 \n",
" 4.18 \n",
" True \n",
" False \n",
" 0.33 \n",
" 0.8768 \n",
" \n",
" \n",
" 1 \n",
" LR-TFIDF-250 \n",
" 0.6301 \n",
" 0.6309 \n",
" 0.08 \n",
" True \n",
" False \n",
" 0.05 \n",
" 0.7056 \n",
" \n",
" \n",
" \n",
"2 \n",
" Hybrid 0.7/0.3 \n",
" 0.7489 \n",
" 0.7917 \n",
" 4.28 \n",
" True \n",
" False \n",
" 0.38 \n",
" 0.8773 \n",
"