refactor: migrate from black+isort+flake8 to unified ruff toolchain

This commit is contained in:
Piero Molino
2026-05-06 21:56:19 -07:00
committed by GitHub
parent 868e40b178
commit f723051649
215 changed files with 3041 additions and 2957 deletions
+7 -18
View File
@@ -33,25 +33,14 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: mixed-line-ending
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
hooks:
- id: pyupgrade
args: [--py312-plus]
- repo: https://github.com/PyCQA/isort
rev: 8.0.1
hooks:
- id: isort
name: Format imports
- repo: https://github.com/pycqa/flake8
rev: 7.3.0
hooks:
- id: flake8
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 26.3.1
hooks:
- id: black
name: Format code
- id: ruff
name: Lint and fix (ruff)
args: [--fix]
- id: ruff-format
name: Format code (ruff)
- repo: https://github.com/asottile/blacken-docs
rev: 1.20.0
hooks:
+11 -8
View File
@@ -63,13 +63,13 @@
"# In Colab: Secrets panel (key icon) -> add HF_TOKEN, then reference it here.\n",
"try:\n",
" from google.colab import userdata\n",
"\n",
" os.environ[\"HUGGING_FACE_HUB_TOKEN\"] = userdata.get(\"HF_TOKEN\")\n",
"except Exception:\n",
" pass # Running locally — export HUGGING_FACE_HUB_TOKEN in your shell instead\n",
"\n",
"assert os.environ.get(\"HUGGING_FACE_HUB_TOKEN\"), (\n",
" \"HUGGING_FACE_HUB_TOKEN is not set. \"\n",
" \"Add it to Colab Secrets or export it in your shell.\"\n",
" \"HUGGING_FACE_HUB_TOKEN is not set. Add it to Colab Secrets or export it in your shell.\"\n",
")"
]
},
@@ -80,11 +80,11 @@
"outputs": [],
"source": [
"import subprocess\n",
"import sys\n",
"\n",
"# Verify GPU availability\n",
"result = subprocess.run([\"nvidia-smi\", \"--query-gpu=name,memory.total\", \"--format=csv,noheader\"],\n",
" capture_output=True, text=True)\n",
"result = subprocess.run(\n",
" [\"nvidia-smi\", \"--query-gpu=name,memory.total\", \"--format=csv,noheader\"], capture_output=True, text=True\n",
")\n",
"if result.returncode == 0:\n",
" print(\"GPU(s) detected:\")\n",
" print(result.stdout.strip())\n",
@@ -120,6 +120,7 @@
"outputs": [],
"source": [
"import re\n",
"\n",
"import pandas as pd\n",
"from datasets import load_dataset\n",
"\n",
@@ -217,7 +218,9 @@
"outputs": [],
"source": [
"import logging\n",
"\n",
"import yaml\n",
"\n",
"from ludwig.api import LudwigModel\n",
"\n",
"dpo_config = yaml.safe_load(\"\"\"\n",
@@ -311,11 +314,11 @@
"dpo_preds, _ = dpo_model.predict(dataset=eval_df)\n",
"\n",
"for i, prompt in enumerate(eval_prompts):\n",
" print(f\"\\n{'='*60}\")\n",
" print(f\"\\n{'=' * 60}\")\n",
" print(f\"PROMPT: {prompt}\")\n",
" print(f\"\\n--- Base model ---\")\n",
" print(\"\\n--- Base model ---\")\n",
" print(base_preds.iloc[i][\"chosen_predictions\"])\n",
" print(f\"\\n--- DPO-aligned model ---\")\n",
" print(\"\\n--- DPO-aligned model ---\")\n",
" print(dpo_preds.iloc[i][\"chosen_predictions\"])"
]
},
@@ -1,24 +1,8 @@
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
},
"accelerator": "GPU",
"colab": {
"provenance": []
}
},
"cells": [
{
"cell_type": "markdown",
"id": "7fb27b941602401d91542211134fc71a",
"metadata": {},
"source": [
"# Anomaly Detection with Deep SVDD, SAD, and DROCC\n",
@@ -47,6 +31,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "acae54e37e7d407bbb7b55eff062a284",
"metadata": {},
"outputs": [],
"source": [
@@ -55,6 +40,7 @@
},
{
"cell_type": "markdown",
"id": "9a63283cbaf04dbcab1f6479b197f3a8",
"metadata": {},
"source": [
"## Generate synthetic sensor data"
@@ -63,6 +49,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8dd0d8092fe74a7c96281538738b07e2",
"metadata": {},
"outputs": [],
"source": [
@@ -75,22 +62,26 @@
"N_ANOMALY = 200\n",
"\n",
"# Normal samples: Gaussian near origin\n",
"normal_df = pd.DataFrame({\n",
" 'sensor_a': RNG.normal(0.0, 1.0, N_NORMAL),\n",
" 'sensor_b': RNG.normal(0.0, 1.0, N_NORMAL),\n",
" 'sensor_c': RNG.normal(0.0, 1.0, N_NORMAL),\n",
" 'timestamp_hour': RNG.integers(0, 24, N_NORMAL).astype(float),\n",
" 'anomaly': 0.0,\n",
"})\n",
"normal_df = pd.DataFrame(\n",
" {\n",
" \"sensor_a\": RNG.normal(0.0, 1.0, N_NORMAL),\n",
" \"sensor_b\": RNG.normal(0.0, 1.0, N_NORMAL),\n",
" \"sensor_c\": RNG.normal(0.0, 1.0, N_NORMAL),\n",
" \"timestamp_hour\": RNG.integers(0, 24, N_NORMAL).astype(float),\n",
" \"anomaly\": 0.0,\n",
" }\n",
")\n",
"\n",
"# Anomalous samples: large offset from origin\n",
"anomaly_df = pd.DataFrame({\n",
" 'sensor_a': RNG.normal(6.0, 1.0, N_ANOMALY),\n",
" 'sensor_b': RNG.normal(6.0, 1.0, N_ANOMALY),\n",
" 'sensor_c': RNG.normal(6.0, 1.0, N_ANOMALY),\n",
" 'timestamp_hour': RNG.integers(0, 24, N_ANOMALY).astype(float),\n",
" 'anomaly': 1.0,\n",
"})\n",
"anomaly_df = pd.DataFrame(\n",
" {\n",
" \"sensor_a\": RNG.normal(6.0, 1.0, N_ANOMALY),\n",
" \"sensor_b\": RNG.normal(6.0, 1.0, N_ANOMALY),\n",
" \"sensor_c\": RNG.normal(6.0, 1.0, N_ANOMALY),\n",
" \"timestamp_hour\": RNG.integers(0, 24, N_ANOMALY).astype(float),\n",
" \"anomaly\": 1.0,\n",
" }\n",
")\n",
"\n",
"# ---------------------------------------------------------------\n",
"# Train split: ONLY normal samples (anomaly detection is unsupervised).\n",
@@ -101,38 +92,36 @@
"normal_idx = normal_df.index.tolist()\n",
"RNG.shuffle(normal_idx)\n",
"n_train = int(0.7 * len(normal_idx))\n",
"n_val = int(0.15 * len(normal_idx))\n",
"n_val = int(0.15 * len(normal_idx))\n",
"\n",
"normal_df['split'] = 2 # test by default\n",
"normal_df.loc[normal_idx[:n_train], 'split'] = 0\n",
"normal_df.loc[normal_idx[n_train:n_train + n_val], 'split'] = 1\n",
"normal_df[\"split\"] = 2 # test by default\n",
"normal_df.loc[normal_idx[:n_train], \"split\"] = 0\n",
"normal_df.loc[normal_idx[n_train : n_train + n_val], \"split\"] = 1\n",
"\n",
"anom_idx = anomaly_df.index.tolist()\n",
"RNG.shuffle(anom_idx)\n",
"n_val_anom = len(anom_idx) // 2\n",
"anomaly_df['split'] = 2\n",
"anomaly_df.loc[anom_idx[:n_val_anom], 'split'] = 1\n",
"anomaly_df[\"split\"] = 2\n",
"anomaly_df.loc[anom_idx[:n_val_anom], \"split\"] = 1\n",
"\n",
"# Training CSV: only normal rows\n",
"train_df = normal_df[normal_df['split'] == 0].copy()\n",
"train_df = normal_df[normal_df[\"split\"] == 0].copy()\n",
"\n",
"# Test CSV: validation + test rows (normal and anomalous), used for scoring\n",
"val_test_df = pd.concat(\n",
" [normal_df[normal_df['split'] != 0], anomaly_df],\n",
" ignore_index=True\n",
")\n",
"val_test_df = pd.concat([normal_df[normal_df[\"split\"] != 0], anomaly_df], ignore_index=True)\n",
"\n",
"train_df.to_csv('/tmp/sensors_train.csv', index=False)\n",
"val_test_df.to_csv('/tmp/sensors_test.csv', index=False)\n",
"train_df.to_csv(\"/tmp/sensors_train.csv\", index=False)\n",
"val_test_df.to_csv(\"/tmp/sensors_test.csv\", index=False)\n",
"\n",
"print(f'Train samples : {len(train_df)} (all normal)')\n",
"print(f'Test samples : {len(val_test_df)}')\n",
"print(f' Normal : {(val_test_df[\"anomaly\"] == 0).sum()}')\n",
"print(f' Anomalous : {(val_test_df[\"anomaly\"] == 1).sum()}')"
"print(f\"Train samples : {len(train_df)} (all normal)\")\n",
"print(f\"Test samples : {len(val_test_df)}\")\n",
"print(f\" Normal : {(val_test_df['anomaly'] == 0).sum()}\")\n",
"print(f\" Anomalous : {(val_test_df['anomaly'] == 1).sum()}\")"
]
},
{
"cell_type": "markdown",
"id": "72eea5119410473aa328ad9291626812",
"metadata": {},
"source": [
"## Train: Deep SVDD\n",
@@ -148,10 +137,12 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8edb47106e1a46a883d545849b8ab81b",
"metadata": {},
"outputs": [],
"source": [
"import yaml\n",
"\n",
"from ludwig.api import LudwigModel\n",
"\n",
"config_svdd_str = \"\"\"\n",
@@ -198,11 +189,12 @@
"model_svdd = LudwigModel(config_svdd, logging_level=30)\n",
"train_stats_svdd, _, _ = model_svdd.train(dataset=train_df)\n",
"\n",
"print('Deep SVDD training complete.')"
"print(\"Deep SVDD training complete.\")"
]
},
{
"cell_type": "markdown",
"id": "10185d26023b46108eb7d9f57d49d2b3",
"metadata": {},
"source": [
"## Evaluate: score distribution\n",
@@ -215,6 +207,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8763a12b2bbd4a93a75aff182afb95dc",
"metadata": {},
"outputs": [],
"source": [
@@ -223,22 +216,22 @@
"\n",
"preds_svdd, _ = model_svdd.predict(dataset=val_test_df)\n",
"\n",
"score_col = 'anomaly_anomaly_score_predictions'\n",
"score_col = \"anomaly_anomaly_score_predictions\"\n",
"scores_svdd = preds_svdd[score_col].values\n",
"true_labels = val_test_df['anomaly'].values\n",
"true_labels = val_test_df[\"anomaly\"].values\n",
"\n",
"auc_svdd = roc_auc_score(true_labels, scores_svdd)\n",
"print(f'Deep SVDD AUC-ROC: {auc_svdd:.4f}')\n",
"print(f\"Deep SVDD AUC-ROC: {auc_svdd:.4f}\")\n",
"\n",
"# --- Plot score distribution ---\n",
"fig, ax = plt.subplots(figsize=(8, 4))\n",
"\n",
"ax.hist(scores_svdd[true_labels == 0], bins=40, alpha=0.6, label='Normal', color='steelblue')\n",
"ax.hist(scores_svdd[true_labels == 1], bins=40, alpha=0.6, label='Anomalous', color='tomato')\n",
"ax.hist(scores_svdd[true_labels == 0], bins=40, alpha=0.6, label=\"Normal\", color=\"steelblue\")\n",
"ax.hist(scores_svdd[true_labels == 1], bins=40, alpha=0.6, label=\"Anomalous\", color=\"tomato\")\n",
"\n",
"ax.set_xlabel('Anomaly score ||z - c||^2')\n",
"ax.set_ylabel('Count')\n",
"ax.set_title(f'Deep SVDD — anomaly score distribution (AUC = {auc_svdd:.3f})')\n",
"ax.set_xlabel(\"Anomaly score ||z - c||^2\")\n",
"ax.set_ylabel(\"Count\")\n",
"ax.set_title(f\"Deep SVDD — anomaly score distribution (AUC = {auc_svdd:.3f})\")\n",
"ax.legend()\n",
"plt.tight_layout()\n",
"plt.show()"
@@ -246,6 +239,7 @@
},
{
"cell_type": "markdown",
"id": "7623eae2785240b9bd12b16a66d81610",
"metadata": {},
"source": [
"## Try Deep SAD (semi-supervised)\n",
@@ -265,6 +259,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "7cdc8c89c7104fffa095e18ddfef8986",
"metadata": {},
"outputs": [],
"source": [
@@ -312,12 +307,14 @@
"# Inject ~10% labeled anomalies into the training set\n",
"N_LABELED = max(1, int(0.1 * len(train_df)))\n",
"labeled_anom = anomaly_df.sample(n=N_LABELED, random_state=0).copy()\n",
"labeled_anom['split'] = 0\n",
"labeled_anom[\"split\"] = 0\n",
"sad_train_df = pd.concat([train_df, labeled_anom], ignore_index=True)\n",
"\n",
"print(f'Deep SAD training set: {len(sad_train_df)} rows '\n",
" f'({N_LABELED} labeled anomalies = '\n",
" f'{100 * N_LABELED / len(sad_train_df):.1f}%)')\n",
"print(\n",
" f\"Deep SAD training set: {len(sad_train_df)} rows \"\n",
" f\"({N_LABELED} labeled anomalies = \"\n",
" f\"{100 * N_LABELED / len(sad_train_df):.1f}%)\"\n",
")\n",
"\n",
"model_sad = LudwigModel(config_sad, logging_level=30)\n",
"train_stats_sad, _, _ = model_sad.train(dataset=sad_train_df)\n",
@@ -325,15 +322,15 @@
"preds_sad, _ = model_sad.predict(dataset=val_test_df)\n",
"scores_sad = preds_sad[score_col].values\n",
"auc_sad = roc_auc_score(true_labels, scores_sad)\n",
"print(f'Deep SAD AUC-ROC: {auc_sad:.4f}')\n",
"print(f\"Deep SAD AUC-ROC: {auc_sad:.4f}\")\n",
"\n",
"# --- Plot ---\n",
"fig, ax = plt.subplots(figsize=(8, 4))\n",
"ax.hist(scores_sad[true_labels == 0], bins=40, alpha=0.6, label='Normal', color='steelblue')\n",
"ax.hist(scores_sad[true_labels == 1], bins=40, alpha=0.6, label='Anomalous', color='tomato')\n",
"ax.set_xlabel('Anomaly score ||z - c||^2')\n",
"ax.set_ylabel('Count')\n",
"ax.set_title(f'Deep SAD — score distribution (AUC = {auc_sad:.3f})')\n",
"ax.hist(scores_sad[true_labels == 0], bins=40, alpha=0.6, label=\"Normal\", color=\"steelblue\")\n",
"ax.hist(scores_sad[true_labels == 1], bins=40, alpha=0.6, label=\"Anomalous\", color=\"tomato\")\n",
"ax.set_xlabel(\"Anomaly score ||z - c||^2\")\n",
"ax.set_ylabel(\"Count\")\n",
"ax.set_title(f\"Deep SAD — score distribution (AUC = {auc_sad:.3f})\")\n",
"ax.legend()\n",
"plt.tight_layout()\n",
"plt.show()"
@@ -341,6 +338,7 @@
},
{
"cell_type": "markdown",
"id": "b118ea5561624da68c537baed56e602f",
"metadata": {},
"source": [
"## Try DROCC\n",
@@ -358,6 +356,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "938c804e27f84196a10c8828c723f798",
"metadata": {},
"outputs": [],
"source": [
@@ -409,15 +408,15 @@
"preds_drocc, _ = model_drocc.predict(dataset=val_test_df)\n",
"scores_drocc = preds_drocc[score_col].values\n",
"auc_drocc = roc_auc_score(true_labels, scores_drocc)\n",
"print(f'DROCC AUC-ROC: {auc_drocc:.4f}')\n",
"print(f\"DROCC AUC-ROC: {auc_drocc:.4f}\")\n",
"\n",
"# --- Plot ---\n",
"fig, ax = plt.subplots(figsize=(8, 4))\n",
"ax.hist(scores_drocc[true_labels == 0], bins=40, alpha=0.6, label='Normal', color='steelblue')\n",
"ax.hist(scores_drocc[true_labels == 1], bins=40, alpha=0.6, label='Anomalous', color='tomato')\n",
"ax.set_xlabel('Anomaly score ||z - c||^2')\n",
"ax.set_ylabel('Count')\n",
"ax.set_title(f'DROCC — score distribution (AUC = {auc_drocc:.3f})')\n",
"ax.hist(scores_drocc[true_labels == 0], bins=40, alpha=0.6, label=\"Normal\", color=\"steelblue\")\n",
"ax.hist(scores_drocc[true_labels == 1], bins=40, alpha=0.6, label=\"Anomalous\", color=\"tomato\")\n",
"ax.set_xlabel(\"Anomaly score ||z - c||^2\")\n",
"ax.set_ylabel(\"Count\")\n",
"ax.set_title(f\"DROCC — score distribution (AUC = {auc_drocc:.3f})\")\n",
"ax.legend()\n",
"plt.tight_layout()\n",
"plt.show()"
@@ -425,6 +424,7 @@
},
{
"cell_type": "markdown",
"id": "504fb2a444614c0babb325280ed9130a",
"metadata": {},
"source": [
"## Summary\n",
@@ -438,29 +438,49 @@
{
"cell_type": "code",
"execution_count": null,
"id": "59bbdb311c014d738909a11f9e486628",
"metadata": {},
"outputs": [],
"source": [
"summary_rows = []\n",
"for name, scores, auc in [\n",
" ('Deep SVDD (unsupervised)', scores_svdd, auc_svdd),\n",
" ('Deep SAD (semi-supervised)', scores_sad, auc_sad),\n",
" ('DROCC (robust unsup.)', scores_drocc, auc_drocc),\n",
" (\"Deep SVDD (unsupervised)\", scores_svdd, auc_svdd),\n",
" (\"Deep SAD (semi-supervised)\", scores_sad, auc_sad),\n",
" (\"DROCC (robust unsup.)\", scores_drocc, auc_drocc),\n",
"]:\n",
" normal_s = scores[true_labels == 0]\n",
" anom_s = scores[true_labels == 1]\n",
" anom_s = scores[true_labels == 1]\n",
" sep = anom_s.mean() / (normal_s.mean() + 1e-9)\n",
" summary_rows.append({\n",
" 'Method': name,\n",
" 'AUC-ROC': round(auc, 4),\n",
" 'Mean normal score': round(float(normal_s.mean()), 4),\n",
" 'Mean anomaly score': round(float(anom_s.mean()), 4),\n",
" 'Separation ratio': round(float(sep), 2),\n",
" })\n",
" summary_rows.append(\n",
" {\n",
" \"Method\": name,\n",
" \"AUC-ROC\": round(auc, 4),\n",
" \"Mean normal score\": round(float(normal_s.mean()), 4),\n",
" \"Mean anomaly score\": round(float(anom_s.mean()), 4),\n",
" \"Separation ratio\": round(float(sep), 2),\n",
" }\n",
" )\n",
"\n",
"summary_df = pd.DataFrame(summary_rows)\n",
"print(summary_df.to_string(index=False))"
]
}
]
],
"metadata": {
"accelerator": "GPU",
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+1 -1
View File
@@ -187,4 +187,4 @@ print("ANOMALY DETECTION — SUMMARY")
print("=" * 70)
print(results_df.to_string(index=False))
print("=" * 70)
print("\nHigher AUC-ROC and separation ratio indicate better discrimination " "between normal and anomalous samples.")
print("\nHigher AUC-ROC and separation ratio indicate better discrimination between normal and anomalous samples.")
@@ -28,12 +28,13 @@
"metadata": {},
"outputs": [],
"source": [
"from ludwig.utils.data_utils import load_json\n",
"from ludwig.visualize import learning_curves\n",
"import pandas as pd\n",
"import numpy as np\n",
"import os.path\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import pandas as pd\n",
"\n",
"from ludwig.utils.data_utils import load_json\n",
"\n",
"%matplotlib inline"
]
},
@@ -62,34 +63,34 @@
"source": [
"def create_plot_ready_data(list_of_train_stats, model_names, metric, target):\n",
" # List of splits to evaluate statistics for\n",
" list_of_splits = ['training', 'validation', 'test'] \n",
" \n",
" list_of_splits = [\"training\", \"validation\", \"test\"]\n",
"\n",
" # Empty list to fill with dfs for each models' stats\n",
" list_of_train_stats_df = []\n",
" \n",
"\n",
" # For each models' stats, create a df with columns of stats for each split listed above\n",
" for name, stats in zip(model_names, list_of_train_stats):\n",
" list_of_dfs = []\n",
" for split in list_of_splits:\n",
" df = pd.DataFrame(stats[split][target])\n",
" df.columns = [split + '_' + c for c in df.columns]\n",
" list_of_dfs.append(df)\n",
" \n",
" df = pd.DataFrame(stats[split][target])\n",
" df.columns = [split + \"_\" + c for c in df.columns]\n",
" list_of_dfs.append(df)\n",
"\n",
" combined_df = pd.concat(list_of_dfs, axis=1)\n",
" combined_df.name = name\n",
" combined_df['epoch'] = combined_df.index + 1\n",
" combined_df[\"epoch\"] = combined_df.index + 1\n",
" list_of_train_stats_df.append(combined_df)\n",
" \n",
"\n",
" # holding ready for plot ready data\n",
" plot_ready_list = []\n",
" \n",
"\n",
" # consolidate the multiple training statistics dataframes\n",
" for df in list_of_train_stats_df:\n",
" for col in ['training', 'validation']:\n",
" df2 = df[['epoch', col + '_{}'.format(metric)]].copy()\n",
" df2.columns = ['epoch', '{}'.format(metric)]\n",
" df2['split'] = col\n",
" df2['model'] = df.name\n",
" for col in [\"training\", \"validation\"]:\n",
" df2 = df[[\"epoch\", col + f\"_{metric}\"]].copy()\n",
" df2.columns = [\"epoch\", f\"{metric}\"]\n",
" df2[\"split\"] = col\n",
" df2[\"model\"] = df.name\n",
" plot_ready_list.append(df2)\n",
"\n",
" return pd.concat(plot_ready_list, axis=0, ignore_index=True)"
@@ -112,12 +113,18 @@
"metadata": {},
"outputs": [],
"source": [
"standard_stats = load_json(os.path.join('results/balance_example_standard_model','training_statistics.json'))\n",
"balanced_stats = load_json(os.path.join('results/balance_example_balanced_model','training_statistics.json'))\n",
"standard_stats = load_json(os.path.join(\"results/balance_example_standard_model\", \"training_statistics.json\"))\n",
"balanced_stats = load_json(os.path.join(\"results/balance_example_balanced_model\", \"training_statistics.json\"))\n",
"\n",
"accuracy_learning_curves = create_plot_ready_data([standard_stats, balanced_stats], ['standard_model', 'balanced_model'], 'accuracy', 'Response')\n",
"roc_auc_learning_curves = create_plot_ready_data([standard_stats, balanced_stats], ['standard_model', 'balanced_model'], 'roc_auc', 'Response')\n",
"loss_learning_curves = create_plot_ready_data([standard_stats, balanced_stats], ['standard_model', 'balanced_model'], 'loss', 'Response')"
"accuracy_learning_curves = create_plot_ready_data(\n",
" [standard_stats, balanced_stats], [\"standard_model\", \"balanced_model\"], \"accuracy\", \"Response\"\n",
")\n",
"roc_auc_learning_curves = create_plot_ready_data(\n",
" [standard_stats, balanced_stats], [\"standard_model\", \"balanced_model\"], \"roc_auc\", \"Response\"\n",
")\n",
"loss_learning_curves = create_plot_ready_data(\n",
" [standard_stats, balanced_stats], [\"standard_model\", \"balanced_model\"], \"loss\", \"Response\"\n",
")"
]
},
{
@@ -140,17 +147,14 @@
}
],
"source": [
"fig = plt.figure(figsize=(10,6))\n",
"sns.set_style(style='dark')\n",
"ax = sns.lineplot(x='epoch', y='accuracy',\n",
" style='split',\n",
" hue='model',\n",
" data=accuracy_learning_curves)\n",
"ax.set_title('Accuracy Learning Curves', fontdict={'fontsize': 16})\n",
"ax.grid(visible=True, which='major', color='black', linewidth=0.075)\n",
"ax.grid(visible=True, which='minor', color='black', linewidth=0.075)\n",
"ax.set_xlabel(\"Epoch\", fontsize = 15)\n",
"ax.set_ylabel(\"Accuracy\", fontsize = 15);"
"fig = plt.figure(figsize=(10, 6))\n",
"sns.set_style(style=\"dark\")\n",
"ax = sns.lineplot(x=\"epoch\", y=\"accuracy\", style=\"split\", hue=\"model\", data=accuracy_learning_curves)\n",
"ax.set_title(\"Accuracy Learning Curves\", fontdict={\"fontsize\": 16})\n",
"ax.grid(visible=True, which=\"major\", color=\"black\", linewidth=0.075)\n",
"ax.grid(visible=True, which=\"minor\", color=\"black\", linewidth=0.075)\n",
"ax.set_xlabel(\"Epoch\", fontsize=15)\n",
"ax.set_ylabel(\"Accuracy\", fontsize=15);"
]
},
{
@@ -171,17 +175,14 @@
}
],
"source": [
"fig = plt.figure(figsize=(10,6))\n",
"sns.set_style(style='dark')\n",
"ax = sns.lineplot(x='epoch', y='roc_auc',\n",
" style='split',\n",
" hue='model',\n",
" data=roc_auc_learning_curves)\n",
"ax.set_title('ROC AUC Learning Curves', fontdict={'fontsize': 16})\n",
"ax.grid(visible=True, which='major', color='black', linewidth=0.075)\n",
"ax.grid(visible=True, which='minor', color='black', linewidth=0.075)\n",
"ax.set_xlabel(\"Epoch\", fontsize = 15)\n",
"ax.set_ylabel(\"ROC AUC\", fontsize = 15);"
"fig = plt.figure(figsize=(10, 6))\n",
"sns.set_style(style=\"dark\")\n",
"ax = sns.lineplot(x=\"epoch\", y=\"roc_auc\", style=\"split\", hue=\"model\", data=roc_auc_learning_curves)\n",
"ax.set_title(\"ROC AUC Learning Curves\", fontdict={\"fontsize\": 16})\n",
"ax.grid(visible=True, which=\"major\", color=\"black\", linewidth=0.075)\n",
"ax.grid(visible=True, which=\"minor\", color=\"black\", linewidth=0.075)\n",
"ax.set_xlabel(\"Epoch\", fontsize=15)\n",
"ax.set_ylabel(\"ROC AUC\", fontsize=15);"
]
},
{
@@ -202,17 +203,14 @@
}
],
"source": [
"fig = plt.figure(figsize=(10,6))\n",
"sns.set_style(style='dark')\n",
"ax = sns.lineplot(x='epoch', y='loss',\n",
" style='split',\n",
" hue='model',\n",
" data=loss_learning_curves)\n",
"ax.set_title('Loss Learning Curves', fontdict={'fontsize': 16})\n",
"ax.grid(visible=True, which='major', color='black', linewidth=0.075)\n",
"ax.grid(visible=True, which='minor', color='black', linewidth=0.075)\n",
"ax.set_xlabel(\"Epoch\", fontsize = 15)\n",
"ax.set_ylabel(\"Loss\", fontsize = 15);"
"fig = plt.figure(figsize=(10, 6))\n",
"sns.set_style(style=\"dark\")\n",
"ax = sns.lineplot(x=\"epoch\", y=\"loss\", style=\"split\", hue=\"model\", data=loss_learning_curves)\n",
"ax.set_title(\"Loss Learning Curves\", fontdict={\"fontsize\": 16})\n",
"ax.grid(visible=True, which=\"major\", color=\"black\", linewidth=0.075)\n",
"ax.grid(visible=True, which=\"minor\", color=\"black\", linewidth=0.075)\n",
"ax.set_xlabel(\"Epoch\", fontsize=15)\n",
"ax.set_ylabel(\"Loss\", fontsize=15);"
]
},
{
+1
View File
@@ -187,6 +187,7 @@
"outputs": [],
"source": [
"import yaml\n",
"\n",
"from ludwig.api import LudwigModel\n",
"\n",
"config_concat_str = \"\"\"\n",
+61 -87
View File
@@ -41,18 +41,16 @@
"outputs": [],
"source": [
"import warnings\n",
"warnings.simplefilter('ignore')\n",
"\n",
"import shutil\n",
"warnings.simplefilter(\"ignore\")\n",
"\n",
"import datetime\n",
"import shutil\n",
"\n",
"import pandas as pd\n",
"import numpy as np\n",
"\n",
"from ludwig.hyperopt.run import hyperopt\n",
"from ludwig.visualize import hyperopt_results_to_dataframe, hyperopt_hiplot_cli, hyperopt_report_cli\n",
"\n",
"from sklearn.model_selection import train_test_split"
"from ludwig.visualize import hyperopt_hiplot_cli, hyperopt_report_cli, hyperopt_results_to_dataframe"
]
},
{
@@ -79,7 +77,7 @@
}
],
"source": [
"train_df = pd.read_csv('./data/winequalityN.csv')\n",
"train_df = pd.read_csv(\"./data/winequalityN.csv\")\n",
"train_df.shape"
]
},
@@ -98,8 +96,8 @@
"source": [
"new_col = []\n",
"for i in range(len(train_df.columns)):\n",
" new_col.append(train_df.columns[i].replace(' ', '_'))\n",
" \n",
" new_col.append(train_df.columns[i].replace(\" \", \"_\"))\n",
"\n",
"train_df.columns = new_col"
]
},
@@ -174,7 +172,7 @@
}
],
"source": [
"train_df['quality'].value_counts().sort_index()"
"train_df[\"quality\"].value_counts().sort_index()"
]
},
{
@@ -195,20 +193,20 @@
],
"source": [
"# isolate the predictor variables only\n",
"predictor_vars = list(set(train_df.columns) - set(['quality']))\n",
"predictor_vars = list(set(train_df.columns) - set([\"quality\"]))\n",
"\n",
"#extract categorical variables\n",
"# extract categorical variables\n",
"categorical_vars = []\n",
"for p in predictor_vars:\n",
" if train_df[p].dtype == 'object':\n",
" if train_df[p].dtype == \"object\":\n",
" categorical_vars.append(p)\n",
" \n",
"print(\"categorical variables:\", categorical_vars,'\\n')\n",
"\n",
"print(\"categorical variables:\", categorical_vars, \"\\n\")\n",
"\n",
"# get numerical variables\n",
"numerical_vars = list(set(predictor_vars) - set(categorical_vars))\n",
"\n",
"print(\"numerical variables:\", numerical_vars,\"\\n\")"
"print(\"numerical variables:\", numerical_vars, \"\\n\")"
]
},
{
@@ -438,7 +436,7 @@
],
"source": [
"for p in categorical_vars:\n",
" print(\"unique values for\",p,\"is\",train_df[p].nunique())"
" print(\"unique values for\", p, \"is\", train_df[p].nunique())"
]
},
{
@@ -455,25 +453,28 @@
"outputs": [],
"source": [
"# template for config\n",
"config = {'input_features':[], 'output_features': [], 'trainer':{}}\n",
"config = {\"input_features\": [], \"output_features\": [], \"trainer\": {}}\n",
"\n",
"# setup input features for categorical variables\n",
"for p in categorical_vars:\n",
" a_feature = {'name': p.replace(' ','_'), 'type': 'category', 'representation': 'sparse'}\n",
" config['input_features'].append(a_feature)\n",
" a_feature = {\"name\": p.replace(\" \", \"_\"), \"type\": \"category\", \"representation\": \"sparse\"}\n",
" config[\"input_features\"].append(a_feature)\n",
"\n",
"\n",
"# setup input features for numerical variables\n",
"for p in numerical_vars:\n",
" a_feature = {'name': p.replace(' ','_'), 'type': 'number', \n",
" 'preprocessing': {'missing_value_strategy': 'fill_with_mean', 'normalization': 'zscore'}}\n",
" config['input_features'].append(a_feature)\n",
" a_feature = {\n",
" \"name\": p.replace(\" \", \"_\"),\n",
" \"type\": \"number\",\n",
" \"preprocessing\": {\"missing_value_strategy\": \"fill_with_mean\", \"normalization\": \"zscore\"},\n",
" }\n",
" config[\"input_features\"].append(a_feature)\n",
"\n",
"# set up output variable\n",
"config['output_features'].append({'name': 'quality', 'type':'category'})\n",
"config[\"output_features\"].append({\"name\": \"quality\", \"type\": \"category\"})\n",
"\n",
"# set up trainer\n",
"config['trainer'] = {'epochs': 20}"
"config[\"trainer\"] = {\"epochs\": 20}"
]
},
{
@@ -566,7 +567,7 @@
"metadata": {},
"outputs": [],
"source": [
"SEED=13\n",
"SEED = 13\n",
"\n",
"hyperopt_configs = {\n",
" \"parameters\": {\n",
@@ -580,33 +581,33 @@
" \"trainer.batch_size\": {\n",
" \"type\": \"int\",\n",
" \"space\": \"qlograndint\",\n",
" \"base\" : 2,\n",
" \"base\": 2,\n",
" \"lower\": 32,\n",
" \"upper\": 256,\n",
" \"q\": 5,\n",
" },\n",
" \"quality.fc_size\": {\n",
" \"type\": \"int\",\n",
" 'space': 'qrandint',\n",
" \"space\": \"qrandint\",\n",
" \"lower\": 32,\n",
" \"upper\": 256,\n",
" \"q\": 5,\n",
" },\n",
" \"quality.num_fc_layers\": {\n",
" 'type': 'int',\n",
" 'space': 'qrandint',\n",
" 'lower': 1,\n",
" 'upper': 5,\n",
" 'q': 4,\n",
" }\n",
" \"type\": \"int\",\n",
" \"space\": \"qrandint\",\n",
" \"lower\": 1,\n",
" \"upper\": 5,\n",
" \"q\": 4,\n",
" },\n",
" },\n",
" \"goal\": \"minimize\",\n",
" 'output_feature': \"quality\",\n",
" 'validation_metrics': 'loss'\n",
" \"output_feature\": \"quality\",\n",
" \"validation_metrics\": \"loss\",\n",
"}\n",
"\n",
"# add hyperopt parameter space to the config\n",
"config['hyperopt'] = hyperopt_configs"
"config[\"hyperopt\"] = hyperopt_configs"
]
},
{
@@ -623,9 +624,9 @@
"outputs": [],
"source": [
"# clean out old results\n",
"shutil.rmtree('./results_ray', ignore_errors=True)\n",
"shutil.rmtree('./results_random_serial', ignore_errors=True)\n",
"shutil.rmtree('./visualizations', ignore_errors=True)"
"shutil.rmtree(\"./results_ray\", ignore_errors=True)\n",
"shutil.rmtree(\"./results_random_serial\", ignore_errors=True)\n",
"shutil.rmtree(\"./visualizations\", ignore_errors=True)"
]
},
{
@@ -688,12 +689,12 @@
"%%time\n",
"%%capture\n",
"print(\"starting:\", datetime.datetime.now())\n",
"config['hyperopt']['executor'] = {'type': 'ray', 'time_budget_s': 1000}\n",
"config['hyperopt']['sampler'] = {'type': 'ray', 'num_samples': 3}\n",
"config[\"hyperopt\"][\"executor\"] = {\"type\": \"ray\", \"time_budget_s\": 1000}\n",
"config[\"hyperopt\"][\"sampler\"] = {\"type\": \"ray\", \"num_samples\": 3}\n",
"results_ray = hyperopt(\n",
" config,\n",
" dataset=train_df.sample(4000, random_state=42), # limit number records for demonstration purposes\n",
" output_directory='results_ray' # location to place results\n",
" output_directory=\"results_ray\", # location to place results\n",
")"
]
},
@@ -721,35 +722,17 @@
" \"space\": \"log\",\n",
" \"steps\": 3,\n",
" },\n",
" \"trainer.batch_size\": {\n",
" \"type\": \"int\",\n",
" \"low\": 32,\n",
" \"high\": 256,\n",
" \"space\": \"log\",\n",
" \"steps\": 5,\n",
" \"base\" : 2\n",
" },\n",
" \"quality.fc_size\": {\n",
" \"type\": \"int\",\n",
" \"low\": 32,\n",
" \"high\": 256,\n",
" \"steps\": 5\n",
" },\n",
" \"quality.num_fc_layers\": {\n",
" 'type': 'int',\n",
" 'low': 1,\n",
" 'high': 5,\n",
" 'space': 'linear',\n",
" 'steps': 4\n",
" }\n",
" \"trainer.batch_size\": {\"type\": \"int\", \"low\": 32, \"high\": 256, \"space\": \"log\", \"steps\": 5, \"base\": 2},\n",
" \"quality.fc_size\": {\"type\": \"int\", \"low\": 32, \"high\": 256, \"steps\": 5},\n",
" \"quality.num_fc_layers\": {\"type\": \"int\", \"low\": 1, \"high\": 5, \"space\": \"linear\", \"steps\": 4},\n",
" },\n",
" \"goal\": \"minimize\",\n",
" 'output_feature': \"quality\",\n",
" 'validation_metrics': 'loss'\n",
" \"output_feature\": \"quality\",\n",
" \"validation_metrics\": \"loss\",\n",
"}\n",
"\n",
"# add hyperopt parameter space to the config\n",
"config['hyperopt'] = hyperopt_configs"
"config[\"hyperopt\"] = hyperopt_configs"
]
},
{
@@ -774,13 +757,13 @@
"source": [
"%%time\n",
"print(\"starting:\", datetime.datetime.now())\n",
"config['hyperopt']['executor'] = {'type': 'serial'}\n",
"config['hyperopt']['sampler'] = {'type': 'random', 'num_samples': 2}\n",
"config[\"hyperopt\"][\"executor\"] = {\"type\": \"serial\"}\n",
"config[\"hyperopt\"][\"sampler\"] = {\"type\": \"random\", \"num_samples\": 2}\n",
"results_random_serial = hyperopt(\n",
" config,\n",
" dataset= train_df.sample(4000, random_state=42), # limit number records for demonstration purposes\n",
" output_directory='hyperopt_results',\n",
" experiment_name='random_serial',\n",
" dataset=train_df.sample(4000, random_state=42), # limit number records for demonstration purposes\n",
" output_directory=\"hyperopt_results\",\n",
" experiment_name=\"random_serial\",\n",
")"
]
},
@@ -897,9 +880,7 @@
],
"source": [
"df1 = hyperopt_results_to_dataframe(\n",
" hyperopt_results_dict(results_ray),\n",
" hyperopt_configs['parameters'],\n",
" hyperopt_configs['validation_metrics']\n",
" hyperopt_results_dict(results_ray), hyperopt_configs[\"parameters\"], hyperopt_configs[\"validation_metrics\"]\n",
")\n",
"df1"
]
@@ -982,9 +963,7 @@
],
"source": [
"df2 = hyperopt_results_to_dataframe(\n",
" hyperopt_results_dict(results_random_serial),\n",
" hyperopt_configs['parameters'],\n",
" hyperopt_configs['validation_metrics']\n",
" hyperopt_results_dict(results_random_serial), hyperopt_configs[\"parameters\"], hyperopt_configs[\"validation_metrics\"]\n",
")\n",
"df2"
]
@@ -1071,10 +1050,7 @@
}
],
"source": [
"hyperopt_report_cli(\n",
" 'hyperopt_results/random_serial/hyperopt_statistics.json',\n",
" output_directory='./visualizations'\n",
")"
"hyperopt_report_cli(\"hyperopt_results/random_serial/hyperopt_statistics.json\", output_directory=\"./visualizations\")"
]
},
{
@@ -1090,10 +1066,7 @@
"metadata": {},
"outputs": [],
"source": [
"hyperopt_hiplot_cli(\n",
" 'hyperopt_results/random_serial/hyperopt_statistics.json',\n",
" output_directory='./visualizations'\n",
")"
"hyperopt_hiplot_cli(\"hyperopt_results/random_serial/hyperopt_statistics.json\", output_directory=\"./visualizations\")"
]
},
{
@@ -1122,7 +1095,8 @@
],
"source": [
"from IPython.display import Image\n",
"Image(filename='./images/parallel_coordinates_plot.png')"
"\n",
"Image(filename=\"./images/parallel_coordinates_plot.png\")"
]
}
],
+35 -20
View File
@@ -2,6 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "7fb27b941602401d91542211134fc71a",
"metadata": {},
"source": [
"# Hyperparameter Optimization with Native Optuna\n",
@@ -17,6 +18,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "acae54e37e7d407bbb7b55eff062a284",
"metadata": {},
"outputs": [],
"source": [
@@ -25,6 +27,7 @@
},
{
"cell_type": "markdown",
"id": "9a63283cbaf04dbcab1f6479b197f3a8",
"metadata": {},
"source": [
"> **Dependency note:** The `optuna` executor type (`hyperopt.executor.type: optuna`) is\n",
@@ -38,6 +41,7 @@
},
{
"cell_type": "markdown",
"id": "8dd0d8092fe74a7c96281538738b07e2",
"metadata": {},
"source": [
"## Dataset\n",
@@ -51,6 +55,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "72eea5119410473aa328ad9291626812",
"metadata": {},
"outputs": [],
"source": [
@@ -62,14 +67,8 @@
"DATA_DIR = pathlib.Path(\"data\")\n",
"DATA_DIR.mkdir(exist_ok=True)\n",
"\n",
"WHITE_URL = (\n",
" \"https://archive.ics.uci.edu/ml/machine-learning-databases/\"\n",
" \"wine-quality/winequality-white.csv\"\n",
")\n",
"RED_URL = (\n",
" \"https://archive.ics.uci.edu/ml/machine-learning-databases/\"\n",
" \"wine-quality/winequality-red.csv\"\n",
")\n",
"WHITE_URL = \"https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv\"\n",
"RED_URL = \"https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv\"\n",
"\n",
"combined_path = DATA_DIR / \"wine_quality.csv\"\n",
"\n",
@@ -79,7 +78,7 @@
" urllib.request.urlretrieve(RED_URL, DATA_DIR / \"winequality-red.csv\")\n",
"\n",
" white = pd.read_csv(DATA_DIR / \"winequality-white.csv\", sep=\";\")\n",
" red = pd.read_csv(DATA_DIR / \"winequality-red.csv\", sep=\";\")\n",
" red = pd.read_csv(DATA_DIR / \"winequality-red.csv\", sep=\";\")\n",
" df = pd.concat([white, red], ignore_index=True)\n",
" df[\"quality\"] = (df[\"quality\"] >= 7).astype(int)\n",
" df.to_csv(combined_path, index=False)\n",
@@ -92,6 +91,7 @@
},
{
"cell_type": "markdown",
"id": "8edb47106e1a46a883d545849b8ab81b",
"metadata": {},
"source": [
"## Define search space\n",
@@ -116,6 +116,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "10185d26023b46108eb7d9f57d49d2b3",
"metadata": {},
"outputs": [],
"source": [
@@ -125,8 +126,7 @@
"config = {\n",
" \"model_type\": \"ecd\",\n",
" \"input_features\": [\n",
" {\"name\": col, \"type\": \"number\", \"preprocessing\": {\"normalization\": \"zscore\"}}\n",
" for col in feature_cols\n",
" {\"name\": col, \"type\": \"number\", \"preprocessing\": {\"normalization\": \"zscore\"}} for col in feature_cols\n",
" ],\n",
" \"output_features\": [\n",
" {\"name\": \"quality\", \"type\": \"binary\"},\n",
@@ -139,7 +139,7 @@
" \"executor\": {\n",
" \"type\": \"optuna\",\n",
" \"num_samples\": 20,\n",
" \"sampler\": \"auto\", # auto, tpe, gp, cmaes, random\n",
" \"sampler\": \"auto\", # auto, tpe, gp, cmaes, random\n",
" \"pruner\": \"hyperband\", # stop bad trials early\n",
" },\n",
" \"parameters\": {\n",
@@ -170,11 +170,13 @@
"}\n",
"\n",
"import json\n",
"\n",
"print(json.dumps(config[\"hyperopt\"], indent=2))"
]
},
{
"cell_type": "markdown",
"id": "8763a12b2bbd4a93a75aff182afb95dc",
"metadata": {},
"source": [
"## Run HPO\n",
@@ -185,6 +187,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "7623eae2785240b9bd12b16a66d81610",
"metadata": {},
"outputs": [],
"source": [
@@ -203,6 +206,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "7cdc8c89c7104fffa095e18ddfef8986",
"metadata": {},
"outputs": [],
"source": [
@@ -220,6 +224,7 @@
},
{
"cell_type": "markdown",
"id": "b118ea5561624da68c537baed56e602f",
"metadata": {},
"source": [
"## Sampler comparison\n",
@@ -253,6 +258,7 @@
},
{
"cell_type": "markdown",
"id": "938c804e27f84196a10c8828c723f798",
"metadata": {},
"source": [
"## Resumable HPO with SQLite\n",
@@ -285,6 +291,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "504fb2a444614c0babb325280ed9130a",
"metadata": {},
"outputs": [],
"source": [
@@ -309,6 +316,7 @@
},
{
"cell_type": "markdown",
"id": "59bbdb311c014d738909a11f9e486628",
"metadata": {},
"source": [
"## Pruner: stop bad trials early\n",
@@ -341,6 +349,7 @@
},
{
"cell_type": "markdown",
"id": "b43b363d81ae4b689946ece5c682cd59",
"metadata": {},
"source": [
"## Results\n",
@@ -353,6 +362,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8a65eabff63a45729fe45fb5ade58bdc",
"metadata": {},
"outputs": [],
"source": [
@@ -371,6 +381,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "c3933fab20d04ec698c2621248eb3be0",
"metadata": {},
"outputs": [],
"source": [
@@ -381,18 +392,21 @@
"results_df[\"optimizer_idx\"] = results_df[\"trainer.optimizer.type\"].map(opt_map)\n",
"\n",
"dims = [\n",
" dict(label=\"learning_rate\", values=results_df[\"trainer.learning_rate\"], type=\"log\"),\n",
" dict(label=\"batch_size\", values=results_df[\"trainer.batch_size\"]),\n",
" dict(label=\"optimizer\", values=results_df[\"optimizer_idx\"],\n",
" tickvals=list(opt_map.values()), ticktext=list(opt_map.keys())),\n",
" dict(label=\"dropout\", values=results_df[\"combiner.dropout\"]),\n",
" dict(label=\"val loss\", values=results_df[\"loss\"]),\n",
" dict(label=\"learning_rate\", values=results_df[\"trainer.learning_rate\"], type=\"log\"),\n",
" dict(label=\"batch_size\", values=results_df[\"trainer.batch_size\"]),\n",
" dict(\n",
" label=\"optimizer\",\n",
" values=results_df[\"optimizer_idx\"],\n",
" tickvals=list(opt_map.values()),\n",
" ticktext=list(opt_map.keys()),\n",
" ),\n",
" dict(label=\"dropout\", values=results_df[\"combiner.dropout\"]),\n",
" dict(label=\"val loss\", values=results_df[\"loss\"]),\n",
"]\n",
"\n",
"fig = px.parallel_coordinates(\n",
" results_df,\n",
" dimensions=[\"trainer.learning_rate\", \"trainer.batch_size\",\n",
" \"optimizer_idx\", \"combiner.dropout\", \"loss\"],\n",
" dimensions=[\"trainer.learning_rate\", \"trainer.batch_size\", \"optimizer_idx\", \"combiner.dropout\", \"loss\"],\n",
" color=\"loss\",\n",
" color_continuous_scale=px.colors.sequential.Viridis_r,\n",
" labels={\n",
@@ -410,6 +424,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "4dd4641cc4064e0191573fe9c69df29b",
"metadata": {},
"outputs": [],
"source": [
+2 -2
View File
@@ -27,8 +27,8 @@ import pandas as pd
DATA_DIR = pathlib.Path("data")
DATA_DIR.mkdir(exist_ok=True)
WHITE_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/" "wine-quality/winequality-white.csv"
RED_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/" "wine-quality/winequality-red.csv"
WHITE_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv"
RED_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
white_path = DATA_DIR / "winequality-white.csv"
red_path = DATA_DIR / "winequality-red.csv"
+1 -2
View File
@@ -125,8 +125,7 @@ def main() -> None:
"--encoders",
nargs="+",
default=None,
help="Subset of encoders to run (e.g. --encoders stacked_cnn dinov2_linear_probe). "
"Defaults to all encoders.",
help="Subset of encoders to run (e.g. --encoders stacked_cnn dinov2_linear_probe). Defaults to all encoders.",
)
args = parser.parse_args()
+15 -17
View File
@@ -1,18 +1,4 @@
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
@@ -59,7 +45,6 @@
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import time\n",
"import warnings\n",
"\n",
@@ -608,7 +593,6 @@
"metadata": {},
"outputs": [],
"source": [
"import csv\n",
"from pathlib import Path\n",
"\n",
"import pandas as pd\n",
@@ -739,5 +723,19 @@
"Available SigLIP variants: `google/siglip-base-patch16-224`, `google/siglip-large-patch16-256`, `google/siglip-so400m-patch14-384`"
]
}
]
],
"metadata": {
"accelerator": "GPU",
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
File diff suppressed because one or more lines are too long
@@ -130,7 +130,7 @@ def parse_args():
parser.add_argument(
"--model",
default=DEFAULT_MODEL,
help=f"LLM model name (default: {DEFAULT_MODEL}). " "Claude models start with 'claude', OpenAI with 'gpt'.",
help=f"LLM model name (default: {DEFAULT_MODEL}). Claude models start with 'claude', OpenAI with 'gpt'.",
)
parser.add_argument(
"--no-train",
@@ -1,20 +1,4 @@
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
},
"colab": {
"provenance": []
}
},
"cells": [
{
"cell_type": "markdown",
@@ -72,6 +56,7 @@
"# --- Colab: read from Colab Secrets ---\n",
"try:\n",
" from google.colab import userdata\n",
"\n",
" os.environ[\"ANTHROPIC_API_KEY\"] = userdata.get(\"ANTHROPIC_API_KEY\")\n",
" print(\"Loaded ANTHROPIC_API_KEY from Colab Secrets.\")\n",
"except Exception:\n",
@@ -84,7 +69,7 @@
" print(\"WARNING: ANTHROPIC_API_KEY not set. Set it before running generation cells.\")\n",
"\n",
"# Choose your model:\n",
"CLAUDE_MODEL = \"claude-sonnet-4-20250514\" # Anthropic\n",
"CLAUDE_MODEL = \"claude-sonnet-4-20250514\" # Anthropic\n",
"# GPT_MODEL = \"gpt-4o\" # OpenAI (set OPENAI_API_KEY instead)"
]
},
@@ -98,6 +83,7 @@
"# NOTE: ludwig.config_generation is provided by PR #4092 (ludwig>=0.14).\n",
"# If you see an ImportError, that PR has not yet been merged into your installed version.\n",
"import yaml\n",
"\n",
"from ludwig.config_generation import generate_config"
]
},
@@ -143,21 +129,25 @@
"outputs": [],
"source": [
"# Build a small synthetic dataset matching the generated schema and train\n",
"import os\n",
"import tempfile\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"import tempfile, os\n",
"\n",
"np.random.seed(42)\n",
"n = 300\n",
"\n",
"synthetic_churn = pd.DataFrame({\n",
" \"age\": np.random.randint(18, 70, n),\n",
" \"annual_income\": np.random.uniform(20_000, 150_000, n),\n",
" \"num_purchases\": np.random.randint(0, 200, n),\n",
" \"days_since_last_purchase\": np.random.randint(0, 365, n),\n",
" \"country\": np.random.choice([\"US\", \"UK\", \"DE\", \"FR\", \"CA\"], n),\n",
" \"churn\": np.random.randint(0, 2, n),\n",
"})\n",
"synthetic_churn = pd.DataFrame(\n",
" {\n",
" \"age\": np.random.randint(18, 70, n),\n",
" \"annual_income\": np.random.uniform(20_000, 150_000, n),\n",
" \"num_purchases\": np.random.randint(0, 200, n),\n",
" \"days_since_last_purchase\": np.random.randint(0, 365, n),\n",
" \"country\": np.random.choice([\"US\", \"UK\", \"DE\", \"FR\", \"CA\"], n),\n",
" \"churn\": np.random.randint(0, 2, n),\n",
" }\n",
")\n",
"\n",
"with tempfile.NamedTemporaryFile(suffix=\".csv\", delete=False, mode=\"w\") as f:\n",
" synthetic_churn.to_csv(f, index=False)\n",
@@ -243,15 +233,17 @@
"np.random.seed(0)\n",
"n = 300\n",
"\n",
"synthetic_multitask = pd.DataFrame({\n",
" \"customer_age\": np.random.randint(18, 75, n),\n",
" \"account_tenure_days\": np.random.randint(1, 3650, n),\n",
" \"total_spend\": np.random.uniform(10, 10_000, n),\n",
" \"product_category\": np.random.choice([\"Electronics\", \"Clothing\", \"Food\", \"Books\"], n),\n",
" \"region\": np.random.choice([\"North\", \"South\", \"East\", \"West\"], n),\n",
" \"will_churn\": np.random.randint(0, 2, n),\n",
" \"predicted_lifetime_value\": np.random.uniform(0, 5000, n),\n",
"})\n",
"synthetic_multitask = pd.DataFrame(\n",
" {\n",
" \"customer_age\": np.random.randint(18, 75, n),\n",
" \"account_tenure_days\": np.random.randint(1, 3650, n),\n",
" \"total_spend\": np.random.uniform(10, 10_000, n),\n",
" \"product_category\": np.random.choice([\"Electronics\", \"Clothing\", \"Food\", \"Books\"], n),\n",
" \"region\": np.random.choice([\"North\", \"South\", \"East\", \"West\"], n),\n",
" \"will_churn\": np.random.randint(0, 2, n),\n",
" \"predicted_lifetime_value\": np.random.uniform(0, 5000, n),\n",
" }\n",
")\n",
"\n",
"with tempfile.NamedTemporaryFile(suffix=\".csv\", delete=False, mode=\"w\") as f:\n",
" synthetic_multitask.to_csv(f, index=False)\n",
@@ -382,5 +374,21 @@
"Even if the first generated config is not perfect, it provides an excellent starting point. Inspect the YAML, make targeted edits, and re-run training — far faster than writing the config from scratch."
]
}
]
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -186,7 +186,7 @@ def run_sentiment_comparison() -> None:
preds_constrained["sentiment_predictions"],
):
short_text = textwrap.shorten(text, width=50)
print(f"{short_text:<52} {str(unconstrained):<30} {str(constrained):<15}")
print(f"{short_text:<52} {unconstrained!s:<30} {constrained!s:<15}")
# Count invalid outputs in unconstrained
valid_labels = {"positive", "negative", "neutral"}
@@ -221,7 +221,7 @@
"sentiment_preds, _, _ = sentiment_model.predict(dataset=sentiment_df)\n",
"\n",
"for text, label in zip(sentiment_samples, sentiment_preds[\"sentiment_predictions\"]):\n",
" print(f\"{str(label):<10} {text}\")"
" print(f\"{label!s:<10} {text}\")"
]
},
{
@@ -281,6 +281,7 @@
"\n",
"if \"response_logits\" in output_df.columns:\n",
" import numpy as np\n",
"\n",
" logits = output_df[\"response_logits\"].iloc[0]\n",
" logits_arr = np.array(logits)\n",
" print(f\"Logits shape: {logits_arr.shape}\")\n",
@@ -352,7 +353,7 @@
" unc_str = str(unc).strip()\n",
" # Highlight invalid outputs\n",
" flag = \" *** INVALID\" if unc_str.lower() not in valid_labels else \"\"\n",
" print(f\"{short:<48} {unc_str:<32} {str(con)}{flag}\")\n",
" print(f\"{short:<48} {unc_str:<32} {con!s}{flag}\")\n",
"\n",
"n_invalid = sum(1 for u in unconstrained_labels if str(u).strip().lower() not in valid_labels)\n",
"print(f\"\\nUnconstrained — invalid outputs: {n_invalid}/{len(sentiment_samples)}\")\n",
@@ -14,18 +14,20 @@
"outputs": [],
"source": [
"import warnings\n",
"warnings.simplefilter('ignore')\n",
"from ludwig.api import LudwigModel\n",
"from ludwig.datasets import mnist\n",
"from ludwig.visualize import compare_performance, compare_classifiers_performance_from_pred, \\\n",
" confusion_matrix\n",
"from ludwig.utils.data_utils import load_json\n",
"import pandas as pd\n",
"\n",
"warnings.simplefilter(\"ignore\")\n",
"import os\n",
"import os.path\n",
"import shutil\n",
"\n",
"shutil.rmtree('./viz2', ignore_errors=True)"
"import pandas as pd\n",
"\n",
"from ludwig.api import LudwigModel\n",
"from ludwig.datasets import mnist\n",
"from ludwig.utils.data_utils import load_json\n",
"from ludwig.visualize import compare_classifiers_performance_from_pred, compare_performance, confusion_matrix\n",
"\n",
"shutil.rmtree(\"./viz2\", ignore_errors=True)"
]
},
{
@@ -55,13 +57,13 @@
],
"source": [
"# create test dataframe\n",
"test_data = {'image_path': [], 'label': []}\n",
"test_data = {\"image_path\": [], \"label\": []}\n",
"dataset = mnist.Mnist()\n",
"test_dir = os.path.join(dataset.processed_dataset_path, 'testing')\n",
"test_dir = os.path.join(dataset.processed_dataset_path, \"testing\")\n",
"for label in os.listdir(test_dir):\n",
" files = os.listdir(os.path.join(test_dir, label))\n",
" test_data['image_path'] += [os.path.join(test_dir, label, f) for f in files]\n",
" test_data['label'] += len(files) * [label]\n",
" test_data[\"image_path\"] += [os.path.join(test_dir, label, f) for f in files]\n",
" test_data[\"label\"] += len(files) * [label]\n",
"\n",
"# collect data into a data frame\n",
"test_df = pd.DataFrame(test_data)\n",
@@ -82,20 +84,20 @@
"outputs": [],
"source": [
"# get list of models to visualize results\n",
"models_list = ['Option1', 'Option2', 'Option3']\n",
"models_list = [\"Option1\", \"Option2\", \"Option3\"]\n",
"test_stats_list = []\n",
"preds_list = []\n",
"\n",
"for m in models_list:\n",
" # retrieve a trained model\n",
" model = LudwigModel.load('./results/multiple_experiment_'+ m + '/model')\n",
" model = LudwigModel.load(\"./results/multiple_experiment_\" + m + \"/model\")\n",
"\n",
" # make predictions\n",
" test_stats, pred_df, _ = model.evaluate(dataset=test_df, collect_predictions=True, collect_overall_stats=True)\n",
" \n",
"\n",
" # collect test statsitics\n",
" preds_list.append(pred_df['label_predictions'].astype('int'))\n",
" test_stats_list.append(test_stats)\n"
" preds_list.append(pred_df[\"label_predictions\"].astype(\"int\"))\n",
" test_stats_list.append(test_stats)"
]
},
{
@@ -123,13 +125,7 @@
],
"source": [
"# overall model performance\n",
"compare_performance(\n",
" test_stats_list,\n",
" 'label',\n",
" model_names=models_list,\n",
" output_directory='./viz2',\n",
" file_format='png'\n",
")"
"compare_performance(test_stats_list, \"label\", model_names=models_list, output_directory=\"./viz2\", file_format=\"png\")"
]
},
{
@@ -150,16 +146,16 @@
],
"source": [
"# Classifiction performance metrics by model\n",
"train_metadata_json = load_json('./results/multiple_experiment_Option1/model/training_set_metadata.json')\n",
"train_metadata_json = load_json(\"./results/multiple_experiment_Option1/model/training_set_metadata.json\")\n",
"compare_classifiers_performance_from_pred(\n",
" preds_list,\n",
" test_df['label'].to_numpy().astype('int'),\n",
" train_metadata_json,\n",
" 'label',\n",
" 10,\n",
" model_names=models_list,\n",
" output_directory='./viz2',\n",
" file_format='png'\n",
" preds_list,\n",
" test_df[\"label\"].to_numpy().astype(\"int\"),\n",
" train_metadata_json,\n",
" \"label\",\n",
" 10,\n",
" model_names=models_list,\n",
" output_directory=\"./viz2\",\n",
" file_format=\"png\",\n",
")"
]
},
@@ -352,14 +348,14 @@
"source": [
"# Confustion matrix by model\n",
"confusion_matrix(\n",
" test_stats_list,\n",
" train_metadata_json,\n",
" 'label',\n",
" [10,10,10],\n",
" False,\n",
" model_names=models_list,\n",
" output_directory='./viz2',\n",
" file_format='png'\n",
" test_stats_list,\n",
" train_metadata_json,\n",
" \"label\",\n",
" [10, 10, 10],\n",
" False,\n",
" model_names=models_list,\n",
" output_directory=\"./viz2\",\n",
" file_format=\"png\",\n",
")"
]
},
+41 -16
View File
@@ -2,6 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "7fb27b941602401d91542211134fc71a",
"metadata": {},
"source": [
"# Multi-Task Learning with Nash-MTL Loss Balancing\n",
@@ -29,6 +30,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "acae54e37e7d407bbb7b55eff062a284",
"metadata": {},
"outputs": [],
"source": [
@@ -38,11 +40,11 @@
{
"cell_type": "code",
"execution_count": null,
"id": "9a63283cbaf04dbcab1f6479b197f3a8",
"metadata": {},
"outputs": [],
"source": [
"import logging\n",
"import os\n",
"import shutil\n",
"import warnings\n",
"\n",
@@ -53,6 +55,7 @@
},
{
"cell_type": "markdown",
"id": "8dd0d8092fe74a7c96281538738b07e2",
"metadata": {},
"source": [
"## Dataset\n",
@@ -69,18 +72,24 @@
{
"cell_type": "code",
"execution_count": null,
"id": "72eea5119410473aa328ad9291626812",
"metadata": {},
"outputs": [],
"source": [
"WINE_URL = (\n",
" \"https://archive.ics.uci.edu/ml/machine-learning-databases/\"\n",
" \"wine-quality/winequality-red.csv\"\n",
")\n",
"WINE_URL = \"https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv\"\n",
"\n",
"WINE_FEATURES = [\n",
" \"fixed_acidity\", \"volatile_acidity\", \"citric_acid\", \"residual_sugar\",\n",
" \"chlorides\", \"free_sulfur_dioxide\", \"total_sulfur_dioxide\",\n",
" \"density\", \"pH\", \"sulphates\", \"alcohol\",\n",
" \"fixed_acidity\",\n",
" \"volatile_acidity\",\n",
" \"citric_acid\",\n",
" \"residual_sugar\",\n",
" \"chlorides\",\n",
" \"free_sulfur_dioxide\",\n",
" \"total_sulfur_dioxide\",\n",
" \"density\",\n",
" \"pH\",\n",
" \"sulphates\",\n",
" \"alcohol\",\n",
"]\n",
"\n",
"print(\"Downloading wine quality dataset...\")\n",
@@ -99,6 +108,7 @@
},
{
"cell_type": "markdown",
"id": "8edb47106e1a46a883d545849b8ab81b",
"metadata": {},
"source": [
"### Why does loss balancing matter here?\n",
@@ -113,16 +123,16 @@
{
"cell_type": "code",
"execution_count": null,
"id": "10185d26023b46108eb7d9f57d49d2b3",
"metadata": {},
"outputs": [],
"source": [
"from ludwig.api import LudwigModel\n",
"\n",
"\n",
"def input_features():\n",
" return [\n",
" {\"name\": feat, \"type\": \"number\", \"preprocessing\": {\"normalization\": \"zscore\"}}\n",
" for feat in WINE_FEATURES\n",
" ]\n",
" return [{\"name\": feat, \"type\": \"number\", \"preprocessing\": {\"normalization\": \"zscore\"}} for feat in WINE_FEATURES]\n",
"\n",
"\n",
"def make_config(loss_balancing: str) -> dict:\n",
" return {\n",
@@ -146,12 +156,13 @@
" },\n",
" }\n",
"\n",
"\n",
"def train_and_evaluate(name: str, loss_balancing: str) -> dict | None:\n",
" result_dir = f\"./results/{name}\"\n",
" shutil.rmtree(result_dir, ignore_errors=True)\n",
" print(f\"\\n{'='*50}\")\n",
" print(f\"\\n{'=' * 50}\")\n",
" print(f\"Training: {name} (loss_balancing={loss_balancing})\")\n",
" print(f\"{'='*50}\")\n",
" print(f\"{'=' * 50}\")\n",
" config = make_config(loss_balancing)\n",
" model = LudwigModel(config=config, logging_level=logging.WARNING)\n",
" train_stats, _, _ = model.train(\n",
@@ -168,6 +179,7 @@
" print(f\" quality_binary ROC-AUC : {binary_auc:.4f}\")\n",
" return {\"method\": name, \"score_mae\": score_mae, \"binary_roc_auc\": binary_auc}\n",
"\n",
"\n",
"def _last(series):\n",
" if not series:\n",
" return float(\"nan\")\n",
@@ -176,11 +188,13 @@
" v = v[-1]\n",
" return float(v)\n",
"\n",
"\n",
"results = []"
]
},
{
"cell_type": "markdown",
"id": "8763a12b2bbd4a93a75aff182afb95dc",
"metadata": {},
"source": [
"## Baseline: No Loss Balancing\n",
@@ -203,6 +217,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "7623eae2785240b9bd12b16a66d81610",
"metadata": {},
"outputs": [],
"source": [
@@ -212,6 +227,7 @@
},
{
"cell_type": "markdown",
"id": "7cdc8c89c7104fffa095e18ddfef8986",
"metadata": {},
"source": [
"## FAMO Balancing\n",
@@ -235,6 +251,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "b118ea5561624da68c537baed56e602f",
"metadata": {},
"outputs": [],
"source": [
@@ -244,6 +261,7 @@
},
{
"cell_type": "markdown",
"id": "938c804e27f84196a10c8828c723f798",
"metadata": {},
"source": [
"## Uncertainty Weighting\n",
@@ -267,6 +285,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "504fb2a444614c0babb325280ed9130a",
"metadata": {},
"outputs": [],
"source": [
@@ -276,6 +295,7 @@
},
{
"cell_type": "markdown",
"id": "59bbdb311c014d738909a11f9e486628",
"metadata": {},
"source": [
"## Nash-MTL Balancing\n",
@@ -314,6 +334,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "b43b363d81ae4b689946ece5c682cd59",
"metadata": {},
"outputs": [],
"source": [
@@ -331,6 +352,7 @@
},
{
"cell_type": "markdown",
"id": "8a65eabff63a45729fe45fb5ade58bdc",
"metadata": {},
"source": [
"## Comparison Table\n",
@@ -341,6 +363,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "c3933fab20d04ec698c2621248eb3be0",
"metadata": {},
"outputs": [],
"source": [
@@ -355,6 +378,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "4dd4641cc4064e0191573fe9c69df29b",
"metadata": {},
"outputs": [],
"source": [
@@ -368,12 +392,12 @@
"\n",
"colors = [\"tab:gray\", \"tab:blue\", \"tab:orange\", \"tab:green\"]\n",
"\n",
"axes[0].bar(methods, mae_vals, color=colors[:len(methods)])\n",
"axes[0].bar(methods, mae_vals, color=colors[: len(methods)])\n",
"axes[0].set_title(\"Quality Score — MAE (lower is better)\")\n",
"axes[0].set_ylabel(\"MAE\")\n",
"axes[0].set_ylim(0, max(mae_vals) * 1.2)\n",
"\n",
"axes[1].bar(methods, auc_vals, color=colors[:len(methods)])\n",
"axes[1].bar(methods, auc_vals, color=colors[: len(methods)])\n",
"axes[1].set_title(\"Quality Binary — ROC-AUC (higher is better)\")\n",
"axes[1].set_ylabel(\"ROC-AUC\")\n",
"axes[1].set_ylim(0.5, 1.0)\n",
@@ -385,6 +409,7 @@
},
{
"cell_type": "markdown",
"id": "8309879909854d7188b41380fd92a7c3",
"metadata": {},
"source": [
"## When to Use Nash-MTL\n",
+3 -3
View File
@@ -31,7 +31,7 @@ logging.basicConfig(level=logging.WARNING)
# Dataset
# ---------------------------------------------------------------------------
WINE_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/" "wine-quality/winequality-red.csv"
WINE_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
WINE_FEATURES = [
"fixed_acidity",
@@ -162,7 +162,7 @@ def _last_value(series) -> float | None:
def print_comparison_table(results: dict) -> None:
"""Print a formatted side-by-side comparison of all methods."""
col_w = 14
header = f"{'Method':<{col_w}} | " f"{'Score MAE':>{col_w}} | " f"{'Binary ROC-AUC':>{col_w}}"
header = f"{'Method':<{col_w}} | {'Score MAE':>{col_w}} | {'Binary ROC-AUC':>{col_w}}"
separator = "-" * len(header)
print()
print("=" * len(header))
@@ -219,7 +219,7 @@ def main():
# Attempt nash_mtl — will succeed if PR #4092 is available
try:
from ludwig.api import LudwigModel # noqa: F401
from ludwig.api import LudwigModel
config = _base_config("nash_mtl")
# Try instantiating to check if nash_mtl is a valid option
@@ -1,20 +1,4 @@
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
},
"colab": {
"provenance": []
}
},
"cells": [
{
"cell_type": "markdown",
@@ -75,36 +59,34 @@
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import csv\n",
"from pathlib import Path\n",
"\n",
"import torch\n",
"from torchvision import datasets, transforms\n",
"from PIL import Image\n",
"from torchvision import datasets, transforms\n",
"\n",
"# ── configuration ──────────────────────────────────────────────────────────────\n",
"DATA_DIR = Path(\"mnist_data\") # raw MNIST download\n",
"IMG_DIR = Path(\"mnist_images\") # saved PNG files\n",
"KNOWN_CLASSES = list(range(8)) # digits 0-7\n",
"UNKNOWN_CLASSES = [8, 9] # background / unknown\n",
"DATA_DIR = Path(\"mnist_data\") # raw MNIST download\n",
"IMG_DIR = Path(\"mnist_images\") # saved PNG files\n",
"KNOWN_CLASSES = list(range(8)) # digits 0-7\n",
"UNKNOWN_CLASSES = [8, 9] # background / unknown\n",
"\n",
"# Limit samples per class so training is fast on CPU\n",
"MAX_TRAIN_KNOWN = 500 # per known class\n",
"MAX_TRAIN_UNKNOWN = 500 # per unknown class (background)\n",
"MAX_TEST_KNOWN = 200 # per known class\n",
"MAX_TEST_UNKNOWN = 200 # per unknown class\n",
"MAX_TRAIN_KNOWN = 500 # per known class\n",
"MAX_TRAIN_UNKNOWN = 500 # per unknown class (background)\n",
"MAX_TEST_KNOWN = 200 # per known class\n",
"MAX_TEST_UNKNOWN = 200 # per unknown class\n",
"\n",
"IMG_DIR.mkdir(parents=True, exist_ok=True)\n",
"\n",
"# ── download ───────────────────────────────────────────────────────────────────\n",
"mnist_train = datasets.MNIST(str(DATA_DIR), train=True, download=True,\n",
" transform=transforms.ToTensor())\n",
"mnist_test = datasets.MNIST(str(DATA_DIR), train=False, download=True,\n",
" transform=transforms.ToTensor())\n",
"mnist_train = datasets.MNIST(str(DATA_DIR), train=True, download=True, transform=transforms.ToTensor())\n",
"mnist_test = datasets.MNIST(str(DATA_DIR), train=False, download=True, transform=transforms.ToTensor())\n",
"\n",
"print(f\"Downloaded: {len(mnist_train)} train / {len(mnist_test)} test samples\")\n",
"\n",
"\n",
"# ── helper: save image and return path ────────────────────────────────────────\n",
"def save_image(tensor: torch.Tensor, split: str, digit: int, idx: int) -> str:\n",
" \"\"\"Save a (1, H, W) float tensor as a grayscale PNG; return the file path.\"\"\"\n",
@@ -115,21 +97,29 @@
" img.save(fpath)\n",
" return str(fpath)\n",
"\n",
"\n",
"# ── build train.csv ───────────────────────────────────────────────────────────\n",
"# class counter for capping per-class samples\n",
"from collections import defaultdict\n",
"\n",
"def build_csv(dataset, csv_path: str, split: str,\n",
" known_classes, unknown_classes,\n",
" max_known: int, max_unknown: int,\n",
" label_unknown_as_background: bool):\n",
"\n",
"def build_csv(\n",
" dataset,\n",
" csv_path: str,\n",
" split: str,\n",
" known_classes,\n",
" unknown_classes,\n",
" max_known: int,\n",
" max_unknown: int,\n",
" label_unknown_as_background: bool,\n",
"):\n",
" \"\"\"\n",
" Walk *dataset*, save PNGs, write *csv_path*.\n",
"\n",
" label_unknown_as_background=True → training split (unknown → \"background\")\n",
" label_unknown_as_background=False → test split (unknown keeps true digit string)\n",
" \"\"\"\n",
" counts_known = defaultdict(int)\n",
" counts_known = defaultdict(int)\n",
" counts_unknown = defaultdict(int)\n",
" rows = []\n",
"\n",
@@ -138,13 +128,13 @@
" if digit in known_classes:\n",
" if counts_known[digit] >= max_known:\n",
" continue\n",
" path = save_image(img_tensor, split, digit, global_idx)\n",
" path = save_image(img_tensor, split, digit, global_idx)\n",
" label = str(digit)\n",
" counts_known[digit] += 1\n",
" elif digit in unknown_classes:\n",
" if counts_unknown[digit] >= max_unknown:\n",
" continue\n",
" path = save_image(img_tensor, split, digit, global_idx)\n",
" path = save_image(img_tensor, split, digit, global_idx)\n",
" label = \"background\" if label_unknown_as_background else str(digit)\n",
" counts_unknown[digit] += 1\n",
" else:\n",
@@ -152,8 +142,9 @@
" rows.append({\"image_path\": path, \"label\": label})\n",
"\n",
" # stop early once all caps are met\n",
" if (all(counts_known[c] >= max_known for c in known_classes) and\n",
" all(counts_unknown[c] >= max_unknown for c in unknown_classes)):\n",
" if all(counts_known[c] >= max_known for c in known_classes) and all(\n",
" counts_unknown[c] >= max_unknown for c in unknown_classes\n",
" ):\n",
" break\n",
"\n",
" with open(csv_path, \"w\", newline=\"\") as f:\n",
@@ -161,24 +152,33 @@
" writer.writeheader()\n",
" writer.writerows(rows)\n",
"\n",
" known_total = sum(counts_known.values())\n",
" known_total = sum(counts_known.values())\n",
" unknown_total = sum(counts_unknown.values())\n",
" print(f\" {csv_path}: {known_total} known, {unknown_total} unknown/background\")\n",
" return rows\n",
"\n",
"\n",
"print(\"Building train.csv ...\")\n",
"train_rows = build_csv(\n",
" mnist_train, \"train.csv\", \"train\",\n",
" KNOWN_CLASSES, UNKNOWN_CLASSES,\n",
" MAX_TRAIN_KNOWN, MAX_TRAIN_UNKNOWN,\n",
" mnist_train,\n",
" \"train.csv\",\n",
" \"train\",\n",
" KNOWN_CLASSES,\n",
" UNKNOWN_CLASSES,\n",
" MAX_TRAIN_KNOWN,\n",
" MAX_TRAIN_UNKNOWN,\n",
" label_unknown_as_background=True,\n",
")\n",
"\n",
"print(\"Building test.csv ...\")\n",
"test_rows = build_csv(\n",
" mnist_test, \"test.csv\", \"test\",\n",
" KNOWN_CLASSES, UNKNOWN_CLASSES,\n",
" MAX_TEST_KNOWN, MAX_TEST_UNKNOWN,\n",
" mnist_test,\n",
" \"test.csv\",\n",
" \"test\",\n",
" KNOWN_CLASSES,\n",
" UNKNOWN_CLASSES,\n",
" MAX_TEST_KNOWN,\n",
" MAX_TEST_UNKNOWN,\n",
" label_unknown_as_background=False,\n",
")\n",
"\n",
@@ -203,7 +203,7 @@
"import pandas as pd\n",
"\n",
"train_df = pd.read_csv(\"train.csv\")\n",
"test_df = pd.read_csv(\"test.csv\")\n",
"test_df = pd.read_csv(\"test.csv\")\n",
"\n",
"print(\"train.csv label distribution:\")\n",
"print(train_df[\"label\"].value_counts().sort_index())\n",
@@ -269,10 +269,7 @@
"config_baseline = {\n",
" \"model_type\": \"ecd\",\n",
" \"input_features\": [{\"name\": \"image_path\", \"type\": \"image\", \"encoder\": ENCODER}],\n",
" \"output_features\": [\n",
" {\"name\": \"label\", \"type\": \"category\",\n",
" \"loss\": {\"type\": \"softmax_cross_entropy\"}}\n",
" ],\n",
" \"output_features\": [{\"name\": \"label\", \"type\": \"category\", \"loss\": {\"type\": \"softmax_cross_entropy\"}}],\n",
" \"trainer\": {\"epochs\": 10, \"learning_rate\": 0.001, \"batch_size\": 128},\n",
"}\n",
"\n",
@@ -329,17 +326,13 @@
"# We need to know what index Ludwig will assign to \"background\" in *that* vocabulary.\n",
"# The simplest way is to train the entropic model first (or do a preprocessing run),\n",
"# but we can also use Ludwig's preprocessing API directly.\n",
"\n",
"from ludwig.api import LudwigModel\n",
"\n",
"# Build a minimal config for preprocessing only\n",
"config_for_vocab = {\n",
" \"model_type\": \"ecd\",\n",
" \"input_features\": [{\"name\": \"image_path\", \"type\": \"image\", \"encoder\": ENCODER}],\n",
" \"output_features\": [\n",
" {\"name\": \"label\", \"type\": \"category\",\n",
" \"loss\": {\"type\": \"softmax_cross_entropy\"}}\n",
" ],\n",
" \"output_features\": [{\"name\": \"label\", \"type\": \"category\", \"loss\": {\"type\": \"softmax_cross_entropy\"}}],\n",
" \"trainer\": {\"epochs\": 1, \"batch_size\": 128},\n",
"}\n",
"\n",
@@ -504,27 +497,29 @@
"import numpy as np\n",
"\n",
"# Separate test rows into known and unknown\n",
"test_known_df = test_df[~test_df[\"label\"].isin([\"8\", \"9\"])].copy()\n",
"test_unknown_df = test_df[ test_df[\"label\"].isin([\"8\", \"9\"])].copy()\n",
"test_known_df = test_df[~test_df[\"label\"].isin([\"8\", \"9\"])].copy()\n",
"test_unknown_df = test_df[test_df[\"label\"].isin([\"8\", \"9\"])].copy()\n",
"\n",
"print(f\"Test known: {len(test_known_df)} samples\")\n",
"print(f\"Test unknown: {len(test_unknown_df)} samples\")\n",
"\n",
"\n",
"def get_max_probs(model, df):\n",
" \"\"\"Return an array of max softmax probabilities for each row in df.\"\"\"\n",
" preds, _ = model.predict(dataset=df, skip_save_predictions=True)\n",
" return preds[\"label_probability\"].values\n",
"\n",
"\n",
"print(\"Predicting with baseline ...\")\n",
"probs_baseline_known = get_max_probs(model_baseline, test_known_df)\n",
"probs_baseline_unknown = get_max_probs(model_baseline, test_unknown_df)\n",
"probs_baseline_known = get_max_probs(model_baseline, test_known_df)\n",
"probs_baseline_unknown = get_max_probs(model_baseline, test_unknown_df)\n",
"\n",
"print(\"Predicting with entropic ...\")\n",
"probs_entropic_known = get_max_probs(model_entropic, test_known_df)\n",
"probs_entropic_unknown = get_max_probs(model_entropic, test_unknown_df)\n",
"probs_entropic_known = get_max_probs(model_entropic, test_known_df)\n",
"probs_entropic_unknown = get_max_probs(model_entropic, test_unknown_df)\n",
"\n",
"print(\"Predicting with objectosphere ...\")\n",
"probs_obj_known = get_max_probs(model_objectosphere, test_known_df)\n",
"probs_obj_known = get_max_probs(model_objectosphere, test_known_df)\n",
"probs_obj_unknown = get_max_probs(model_objectosphere, test_unknown_df)\n",
"\n",
"print(\"Done.\")"
@@ -543,14 +538,14 @@
"bins = np.linspace(0, 1, 30)\n",
"\n",
"model_data = [\n",
" (\"CE Baseline\", probs_baseline_known, probs_baseline_unknown),\n",
" (\"CE Baseline\", probs_baseline_known, probs_baseline_unknown),\n",
" (\"Entropic Open-Set\", probs_entropic_known, probs_entropic_unknown),\n",
" (\"Objectosphere\", probs_obj_known, probs_obj_unknown),\n",
" (\"Objectosphere\", probs_obj_known, probs_obj_unknown),\n",
"]\n",
"\n",
"for ax, (title, known, unknown) in zip(axes, model_data):\n",
" ax.hist(known, bins=bins, alpha=0.6, color=\"steelblue\", label=f\"Known (0-7)\\nmean={known.mean():.3f}\")\n",
" ax.hist(unknown, bins=bins, alpha=0.6, color=\"orangered\", label=f\"Unknown (8-9)\\nmean={unknown.mean():.3f}\")\n",
" ax.hist(known, bins=bins, alpha=0.6, color=\"steelblue\", label=f\"Known (0-7)\\nmean={known.mean():.3f}\")\n",
" ax.hist(unknown, bins=bins, alpha=0.6, color=\"orangered\", label=f\"Unknown (8-9)\\nmean={unknown.mean():.3f}\")\n",
" ax.set_title(title, fontsize=13)\n",
" ax.set_xlabel(\"Max softmax probability\")\n",
" ax.legend(fontsize=9)\n",
@@ -584,19 +579,19 @@
"metadata": {},
"outputs": [],
"source": [
"from sklearn.metrics import roc_curve, roc_auc_score\n",
"from sklearn.metrics import roc_auc_score, roc_curve\n",
"\n",
"fig, axes = plt.subplots(1, 3, figsize=(15, 4), sharey=True)\n",
"\n",
"model_data_thresh = [\n",
" (\"CE Baseline\", probs_baseline_known, probs_baseline_unknown),\n",
" (\"CE Baseline\", probs_baseline_known, probs_baseline_unknown),\n",
" (\"Entropic Open-Set\", probs_entropic_known, probs_entropic_unknown),\n",
" (\"Objectosphere\", probs_obj_known, probs_obj_unknown),\n",
" (\"Objectosphere\", probs_obj_known, probs_obj_unknown),\n",
"]\n",
"\n",
"for ax, (title, known, unknown) in zip(axes, model_data_thresh):\n",
" # Label: 0 = known, 1 = unknown; detector score = 1 - max_prob\n",
" y_true = np.concatenate([np.zeros(len(known)), np.ones(len(unknown))])\n",
" y_true = np.concatenate([np.zeros(len(known)), np.ones(len(unknown))])\n",
" y_score = np.concatenate([1 - known, 1 - unknown])\n",
"\n",
" fpr, tpr, thresholds = roc_curve(y_true, y_score)\n",
@@ -637,21 +632,21 @@
"from sklearn.metrics import roc_auc_score\n",
"\n",
"rows = [\n",
" (\"CE Baseline\", probs_baseline_known, probs_baseline_unknown),\n",
" (\"CE Baseline\", probs_baseline_known, probs_baseline_unknown),\n",
" (\"Entropic Open-Set\", probs_entropic_known, probs_entropic_unknown),\n",
" (\"Objectosphere\", probs_obj_known, probs_obj_unknown),\n",
" (\"Objectosphere\", probs_obj_known, probs_obj_unknown),\n",
"]\n",
"\n",
"header = f\"{'Model':<22} | {'Mean max-prob (known)':>21} | {'Mean max-prob (unknown)':>23} | {'AUC (unknown det.)':>18}\"\n",
"sep = \"-\" * len(header)\n",
"sep = \"-\" * len(header)\n",
"print(sep)\n",
"print(header)\n",
"print(sep)\n",
"\n",
"for name, known, unknown in rows:\n",
" y_true = np.concatenate([np.zeros(len(known)), np.ones(len(unknown))])\n",
" y_true = np.concatenate([np.zeros(len(known)), np.ones(len(unknown))])\n",
" y_score = np.concatenate([1 - known, 1 - unknown])\n",
" auc = roc_auc_score(y_true, y_score)\n",
" auc = roc_auc_score(y_true, y_score)\n",
" print(f\"{name:<22} | {known.mean():>21.4f} | {unknown.mean():>23.4f} | {auc:>18.4f}\")\n",
"\n",
"print(sep)\n",
@@ -662,5 +657,21 @@
"print(\" - Objectosphere: similar to entropic; also creates a logit-norm gap (not shown here)\")"
]
}
]
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -248,18 +248,18 @@ def main():
eos_unknown = results[1][2]
obj_unknown = results[2][2]
assert (
eos_unknown < ce_unknown
), f"Entropic loss should reduce unknown confidence: {eos_unknown:.3f} < {ce_unknown:.3f}"
assert (
obj_unknown < ce_unknown
), f"Objectosphere loss should reduce unknown confidence: {obj_unknown:.3f} < {ce_unknown:.3f}"
assert eos_unknown < ce_unknown, (
f"Entropic loss should reduce unknown confidence: {eos_unknown:.3f} < {ce_unknown:.3f}"
)
assert obj_unknown < ce_unknown, (
f"Objectosphere loss should reduce unknown confidence: {obj_unknown:.3f} < {ce_unknown:.3f}"
)
obj_norm_known = results[2][3]
obj_norm_unknown = results[2][4]
assert (
obj_norm_known > obj_norm_unknown * 1.5
), f"Objectosphere should create norm gap: known={obj_norm_known:.3f} unknown={obj_norm_unknown:.3f}"
assert obj_norm_known > obj_norm_unknown * 1.5, (
f"Objectosphere should create norm gap: known={obj_norm_known:.3f} unknown={obj_norm_unknown:.3f}"
)
print("\nAll assertions passed.")
+71 -55
View File
@@ -1,17 +1,4 @@
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
}
},
"cells": [
{
"cell_type": "markdown",
@@ -62,14 +49,15 @@
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"import tempfile\n",
"import time\n",
"import warnings\n",
"warnings.filterwarnings('ignore')\n",
"\n",
"import pandas as pd\n",
"warnings.filterwarnings(\"ignore\")\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.ticker as ticker\n",
"import pandas as pd\n",
"\n",
"from ludwig.api import LudwigModel"
]
@@ -92,10 +80,7 @@
"metadata": {},
"outputs": [],
"source": [
"DATA_URL = (\n",
" \"https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/\"\n",
" \"winequality-red.csv\"\n",
")\n",
"DATA_URL = \"https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv\"\n",
"\n",
"df = pd.read_csv(DATA_URL, sep=\";\")\n",
"df.columns = [c.strip().replace(\" \", \"_\") for c in df.columns]\n",
@@ -125,9 +110,17 @@
"outputs": [],
"source": [
"FEATURE_NAMES = [\n",
" \"fixed_acidity\", \"volatile_acidity\", \"citric_acid\", \"residual_sugar\",\n",
" \"chlorides\", \"free_sulfur_dioxide\", \"total_sulfur_dioxide\", \"density\",\n",
" \"pH\", \"sulphates\", \"alcohol\",\n",
" \"fixed_acidity\",\n",
" \"volatile_acidity\",\n",
" \"citric_acid\",\n",
" \"residual_sugar\",\n",
" \"chlorides\",\n",
" \"free_sulfur_dioxide\",\n",
" \"total_sulfur_dioxide\",\n",
" \"density\",\n",
" \"pH\",\n",
" \"sulphates\",\n",
" \"alcohol\",\n",
"]\n",
"\n",
"INPUT_FEATURES = [{\"name\": n, \"type\": \"number\"} for n in FEATURE_NAMES]\n",
@@ -194,12 +187,14 @@
" elapsed_adamw = time.time() - t0\n",
"\n",
"all_results[\"adamw\"] = train_stats.validation\n",
"summary.append({\n",
" \"optimizer\": \"adamw\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_adamw, 1),\n",
"})\n",
"summary.append(\n",
" {\n",
" \"optimizer\": \"adamw\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_adamw, 1),\n",
" }\n",
")\n",
"print(f\"AdamW done in {elapsed_adamw:.1f}s\")"
]
},
@@ -256,12 +251,14 @@
" elapsed_radam = time.time() - t0\n",
"\n",
"all_results[\"radam\"] = train_stats.validation\n",
"summary.append({\n",
" \"optimizer\": \"radam\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_radam, 1),\n",
"})\n",
"summary.append(\n",
" {\n",
" \"optimizer\": \"radam\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_radam, 1),\n",
" }\n",
")\n",
"print(f\"RAdam done in {elapsed_radam:.1f}s\")"
]
},
@@ -318,12 +315,14 @@
" elapsed_adafactor = time.time() - t0\n",
"\n",
"all_results[\"adafactor\"] = train_stats.validation\n",
"summary.append({\n",
" \"optimizer\": \"adafactor\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_adafactor, 1),\n",
"})\n",
"summary.append(\n",
" {\n",
" \"optimizer\": \"adafactor\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_adafactor, 1),\n",
" }\n",
")\n",
"print(f\"Adafactor done in {elapsed_adafactor:.1f}s\")"
]
},
@@ -382,12 +381,14 @@
" elapsed_sfa = time.time() - t0\n",
"\n",
"all_results[\"schedule_free_adamw\"] = train_stats.validation\n",
"summary.append({\n",
" \"optimizer\": \"schedule_free_adamw\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_sfa, 1),\n",
"})\n",
"summary.append(\n",
" {\n",
" \"optimizer\": \"schedule_free_adamw\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_sfa, 1),\n",
" }\n",
")\n",
"print(f\"Schedule-Free AdamW done in {elapsed_sfa:.1f}s\")"
]
},
@@ -445,12 +446,14 @@
" elapsed_muon = time.time() - t0\n",
"\n",
"all_results[\"muon\"] = train_stats.validation\n",
"summary.append({\n",
" \"optimizer\": \"muon\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_muon, 1),\n",
"})\n",
"summary.append(\n",
" {\n",
" \"optimizer\": \"muon\",\n",
" \"final_val_loss\": train_stats.validation[\"quality\"][\"loss\"][-1],\n",
" \"final_val_accuracy\": train_stats.validation[\"quality\"][\"accuracy\"][-1],\n",
" \"training_time_s\": round(elapsed_muon, 1),\n",
" }\n",
")\n",
"print(f\"Muon done in {elapsed_muon:.1f}s\")"
]
},
@@ -498,7 +501,7 @@
" losses = val_stats[\"quality\"][\"loss\"]\n",
" accs = val_stats[\"quality\"][\"accuracy\"]\n",
" epochs = range(1, len(losses) + 1)\n",
" color = COLORS.get(opt_name, None)\n",
" color = COLORS.get(opt_name)\n",
" axes[0].plot(epochs, losses, label=opt_name, color=color)\n",
" axes[1].plot(epochs, accs, label=opt_name, color=color)\n",
"\n",
@@ -563,5 +566,18 @@
"> internal schedule a short ramp-up."
]
}
]
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+2 -2
View File
@@ -18,7 +18,7 @@ import pandas as pd
# 1. Load and prepare data
# ---------------------------------------------------------------------------
DATA_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/" "winequality-red.csv"
DATA_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
print("Downloading wine quality data...")
df = pd.read_csv(DATA_URL, sep=";")
@@ -89,7 +89,7 @@ OPTIMIZERS = {
# 3. Train and collect results
# ---------------------------------------------------------------------------
from ludwig.api import LudwigModel # noqa: E402 (import after pip install note)
from ludwig.api import LudwigModel
results = []
@@ -44,7 +44,6 @@
"outputs": [],
"source": [
"import logging\n",
"import os\n",
"import time\n",
"\n",
"import matplotlib.pyplot as plt\n",
@@ -103,12 +102,12 @@
"from PIL import Image as PILImage\n",
"\n",
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
"axes[0].imshow(PILImage.open(pred_set['image_path'][0]))\n",
"axes[0].set_title('Input image')\n",
"axes[0].axis('off')\n",
"axes[1].imshow(PILImage.open(pred_set['mask_path'][0]))\n",
"axes[1].set_title('Ground-truth mask (32 classes)')\n",
"axes[1].axis('off')\n",
"axes[0].imshow(PILImage.open(pred_set[\"image_path\"][0]))\n",
"axes[0].set_title(\"Input image\")\n",
"axes[0].axis(\"off\")\n",
"axes[1].imshow(PILImage.open(pred_set[\"mask_path\"][0]))\n",
"axes[1].set_title(\"Ground-truth mask (32 classes)\")\n",
"axes[1].axis(\"off\")\n",
"plt.tight_layout()\n",
"plt.show()"
]
@@ -131,39 +130,39 @@
"outputs": [],
"source": [
"unet_config = {\n",
" 'input_features': [\n",
" \"input_features\": [\n",
" {\n",
" 'name': 'image_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {'num_processes': 4, 'height': 512, 'width': 512},\n",
" 'encoder': {'type': 'unet'},\n",
" \"name\": \"image_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\"num_processes\": 4, \"height\": 512, \"width\": 512},\n",
" \"encoder\": {\"type\": \"unet\"},\n",
" }\n",
" ],\n",
" 'output_features': [\n",
" \"output_features\": [\n",
" {\n",
" 'name': 'mask_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {\n",
" 'num_processes': 4,\n",
" 'height': 512,\n",
" 'width': 512,\n",
" 'num_classes': 32,\n",
" \"name\": \"mask_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\n",
" \"num_processes\": 4,\n",
" \"height\": 512,\n",
" \"width\": 512,\n",
" \"num_classes\": 32,\n",
" },\n",
" 'decoder': {\n",
" 'type': 'unet',\n",
" 'num_stages': 4, # configurable depth\n",
" 'num_fc_layers': 0,\n",
" 'conv_norm': 'batch',\n",
" \"decoder\": {\n",
" \"type\": \"unet\",\n",
" \"num_stages\": 4, # configurable depth\n",
" \"num_fc_layers\": 0,\n",
" \"conv_norm\": \"batch\",\n",
" },\n",
" 'loss': {'type': 'softmax_cross_entropy'},\n",
" \"loss\": {\"type\": \"softmax_cross_entropy\"},\n",
" }\n",
" ],\n",
" 'combiner': {'type': 'concat', 'num_fc_layers': 0},\n",
" 'trainer': {\n",
" 'epochs': 50,\n",
" 'early_stop': 10,\n",
" 'batch_size': 4,\n",
" 'learning_rate': 0.0001,\n",
" \"combiner\": {\"type\": \"concat\", \"num_fc_layers\": 0},\n",
" \"trainer\": {\n",
" \"epochs\": 50,\n",
" \"early_stop\": 10,\n",
" \"batch_size\": 4,\n",
" \"learning_rate\": 0.0001,\n",
" },\n",
"}\n",
"\n",
@@ -171,8 +170,8 @@
"unet_model = LudwigModel(unet_config, logging_level=logging.WARNING)\n",
"unet_stats, _, unet_output_dir = unet_model.train(\n",
" dataset=train_set,\n",
" experiment_name='seg_comparison',\n",
" model_name='unet',\n",
" experiment_name=\"seg_comparison\",\n",
" model_name=\"unet\",\n",
" skip_save_processed_input=True,\n",
")\n",
"unet_time = time.time() - t0\n",
@@ -188,8 +187,8 @@
"unet_preds, _ = unet_model.predict(pred_set)\n",
"if not isinstance(unet_preds, pd.DataFrame):\n",
" unet_preds = unet_preds.compute()\n",
"unet_pred_mask = torch.from_numpy(unet_preds['mask_path_predictions'].iloc[0])\n",
"print('UNet prediction mask shape:', unet_pred_mask.shape)"
"unet_pred_mask = torch.from_numpy(unet_preds[\"mask_path_predictions\"].iloc[0])\n",
"print(\"UNet prediction mask shape:\", unet_pred_mask.shape)"
]
},
{
@@ -214,43 +213,43 @@
"outputs": [],
"source": [
"segformer_config = {\n",
" 'input_features': [\n",
" \"input_features\": [\n",
" {\n",
" 'name': 'image_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {'num_processes': 4, 'height': 512, 'width': 512},\n",
" 'encoder': {\n",
" 'type': 'dinov2',\n",
" 'pretrained_model_name_or_path': 'facebook/dinov2-base',\n",
" 'use_pretrained': True,\n",
" 'trainable': True,\n",
" \"name\": \"image_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\"num_processes\": 4, \"height\": 512, \"width\": 512},\n",
" \"encoder\": {\n",
" \"type\": \"dinov2\",\n",
" \"pretrained_model_name_or_path\": \"facebook/dinov2-base\",\n",
" \"use_pretrained\": True,\n",
" \"trainable\": True,\n",
" },\n",
" }\n",
" ],\n",
" 'output_features': [\n",
" \"output_features\": [\n",
" {\n",
" 'name': 'mask_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {\n",
" 'num_processes': 4,\n",
" 'height': 512,\n",
" 'width': 512,\n",
" 'num_classes': 32,\n",
" \"name\": \"mask_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\n",
" \"num_processes\": 4,\n",
" \"height\": 512,\n",
" \"width\": 512,\n",
" \"num_classes\": 32,\n",
" },\n",
" 'decoder': {\n",
" 'type': 'segformer',\n",
" 'hidden_size': 256,\n",
" 'dropout': 0.1,\n",
" \"decoder\": {\n",
" \"type\": \"segformer\",\n",
" \"hidden_size\": 256,\n",
" \"dropout\": 0.1,\n",
" },\n",
" 'loss': {'type': 'softmax_cross_entropy'},\n",
" \"loss\": {\"type\": \"softmax_cross_entropy\"},\n",
" }\n",
" ],\n",
" 'combiner': {'type': 'concat', 'num_fc_layers': 0},\n",
" 'trainer': {\n",
" 'epochs': 50,\n",
" 'early_stop': 10,\n",
" 'batch_size': 4,\n",
" 'learning_rate': 0.0001,\n",
" \"combiner\": {\"type\": \"concat\", \"num_fc_layers\": 0},\n",
" \"trainer\": {\n",
" \"epochs\": 50,\n",
" \"early_stop\": 10,\n",
" \"batch_size\": 4,\n",
" \"learning_rate\": 0.0001,\n",
" },\n",
"}\n",
"\n",
@@ -258,8 +257,8 @@
"segformer_model = LudwigModel(segformer_config, logging_level=logging.WARNING)\n",
"segformer_stats, _, segformer_output_dir = segformer_model.train(\n",
" dataset=train_set,\n",
" experiment_name='seg_comparison',\n",
" model_name='segformer',\n",
" experiment_name=\"seg_comparison\",\n",
" model_name=\"segformer\",\n",
" skip_save_processed_input=True,\n",
")\n",
"segformer_time = time.time() - t0\n",
@@ -275,8 +274,8 @@
"segformer_preds, _ = segformer_model.predict(pred_set)\n",
"if not isinstance(segformer_preds, pd.DataFrame):\n",
" segformer_preds = segformer_preds.compute()\n",
"segformer_pred_mask = torch.from_numpy(segformer_preds['mask_path_predictions'].iloc[0])\n",
"print('SegFormer prediction mask shape:', segformer_pred_mask.shape)"
"segformer_pred_mask = torch.from_numpy(segformer_preds[\"mask_path_predictions\"].iloc[0])\n",
"print(\"SegFormer prediction mask shape:\", segformer_pred_mask.shape)"
]
},
{
@@ -301,42 +300,42 @@
"outputs": [],
"source": [
"fpn_config = {\n",
" 'input_features': [\n",
" \"input_features\": [\n",
" {\n",
" 'name': 'image_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {'num_processes': 4, 'height': 512, 'width': 512},\n",
" 'encoder': {\n",
" 'type': 'efficientnet',\n",
" 'use_pretrained': True,\n",
" 'trainable': True,\n",
" \"name\": \"image_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\"num_processes\": 4, \"height\": 512, \"width\": 512},\n",
" \"encoder\": {\n",
" \"type\": \"efficientnet\",\n",
" \"use_pretrained\": True,\n",
" \"trainable\": True,\n",
" },\n",
" }\n",
" ],\n",
" 'output_features': [\n",
" \"output_features\": [\n",
" {\n",
" 'name': 'mask_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {\n",
" 'num_processes': 4,\n",
" 'height': 512,\n",
" 'width': 512,\n",
" 'num_classes': 32,\n",
" \"name\": \"mask_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\n",
" \"num_processes\": 4,\n",
" \"height\": 512,\n",
" \"width\": 512,\n",
" \"num_classes\": 32,\n",
" },\n",
" 'decoder': {\n",
" 'type': 'fpn',\n",
" 'num_channels': 256,\n",
" 'num_levels': 4,\n",
" \"decoder\": {\n",
" \"type\": \"fpn\",\n",
" \"num_channels\": 256,\n",
" \"num_levels\": 4,\n",
" },\n",
" 'loss': {'type': 'softmax_cross_entropy'},\n",
" \"loss\": {\"type\": \"softmax_cross_entropy\"},\n",
" }\n",
" ],\n",
" 'combiner': {'type': 'concat', 'num_fc_layers': 0},\n",
" 'trainer': {\n",
" 'epochs': 100,\n",
" 'early_stop': 10,\n",
" 'batch_size': 8,\n",
" 'learning_rate': 0.0001,\n",
" \"combiner\": {\"type\": \"concat\", \"num_fc_layers\": 0},\n",
" \"trainer\": {\n",
" \"epochs\": 100,\n",
" \"early_stop\": 10,\n",
" \"batch_size\": 8,\n",
" \"learning_rate\": 0.0001,\n",
" },\n",
"}\n",
"\n",
@@ -344,8 +343,8 @@
"fpn_model = LudwigModel(fpn_config, logging_level=logging.WARNING)\n",
"fpn_stats, _, fpn_output_dir = fpn_model.train(\n",
" dataset=train_set,\n",
" experiment_name='seg_comparison',\n",
" model_name='fpn',\n",
" experiment_name=\"seg_comparison\",\n",
" model_name=\"fpn\",\n",
" skip_save_processed_input=True,\n",
")\n",
"fpn_time = time.time() - t0\n",
@@ -361,8 +360,8 @@
"fpn_preds, _ = fpn_model.predict(pred_set)\n",
"if not isinstance(fpn_preds, pd.DataFrame):\n",
" fpn_preds = fpn_preds.compute()\n",
"fpn_pred_mask = torch.from_numpy(fpn_preds['mask_path_predictions'].iloc[0])\n",
"print('FPN prediction mask shape:', fpn_pred_mask.shape)"
"fpn_pred_mask = torch.from_numpy(fpn_preds[\"mask_path_predictions\"].iloc[0])\n",
"print(\"FPN prediction mask shape:\", fpn_pred_mask.shape)"
]
},
{
@@ -394,39 +393,39 @@
"import yaml\n",
"\n",
"SWEEP_BASE = {\n",
" 'input_features': [\n",
" \"input_features\": [\n",
" {\n",
" 'name': 'image_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {'num_processes': 4, 'height': 512, 'width': 512},\n",
" 'encoder': {'type': 'unet'},\n",
" \"name\": \"image_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\"num_processes\": 4, \"height\": 512, \"width\": 512},\n",
" \"encoder\": {\"type\": \"unet\"},\n",
" }\n",
" ],\n",
" 'output_features': [\n",
" \"output_features\": [\n",
" {\n",
" 'name': 'mask_path',\n",
" 'type': 'image',\n",
" 'preprocessing': {'num_processes': 4, 'height': 512, 'width': 512, 'num_classes': 32},\n",
" 'decoder': {'type': 'unet', 'num_fc_layers': 0, 'conv_norm': 'batch'},\n",
" 'loss': {'type': 'softmax_cross_entropy'},\n",
" \"name\": \"mask_path\",\n",
" \"type\": \"image\",\n",
" \"preprocessing\": {\"num_processes\": 4, \"height\": 512, \"width\": 512, \"num_classes\": 32},\n",
" \"decoder\": {\"type\": \"unet\", \"num_fc_layers\": 0, \"conv_norm\": \"batch\"},\n",
" \"loss\": {\"type\": \"softmax_cross_entropy\"},\n",
" }\n",
" ],\n",
" 'combiner': {'type': 'concat', 'num_fc_layers': 0},\n",
" 'trainer': {'epochs': 20, 'early_stop': 5, 'batch_size': 4, 'learning_rate': 0.0001},\n",
" \"combiner\": {\"type\": \"concat\", \"num_fc_layers\": 0},\n",
" \"trainer\": {\"epochs\": 20, \"early_stop\": 5, \"batch_size\": 4, \"learning_rate\": 0.0001},\n",
"}\n",
"\n",
"sweep_results = []\n",
"\n",
"for depth in [2, 3, 4, 5]:\n",
" cfg = yaml.safe_load(yaml.dump(SWEEP_BASE))\n",
" cfg['output_features'][0]['decoder']['num_stages'] = depth\n",
" cfg[\"output_features\"][0][\"decoder\"][\"num_stages\"] = depth\n",
"\n",
" m = LudwigModel(cfg, logging_level=logging.WARNING)\n",
" t0 = time.time()\n",
" stats, _, _ = m.train(\n",
" dataset=train_set,\n",
" experiment_name='depth_sweep',\n",
" model_name=f'unet_depth_{depth}',\n",
" experiment_name=\"depth_sweep\",\n",
" model_name=f\"unet_depth_{depth}\",\n",
" skip_save_processed_input=True,\n",
" )\n",
" elapsed = time.time() - t0\n",
@@ -435,16 +434,18 @@
"\n",
" val_loss = None\n",
" try:\n",
" val_loss = min(stats['validation']['combined']['loss'])\n",
" val_loss = min(stats[\"validation\"][\"combined\"][\"loss\"])\n",
" except (KeyError, TypeError):\n",
" pass\n",
"\n",
" sweep_results.append({\n",
" 'num_stages': depth,\n",
" 'trainable_params': f'{n_params:,}',\n",
" 'best_val_loss': round(val_loss, 4) if val_loss is not None else 'n/a',\n",
" 'training_time_s': round(elapsed, 1),\n",
" })\n",
" sweep_results.append(\n",
" {\n",
" \"num_stages\": depth,\n",
" \"trainable_params\": f\"{n_params:,}\",\n",
" \"best_val_loss\": round(val_loss, 4) if val_loss is not None else \"n/a\",\n",
" \"training_time_s\": round(elapsed, 1),\n",
" }\n",
" )\n",
" print(f\"depth={depth} params={n_params:,} val_loss={val_loss} time={elapsed:.1f}s\")\n",
"\n",
"sweep_df = pd.DataFrame(sweep_results)\n",
@@ -458,23 +459,23 @@
"outputs": [],
"source": [
"fig, ax1 = plt.subplots(figsize=(8, 4))\n",
"depths = [r['num_stages'] for r in sweep_results]\n",
"times = [r['training_time_s'] for r in sweep_results]\n",
"depths = [r[\"num_stages\"] for r in sweep_results]\n",
"times = [r[\"training_time_s\"] for r in sweep_results]\n",
"\n",
"ax1.bar(depths, times, color='steelblue', alpha=0.7, label='Training time (s)')\n",
"ax1.set_xlabel('UNet num_stages')\n",
"ax1.set_ylabel('Training time (s)', color='steelblue')\n",
"ax1.tick_params(axis='y', labelcolor='steelblue')\n",
"ax1.bar(depths, times, color=\"steelblue\", alpha=0.7, label=\"Training time (s)\")\n",
"ax1.set_xlabel(\"UNet num_stages\")\n",
"ax1.set_ylabel(\"Training time (s)\", color=\"steelblue\")\n",
"ax1.tick_params(axis=\"y\", labelcolor=\"steelblue\")\n",
"ax1.set_xticks(depths)\n",
"\n",
"val_losses = [r['best_val_loss'] for r in sweep_results if isinstance(r['best_val_loss'], float)]\n",
"val_losses = [r[\"best_val_loss\"] for r in sweep_results if isinstance(r[\"best_val_loss\"], float)]\n",
"if len(val_losses) == len(depths):\n",
" ax2 = ax1.twinx()\n",
" ax2.plot(depths, val_losses, 'o-', color='tomato', label='Best val loss')\n",
" ax2.set_ylabel('Best val loss', color='tomato')\n",
" ax2.tick_params(axis='y', labelcolor='tomato')\n",
" ax2.plot(depths, val_losses, \"o-\", color=\"tomato\", label=\"Best val loss\")\n",
" ax2.set_ylabel(\"Best val loss\", color=\"tomato\")\n",
" ax2.tick_params(axis=\"y\", labelcolor=\"tomato\")\n",
"\n",
"plt.title('UNet depth: training time vs validation loss')\n",
"plt.title(\"UNet depth: training time vs validation loss\")\n",
"plt.tight_layout()\n",
"plt.show()"
]
@@ -505,30 +506,30 @@
" return arr\n",
"\n",
"\n",
"input_img = PILImage.open(pred_set['image_path'][0])\n",
"gt_mask = PILImage.open(pred_set['mask_path'][0])\n",
"input_img = PILImage.open(pred_set[\"image_path\"][0])\n",
"gt_mask = PILImage.open(pred_set[\"mask_path\"][0])\n",
"\n",
"fig, axes = plt.subplots(1, 5, figsize=(22, 5))\n",
"\n",
"axes[0].imshow(input_img)\n",
"axes[0].set_title('Input image')\n",
"axes[0].set_title(\"Input image\")\n",
"\n",
"axes[1].imshow(gt_mask)\n",
"axes[1].set_title('Ground truth')\n",
"axes[1].set_title(\"Ground truth\")\n",
"\n",
"axes[2].imshow(to_rgb(unet_pred_mask), cmap='tab20')\n",
"axes[2].set_title(f'UNet (depth=4)')\n",
"axes[2].imshow(to_rgb(unet_pred_mask), cmap=\"tab20\")\n",
"axes[2].set_title(\"UNet (depth=4)\")\n",
"\n",
"axes[3].imshow(to_rgb(segformer_pred_mask), cmap='tab20')\n",
"axes[3].set_title('SegFormer + DINOv2')\n",
"axes[3].imshow(to_rgb(segformer_pred_mask), cmap=\"tab20\")\n",
"axes[3].set_title(\"SegFormer + DINOv2\")\n",
"\n",
"axes[4].imshow(to_rgb(fpn_pred_mask), cmap='tab20')\n",
"axes[4].set_title('FPN + EfficientNet')\n",
"axes[4].imshow(to_rgb(fpn_pred_mask), cmap=\"tab20\")\n",
"axes[4].set_title(\"FPN + EfficientNet\")\n",
"\n",
"for ax in axes:\n",
" ax.axis('off')\n",
" ax.axis(\"off\")\n",
"\n",
"plt.suptitle('Segmentation map comparison', fontsize=14)\n",
"plt.suptitle(\"Segmentation map comparison\", fontsize=14)\n",
"plt.tight_layout()\n",
"plt.show()"
]
@@ -540,11 +541,23 @@
"outputs": [],
"source": [
"# Print a summary comparison table\n",
"comparison = pd.DataFrame([\n",
" {'model': 'UNet (num_stages=4)', 'encoder': 'unet', 'decoder': 'unet', 'training_time_s': round(unet_time, 1)},\n",
" {'model': 'SegFormer + DINOv2', 'encoder': 'dinov2', 'decoder': 'segformer', 'training_time_s': round(segformer_time, 1)},\n",
" {'model': 'FPN + EfficientNet', 'encoder': 'efficientnet', 'decoder': 'fpn', 'training_time_s': round(fpn_time, 1)},\n",
"])\n",
"comparison = pd.DataFrame(\n",
" [\n",
" {\"model\": \"UNet (num_stages=4)\", \"encoder\": \"unet\", \"decoder\": \"unet\", \"training_time_s\": round(unet_time, 1)},\n",
" {\n",
" \"model\": \"SegFormer + DINOv2\",\n",
" \"encoder\": \"dinov2\",\n",
" \"decoder\": \"segformer\",\n",
" \"training_time_s\": round(segformer_time, 1),\n",
" },\n",
" {\n",
" \"model\": \"FPN + EfficientNet\",\n",
" \"encoder\": \"efficientnet\",\n",
" \"decoder\": \"fpn\",\n",
" \"training_time_s\": round(fpn_time, 1),\n",
" },\n",
" ]\n",
")\n",
"comparison"
]
}
@@ -70,9 +70,9 @@ def run_sweep():
results = []
for depth in DEPTHS:
print(f"\n{"=" * 60}")
print(f"\n{'=' * 60}")
print(f" Training UNet with num_stages={depth}")
print(f"{"=" * 60}")
print(f"{'=' * 60}")
config = yaml.safe_load(yaml.dump(BASE_CONFIG)) # deep copy via yaml round-trip
config["output_features"][0]["decoder"]["num_stages"] = depth
@@ -108,7 +108,7 @@ def run_sweep():
}
)
print(f" num_stages={depth} params={n_params:,} " f"best_val_loss={val_loss} time={elapsed:.1f}s")
print(f" num_stages={depth} params={n_params:,} best_val_loss={val_loss} time={elapsed:.1f}s")
# ── summary table ─────────────────────────────────────────────────────────
print("\n\nDepth sweep summary")
+6 -4
View File
@@ -69,6 +69,7 @@
"#\n",
"# Give the server a few seconds to start before running subsequent cells.\n",
"import time\n",
"\n",
"SERVER_URL = \"http://localhost:8000\"\n",
"print(f\"Server URL: {SERVER_URL}\")"
]
@@ -99,7 +100,6 @@
"outputs": [],
"source": [
"import json\n",
"import time\n",
"\n",
"import requests\n",
"\n",
@@ -160,7 +160,7 @@
"response.raise_for_status()\n",
"result = response.json()\n",
"\n",
"print(f\"{len(prompts)} examples in {elapsed:.2f} s ({len(prompts)/elapsed:.1f} examples/s)\")\n",
"print(f\"{len(prompts)} examples in {elapsed:.2f} s ({len(prompts) / elapsed:.1f} examples/s)\")\n",
"print(\"Columns:\", result[\"columns\"])\n",
"for i, row in enumerate(result[\"data\"]):\n",
" print(f\" [{i}] {row[0][:120]}\")"
@@ -252,7 +252,7 @@
"import statistics\n",
"\n",
"DEFAULT_URL = \"http://localhost:8001\" # default FastAPI backend\n",
"VLLM_URL = \"http://localhost:8000\" # vLLM backend\n",
"VLLM_URL = \"http://localhost:8000\" # vLLM backend\n",
"\n",
"BENCH_PROMPTS = [\n",
" \"What is machine learning?\",\n",
@@ -288,7 +288,9 @@
" p50 = statistics.median(latencies)\n",
" p95 = sorted(latencies)[int(0.95 * len(latencies))]\n",
" mean = statistics.mean(latencies)\n",
" print(f\"{label}: mean={mean*1000:.0f} ms p50={p50*1000:.0f} ms p95={p95*1000:.0f} ms ({len(latencies)} requests)\")\n",
" print(\n",
" f\"{label}: mean={mean * 1000:.0f} ms p50={p50 * 1000:.0f} ms p95={p95 * 1000:.0f} ms ({len(latencies)} requests)\"\n",
" )\n",
"\n",
"\n",
"print(\"Benchmarking default backend (port 8001)...\")\n",
+38 -39
View File
@@ -31,13 +31,14 @@
},
"outputs": [],
"source": [
"from ludwig.utils.data_utils import load_json\n",
"from ludwig.visualize import learning_curves\n",
"import pandas as pd\n",
"import numpy as np\n",
"import os.path\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns"
"import pandas as pd\n",
"import seaborn as sns\n",
"\n",
"from ludwig.utils.data_utils import load_json\n",
"from ludwig.visualize import learning_curves"
]
},
{
@@ -80,18 +81,17 @@
"list_of_stats = []\n",
"list_of_models = []\n",
"\n",
"for model in ['model1', 'model2']:\n",
" experiment_model_dir = './results/multiple_experiment_' + model \n",
" train_stats = load_json(os.path.join(experiment_model_dir,'training_statistics.json'))\n",
"for model in [\"model1\", \"model2\"]:\n",
" experiment_model_dir = \"./results/multiple_experiment_\" + model\n",
" train_stats = load_json(os.path.join(experiment_model_dir, \"training_statistics.json\"))\n",
" list_of_stats.append(train_stats)\n",
" list_of_models.append(model)\n",
" \n",
"\n",
"\n",
"# generating learning curves from training\n",
"learning_curves(list_of_stats, 'Survived',\n",
" model_names=list_of_models,\n",
" output_directory='./visualizations',\n",
" file_format='png')\n"
"learning_curves(\n",
" list_of_stats, \"Survived\", model_names=list_of_models, output_directory=\"./visualizations\", file_format=\"png\"\n",
")"
]
},
{
@@ -116,18 +116,19 @@
"# Returns: pandas dataframe containing the performance metric and loss\n",
"#\n",
"\n",
"\n",
"def extract_training_stats(experiment_model_dir):\n",
" list_of_splits = ['training', 'validation', 'test']\n",
" list_of_splits = [\"training\", \"validation\", \"test\"]\n",
" list_of_df = []\n",
" for split in list_of_splits:\n",
" train_stats = load_json(os.path.join(experiment_model_dir,'training_statistics.json'))\n",
" df = pd.DataFrame(train_stats[split]['combined'])\n",
" df.columns = [split + '_' + c for c in df.columns]\n",
" train_stats = load_json(os.path.join(experiment_model_dir, \"training_statistics.json\"))\n",
" df = pd.DataFrame(train_stats[split][\"combined\"])\n",
" df.columns = [split + \"_\" + c for c in df.columns]\n",
" list_of_df.append(df)\n",
" \n",
"\n",
" df = pd.concat(list_of_df, axis=1)\n",
" df['epoch'] = df.index + 1\n",
" \n",
" df[\"epoch\"] = df.index + 1\n",
"\n",
" return df"
]
},
@@ -148,10 +149,10 @@
},
"outputs": [],
"source": [
"model1 = extract_training_stats('./results/multiple_experiment_model1')\n",
"model1.name = 'model1'\n",
"model2 = extract_training_stats('./results/multiple_experiment_model2')\n",
"model2.name = 'model2'"
"model1 = extract_training_stats(\"./results/multiple_experiment_model1\")\n",
"model1.name = \"model1\"\n",
"model2 = extract_training_stats(\"./results/multiple_experiment_model2\")\n",
"model2.name = \"model2\""
]
},
{
@@ -263,20 +264,21 @@
"#\n",
"# Returns: plot ready pandas dataframe\n",
"\n",
"\n",
"def create_plot_ready_data(list_of_train_stats_df):\n",
" # holding ready for plot ready data\n",
" plot_ready_list = []\n",
" \n",
"\n",
" # consolidate the multiple training statistics dataframes\n",
" for df in list_of_train_stats_df:\n",
" for col in ['training', 'validation']:\n",
" df2 = df[['epoch', col + '_loss']].copy()\n",
" df2.columns = ['epoch', 'loss']\n",
" df2['type'] = col\n",
" df2['model'] = df.name\n",
" for col in [\"training\", \"validation\"]:\n",
" df2 = df[[\"epoch\", col + \"_loss\"]].copy()\n",
" df2.columns = [\"epoch\", \"loss\"]\n",
" df2[\"type\"] = col\n",
" df2[\"model\"] = df.name\n",
" plot_ready_list.append(df2)\n",
"\n",
" return pd.concat(plot_ready_list, axis=0, ignore_index=True)\n"
" return pd.concat(plot_ready_list, axis=0, ignore_index=True)"
]
},
{
@@ -324,13 +326,10 @@
],
"source": [
"# Plot learning curves for the different models\n",
"fig = plt.figure(figsize=(10,6))\n",
"sns.set_style(style='dark')\n",
"ax = sns.lineplot(x='epoch', y='loss',\n",
" style='type',\n",
" hue='model',\n",
" data=learning_curves)\n",
"ax.set_title('Learning Curves', fontdict={'fontsize': 16})"
"fig = plt.figure(figsize=(10, 6))\n",
"sns.set_style(style=\"dark\")\n",
"ax = sns.lineplot(x=\"epoch\", y=\"loss\", style=\"type\", hue=\"model\", data=learning_curves)\n",
"ax.set_title(\"Learning Curves\", fontdict={\"fontsize\": 16})"
]
},
{
@@ -339,7 +338,7 @@
"metadata": {},
"outputs": [],
"source": [
"fig.savefig('./visualizations/custom_learning_curve.png')"
"fig.savefig(\"./visualizations/custom_learning_curve.png\")"
]
},
{
+1 -1
View File
@@ -28,7 +28,7 @@ logging.basicConfig(level=logging.WARNING)
# Dataset
# ---------------------------------------------------------------------------
WINE_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/" "wine-quality/winequality-red.csv"
WINE_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
WINE_FEATURES = [
"fixed_acidity",
+26 -29
View File
@@ -49,7 +49,6 @@
"outputs": [],
"source": [
"import logging\n",
"import os\n",
"import shutil\n",
"import warnings\n",
"\n",
@@ -59,10 +58,7 @@
"\n",
"warnings.filterwarnings(\"ignore\")\n",
"\n",
"WINE_URL = (\n",
" \"https://archive.ics.uci.edu/ml/machine-learning-databases/\"\n",
" \"wine-quality/winequality-red.csv\"\n",
")\n",
"WINE_URL = \"https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv\"\n",
"\n",
"df = pd.read_csv(WINE_URL, sep=\";\")\n",
"df.columns = [c.replace(\" \", \"_\") for c in df.columns]\n",
@@ -148,16 +144,23 @@
"from ludwig.api import LudwigModel\n",
"\n",
"WINE_FEATURES = [\n",
" \"fixed_acidity\", \"volatile_acidity\", \"citric_acid\", \"residual_sugar\",\n",
" \"chlorides\", \"free_sulfur_dioxide\", \"total_sulfur_dioxide\",\n",
" \"density\", \"pH\", \"sulphates\", \"alcohol\",\n",
" \"fixed_acidity\",\n",
" \"volatile_acidity\",\n",
" \"citric_acid\",\n",
" \"residual_sugar\",\n",
" \"chlorides\",\n",
" \"free_sulfur_dioxide\",\n",
" \"total_sulfur_dioxide\",\n",
" \"density\",\n",
" \"pH\",\n",
" \"sulphates\",\n",
" \"alcohol\",\n",
"]\n",
"\n",
"\n",
"def make_input_features():\n",
" return [\n",
" {\"name\": f, \"type\": \"number\", \"preprocessing\": {\"normalization\": \"zscore\"}}\n",
" for f in WINE_FEATURES\n",
" ]\n",
" return [{\"name\": f, \"type\": \"number\", \"preprocessing\": {\"normalization\": \"zscore\"}} for f in WINE_FEATURES]\n",
"\n",
"\n",
"baseline_config = {\n",
" \"model_type\": \"ecd\",\n",
@@ -275,6 +278,7 @@
"\n",
"print(\"Calibrated decoder config:\")\n",
"import json\n",
"\n",
"print(json.dumps(calibrated_config[\"output_features\"][0][\"decoder\"], indent=2))"
]
},
@@ -352,10 +356,12 @@
"outputs": [],
"source": [
"mc_config = copy.deepcopy(baseline_config)\n",
"mc_config[\"output_features\"][0][\"decoder\"].update({\n",
" \"dropout\": 0.3, # higher dropout → more variance across MC passes\n",
" \"mc_dropout_samples\": 20, # run 20 stochastic forward passes at inference\n",
"})\n",
"mc_config[\"output_features\"][0][\"decoder\"].update(\n",
" {\n",
" \"dropout\": 0.3, # higher dropout → more variance across MC passes\n",
" \"mc_dropout_samples\": 20, # run 20 stochastic forward passes at inference\n",
" }\n",
")\n",
"mc_config[\"combiner\"][\"dropout\"] = 0.2 # combiner dropout also contributes to variance\n",
"\n",
"print(\"MC Dropout config:\")\n",
@@ -418,10 +424,7 @@
" ax1.set_title(\"MC Dropout Uncertainty Distribution\")\n",
"\n",
" # Uncertainty vs predicted probability\n",
" scatter = ax2.scatter(\n",
" mc_probs, uncertainty, c=true_labels, cmap=\"RdYlGn\",\n",
" alpha=0.4, s=10, vmin=0, vmax=1\n",
" )\n",
" scatter = ax2.scatter(mc_probs, uncertainty, c=true_labels, cmap=\"RdYlGn\", alpha=0.4, s=10, vmin=0, vmax=1)\n",
" plt.colorbar(scatter, ax=ax2, label=\"True label\")\n",
" ax2.set_xlabel(\"Mean predicted probability\")\n",
" ax2.set_ylabel(\"Uncertainty\")\n",
@@ -476,15 +479,9 @@
"metadata": {},
"outputs": [],
"source": [
"baseline_acc = (\n",
" baseline_preds[\"quality_predictions\"].astype(bool) == true_labels.astype(bool)\n",
").mean()\n",
"calibrated_acc = (\n",
" calibrated_preds[\"quality_predictions\"].astype(bool) == true_labels.astype(bool)\n",
").mean()\n",
"mc_acc = (\n",
" mc_preds[\"quality_predictions\"].astype(bool) == true_labels.astype(bool)\n",
").mean()\n",
"baseline_acc = (baseline_preds[\"quality_predictions\"].astype(bool) == true_labels.astype(bool)).mean()\n",
"calibrated_acc = (calibrated_preds[\"quality_predictions\"].astype(bool) == true_labels.astype(bool)).mean()\n",
"mc_acc = (mc_preds[\"quality_predictions\"].astype(bool) == true_labels.astype(bool)).mean()\n",
"\n",
"mc_ece = expected_calibration_error(mc_preds[\"quality_probability_True\"].values, true_labels)\n",
"\n",
@@ -6,14 +6,12 @@
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd \n",
"import numpy as np\n",
"\n",
"import logging\n",
"import os\n",
"\n",
"import shutil\n",
"from pprint import pprint\n",
"import logging\n",
"\n",
"import pandas as pd\n",
"\n",
"from ludwig.api import LudwigModel"
]
@@ -31,8 +29,8 @@
"metadata": {},
"outputs": [],
"source": [
"train_df = pd.read_csv('./data/winequalityN.csv')\n",
"train_df['quality'] = train_df['quality'].apply(str)\n",
"train_df = pd.read_csv(\"./data/winequalityN.csv\")\n",
"train_df[\"quality\"] = train_df[\"quality\"].apply(str)\n",
"train_df.shape"
]
},
@@ -45,8 +43,8 @@
"# Replace white space in column names with underscore\n",
"new_col = []\n",
"for i in range(len(train_df.columns)):\n",
" new_col.append(train_df.columns[i].replace(' ', '_'))\n",
" \n",
" new_col.append(train_df.columns[i].replace(\" \", \"_\"))\n",
"\n",
"train_df.columns = new_col"
]
},
@@ -83,7 +81,7 @@
"metadata": {},
"outputs": [],
"source": [
"train_df['quality'].value_counts().sort_index()"
"train_df[\"quality\"].value_counts().sort_index()"
]
},
{
@@ -92,16 +90,16 @@
"metadata": {},
"outputs": [],
"source": [
"cols = list(set(train_df.columns) - set(['quality']))\n",
"cols = list(set(train_df.columns) - set([\"quality\"]))\n",
"features = train_df[cols]\n",
"\n",
"#extract categorical features\n",
"# extract categorical features\n",
"categorical_features = []\n",
"for p in features:\n",
" if train_df[p].dtype == 'object':\n",
" if train_df[p].dtype == \"object\":\n",
" categorical_features.append(p)\n",
" \n",
"print(\"categorical features:\", categorical_features, '\\n')\n",
"\n",
"print(\"categorical features:\", categorical_features, \"\\n\")\n",
"\n",
"# get numerical features\n",
"numerical_features = list(set(features) - set(categorical_features))\n",
@@ -133,54 +131,32 @@
"outputs": [],
"source": [
"# template for config\n",
"config = {'input_features':[], 'output_features': [], 'trainer':{}}\n",
"config = {\"input_features\": [], \"output_features\": [], \"trainer\": {}}\n",
"\n",
"# setup input features for categorical features\n",
"for p in categorical_features:\n",
" a_feature = {\n",
" 'name': p.replace(' ','_'), \n",
" 'type': 'category'\n",
" }\n",
" config['input_features'].append(a_feature)\n",
" a_feature = {\"name\": p.replace(\" \", \"_\"), \"type\": \"category\"}\n",
" config[\"input_features\"].append(a_feature)\n",
"\n",
"# setup input features for numerical features\n",
"for p in numerical_features:\n",
" a_feature = {\n",
" 'name': p.replace(' ', '_'), \n",
" 'type': 'number'\n",
" }\n",
" config['input_features'].append(a_feature)\n",
" a_feature = {\"name\": p.replace(\" \", \"_\"), \"type\": \"number\"}\n",
" config[\"input_features\"].append(a_feature)\n",
"\n",
"# set up output variable\n",
"config['output_features'].append({'name': 'quality', 'type':'category'})\n",
"config[\"output_features\"].append({\"name\": \"quality\", \"type\": \"category\"})\n",
"\n",
"# set default preprocessing and encoder for numerical features\n",
"config['defaults'] = {\n",
" 'number': {\n",
" 'preprocessing': {\n",
" 'missing_value_strategy': 'fill_with_mean', \n",
" 'normalization': 'zscore'\n",
" },\n",
" 'encoder': {\n",
" 'type': 'dense',\n",
" 'num_layers': 2\n",
" },\n",
"config[\"defaults\"] = {\n",
" \"number\": {\n",
" \"preprocessing\": {\"missing_value_strategy\": \"fill_with_mean\", \"normalization\": \"zscore\"},\n",
" \"encoder\": {\"type\": \"dense\", \"num_layers\": 2},\n",
" },\n",
" 'category': {\n",
" 'encoder': {\n",
" 'type': 'sparse'\n",
" },\n",
" 'decoder': {\n",
" 'top_k': 2\n",
" },\n",
" 'loss': {\n",
" 'confidence_penalty': 0.1 \n",
" }\n",
" }\n",
" \"category\": {\"encoder\": {\"type\": \"sparse\"}, \"decoder\": {\"top_k\": 2}, \"loss\": {\"confidence_penalty\": 0.1}},\n",
"}\n",
"\n",
"# set up trainer\n",
"config['trainer'] = {'epochs': 5}"
"config[\"trainer\"] = {\"epochs\": 5}"
]
},
{
@@ -205,7 +181,7 @@
"metadata": {},
"outputs": [],
"source": [
"model = LudwigModel(config, backend = 'local', logging_level = logging.INFO)"
"model = LudwigModel(config, backend=\"local\", logging_level=logging.INFO)"
]
},
{
@@ -221,7 +197,7 @@
"metadata": {},
"outputs": [],
"source": [
"pprint(model.config['input_features'], indent=2)"
"pprint(model.config[\"input_features\"], indent=2)"
]
},
{
@@ -230,7 +206,7 @@
"metadata": {},
"outputs": [],
"source": [
"pprint(model.config['output_features'], indent=2)"
"pprint(model.config[\"output_features\"], indent=2)"
]
},
{
@@ -239,10 +215,7 @@
"metadata": {},
"outputs": [],
"source": [
"eval_stats, train_stats, _, _ = model.experiment(\n",
" dataset = train_df,\n",
" experiment_name = 'wine_quality'\n",
")"
"eval_stats, train_stats, _, _ = model.experiment(dataset=train_df, experiment_name=\"wine_quality\")"
]
},
{
@@ -259,13 +232,13 @@
"outputs": [],
"source": [
"try:\n",
" shutil.rmtree('./results')\n",
" items = os.listdir('./')\n",
" shutil.rmtree(\"./results\")\n",
" items = os.listdir(\"./\")\n",
" for item in items:\n",
" if item.endswith(\".hdf5\") or item.endswith(\".json\") or item == '.lock_preprocessing':\n",
" os.remove(os.path.join('./', item))\n",
"except Exception as e:\n",
" pass "
" if item.endswith(\".hdf5\") or item.endswith(\".json\") or item == \".lock_preprocessing\":\n",
" os.remove(os.path.join(\"./\", item))\n",
"except Exception:\n",
" pass"
]
}
],
+9 -9
View File
@@ -120,7 +120,7 @@ logger = logging.getLogger(__name__)
@PublicAPI
@dataclass
class EvaluationFrequency: # noqa F821
class EvaluationFrequency:
"""Represents the frequency of periodic evaluation of a metric during training. For example:
"every epoch"
@@ -169,7 +169,7 @@ class TrainingStats:
return self._KEYS
def __iter__(self):
return iter(self._KEYS) # noqa: F811
return iter(self._KEYS)
@PublicAPI
@@ -1155,7 +1155,7 @@ class LudwigModel:
def predict(
self,
dataset: str | dict | pd.DataFrame | None = None,
data_format: str = None,
data_format: str | None = None,
split: str = FULL,
batch_size: int = 128,
generation_config: dict | None = None,
@@ -1698,7 +1698,7 @@ class LudwigModel:
elif eval_split == TEST:
eval_set = preprocessed_data.test_set
else:
logger.warning(f"Eval split {eval_split} not supported. " f"Using validation set instead")
logger.warning(f"Eval split {eval_split} not supported. Using validation set instead")
if eval_set is not None:
trainer_dict = self.config_obj.trainer.to_dict()
@@ -1726,12 +1726,12 @@ class LudwigModel:
)
eval_stats = None
else:
logger.warning(f"The evaluation set {eval_set} was not provided. " f"Skipping evaluation")
logger.warning(f"The evaluation set {eval_set} was not provided. Skipping evaluation")
eval_stats = None
return eval_stats, train_stats, preprocessed_data, output_directory
def collect_weights(self, tensor_names: list[str] = None, **kwargs) -> list:
def collect_weights(self, tensor_names: list[str] | None = None, **kwargs) -> list:
"""Load a pre-trained model and collect the tensors with a specific name.
# Inputs
@@ -2139,7 +2139,7 @@ class LudwigModel:
model_hyperparameters_path = os.path.join(save_path, MODEL_HYPERPARAMETERS_FILE_NAME)
save_json(model_hyperparameters_path, self.config_obj.to_dict())
def export_model(self, save_path: str, format: str = "safetensors", sample_input: dict = None):
def export_model(self, save_path: str, format: str = "safetensors", sample_input: dict | None = None):
"""Export the model in various formats.
Args:
@@ -2266,8 +2266,8 @@ class LudwigModel:
def kfold_cross_validate(
num_folds: int,
config: dict | str,
dataset: str = None,
data_format: str = None,
dataset: str | None = None,
data_format: str | None = None,
skip_save_training_description: bool = False,
skip_save_training_statistics: bool = False,
skip_save_model: bool = False,
+6 -4
View File
@@ -1,4 +1,6 @@
from ludwig.automl.automl import auto_train # noqa
from ludwig.automl.automl import cli_init_config # noqa
from ludwig.automl.automl import create_auto_config # noqa
from ludwig.automl.automl import train_with_config # noqa; noqa
from ludwig.automl.automl import (
auto_train, # noqa: F401
cli_init_config, # noqa: F401
create_auto_config, # noqa: F401
train_with_config, # noqa: F401
)
+5 -5
View File
@@ -189,7 +189,7 @@ def reduce_text_feature_max_length(config, training_set_metadata) -> bool:
# combinations and return that value if it is less than num_samples; else return num_samples.
def _update_num_samples(num_samples, hyperparam_search_space):
max_num_samples = 1
for param in hyperparam_search_space.keys():
for param in hyperparam_search_space:
if hyperparam_search_space[param][SPACE] == "choice":
max_num_samples *= len(hyperparam_search_space[param]["categories"])
else:
@@ -238,7 +238,7 @@ def memory_tune_config(config, dataset, model_category, row_count, backend):
# check if we have exhausted tuning of current param (e.g. we can no longer reduce the param value)
param, min_value = param_list[0], params_to_modify[param_list[0]]
if param in modified_hyperparam_search_space.keys():
if param in modified_hyperparam_search_space:
param_space = modified_hyperparam_search_space[param]["space"]
if param_space == "choice":
if (
@@ -268,9 +268,9 @@ def memory_tune_config(config, dataset, model_category, row_count, backend):
if model_category == TEXT and row_count > AUTOML_LARGE_TEXT_DATASET:
if "checkpoints_per_epoch" not in config[TRAINER] and "steps_per_checkpoint" not in config[TRAINER]:
checkpoints_per_epoch = max(2, math.floor(row_count / AUTOML_MAX_ROWS_PER_CHECKPOINT))
config[TRAINER][
"checkpoints_per_epoch"
] = checkpoints_per_epoch # decrease latency to get model accuracy signal
config[TRAINER]["checkpoints_per_epoch"] = (
checkpoints_per_epoch # decrease latency to get model accuracy signal
)
if "evaluate_training_set" not in config[TRAINER]:
config[TRAINER]["evaluate_training_set"] = False # reduce overhead for increased evaluation frequency
if not fits_in_memory:
+12 -12
View File
@@ -78,7 +78,7 @@ TABULAR_TYPES = {CATEGORY, NUMBER, BINARY}
class AutoTrainResults:
def __init__(self, experiment_analysis: ExperimentAnalysis, creds: dict[str, Any] = None):
def __init__(self, experiment_analysis: ExperimentAnalysis, creds: dict[str, Any] | None = None):
self._experiment_analysis = experiment_analysis
self._creds = creds
@@ -120,7 +120,7 @@ def auto_train(
time_limit_s: int | float,
output_directory: str = OUTPUT_DIR,
tune_for_memory: bool = False,
user_config: dict = None,
user_config: dict | None = None,
random_seed: int = default_random_seed,
use_reference_config: bool = False,
**kwargs,
@@ -169,7 +169,7 @@ def create_auto_config(
target: str | list[str],
time_limit_s: int | float,
tune_for_memory: bool = False,
user_config: dict = None,
user_config: dict | None = None,
random_seed: int = default_random_seed,
imbalance_threshold: float = 0.9,
use_reference_config: bool = False,
@@ -228,7 +228,7 @@ def create_automl_config_for_features(
target: str | list[str],
time_limit_s: int | float,
tune_for_memory: bool = False,
user_config: dict = None,
user_config: dict | None = None,
random_seed: int = default_random_seed,
imbalance_threshold: float = 0.9,
use_reference_config: bool = False,
@@ -248,7 +248,7 @@ def create_automl_config_for_features(
@PublicAPI
def create_features_config(
dataset_info: DatasetInfo,
target_name: str | list[str] = None,
target_name: str | list[str] | None = None,
) -> ModelConfigDict:
return get_features_config(dataset_info.fields, dataset_info.row_count, target_name)
@@ -329,7 +329,7 @@ def _model_select(
# override combiner heuristic if explicitly provided by user
if user_config is not None:
if "combiner" in user_config.keys():
if "combiner" in user_config:
model_type = user_config["combiner"]["type"]
base_config = merge_dict(base_config, default_configs["combiner"][model_type])
else:
@@ -375,12 +375,12 @@ def _model_select(
# remove all parameters from hyperparameter search that user has
# provided explicit values for
hyperopt_params = copy.deepcopy(base_config["hyperopt"]["parameters"])
for hyperopt_params in hyperopt_params.keys():
config_section, param = hyperopt_params.split(".")[0], hyperopt_params.split(".")[1]
if config_section in user_config.keys():
hyperopt_params_copy = copy.deepcopy(base_config["hyperopt"]["parameters"])
for param_key in hyperopt_params_copy:
config_section, param = param_key.split(".")[0], param_key.split(".")[1]
if config_section in user_config:
if param in user_config[config_section]:
del base_config["hyperopt"]["parameters"][hyperopt_params]
del base_config["hyperopt"]["parameters"][param_key]
# if single output feature, set relevant metric and goal if not already set
base_config = set_output_feature_metric(base_config)
@@ -421,7 +421,7 @@ def init_config(
tune_for_memory: bool = False,
suggested: bool = False,
hyperopt: bool = False,
output: str = None,
output: str | None = None,
random_seed: int = default_random_seed,
use_reference_config: bool = False,
**kwargs,
+6 -6
View File
@@ -232,7 +232,7 @@ def create_default_config(
# read in all encoder configs
for feat_type, default_configs in encoder_defaults.items():
if feat_type in feature_types:
if feat_type not in model_configs.keys():
if feat_type not in model_configs:
model_configs[feat_type] = {}
for encoder_name, encoder_config_path in default_configs.items():
model_configs[feat_type][encoder_name] = load_yaml(encoder_config_path)
@@ -341,7 +341,7 @@ def get_dataset_info_from_source(source: DataSource) -> DatasetInfo:
def get_features_config(
fields: list[FieldInfo],
row_count: int,
target_name: str | list[str] = None,
target_name: str | list[str] | None = None,
) -> dict:
"""Constructs FieldInfo objects for each feature in dataset. These objects are used for downstream type
inference.
@@ -357,7 +357,7 @@ def get_features_config(
return get_config_from_metadata(metadata, targets)
def convert_targets(target_name: str | list[str] = None) -> set[str]:
def convert_targets(target_name: str | list[str] | None = None) -> set[str]:
targets = target_name
if isinstance(targets, str):
targets = [targets]
@@ -366,7 +366,7 @@ def convert_targets(target_name: str | list[str] = None) -> set[str]:
return set(targets)
def get_config_from_metadata(metadata: list[FieldMetadata], targets: set[str] = None) -> dict:
def get_config_from_metadata(metadata: list[FieldMetadata], targets: set[str] | None = None) -> dict:
"""Builds input/output feature sections of auto-train config using field metadata.
# Inputs
@@ -389,7 +389,7 @@ def get_config_from_metadata(metadata: list[FieldMetadata], targets: set[str] =
@DeveloperAPI
def get_field_metadata(fields: list[FieldInfo], row_count: int, targets: set[str] = None) -> list[FieldMetadata]:
def get_field_metadata(fields: list[FieldInfo], row_count: int, targets: set[str] | None = None) -> list[FieldMetadata]:
"""Computes metadata for each field in dataset.
# Inputs
@@ -422,7 +422,7 @@ def get_field_metadata(fields: list[FieldInfo], row_count: int, targets: set[str
return metadata
def infer_mode(field: FieldInfo, targets: set[str] = None) -> str:
def infer_mode(field: FieldInfo, targets: set[str] | None = None) -> str:
if field.name in targets:
return "output"
if field.name.lower() == "split":
+31 -33
View File
@@ -95,15 +95,15 @@ def get_trainer_kwargs(**kwargs) -> dict[str, Any]:
# Remove nics if present (legacy option)
kwargs.pop("nics", None)
defaults = dict(
backend=TorchConfig(),
num_workers=num_workers,
use_gpu=use_gpu,
resources_per_worker={
defaults = {
"backend": TorchConfig(),
"num_workers": num_workers,
"use_gpu": use_gpu,
"resources_per_worker": {
"CPU": 0 if use_gpu else 1,
"GPU": 1 if use_gpu else 0,
},
)
}
return {**defaults, **kwargs}
@@ -151,9 +151,7 @@ def _make_picklable(obj):
"""Recursively convert defaultdicts (which contain unpicklable lambdas) to regular dicts."""
from collections import defaultdict
if isinstance(obj, defaultdict):
return {k: _make_picklable(v) for k, v in obj.items()}
elif isinstance(obj, dict):
if isinstance(obj, defaultdict) or isinstance(obj, dict):
return {k: _make_picklable(v) for k, v in obj.items()}
elif isinstance(obj, tuple) and hasattr(obj, "_fields"):
# NamedTuple: reconstruct with the same field names
@@ -166,10 +164,10 @@ def _make_picklable(obj):
def train_fn(
executable_kwargs: dict[str, Any] = None,
model_ref: ObjectRef = None, # noqa: F821
training_set_metadata: dict[str, Any] = None,
features: dict[str, dict] = None,
executable_kwargs: dict[str, Any] | None = None,
model_ref: ObjectRef = None,
training_set_metadata: dict[str, Any] | None = None,
features: dict[str, dict] | None = None,
**kwargs,
):
"""Ray Train worker function for distributed training.
@@ -271,12 +269,12 @@ def train_fn(
@ray.remote
def tune_batch_size_fn(
dataset: RayDataset = None,
data_loader_kwargs: dict[str, Any] = None,
executable_kwargs: dict[str, Any] = None,
model: ECD = None, # noqa: F821
data_loader_kwargs: dict[str, Any] | None = None,
executable_kwargs: dict[str, Any] | None = None,
model: ECD = None,
ludwig_config: ModelConfig | dict[str, Any] = None,
training_set_metadata: dict[str, Any] = None,
features: dict[str, dict] = None,
training_set_metadata: dict[str, Any] | None = None,
features: dict[str, dict] | None = None,
**kwargs,
) -> int:
# Pin GPU before loading the model to prevent memory leaking onto other devices
@@ -303,11 +301,11 @@ def tune_batch_size_fn(
def tune_learning_rate_fn(
dataset: RayDataset,
config: dict[str, Any],
data_loader_kwargs: dict[str, Any] = None,
executable_kwargs: dict[str, Any] = None,
model: ECD = None, # noqa: F821
training_set_metadata: dict[str, Any] = None,
features: dict[str, dict] = None,
data_loader_kwargs: dict[str, Any] | None = None,
executable_kwargs: dict[str, Any] | None = None,
model: ECD = None,
training_set_metadata: dict[str, Any] | None = None,
features: dict[str, dict] | None = None,
**kwargs,
) -> float:
# Pin GPU before loading the model to prevent memory leaking onto other devices
@@ -553,10 +551,10 @@ class RayTrainerV2(BaseTrainer):
def eval_fn(
predictor_kwargs: dict[str, Any] = None,
model_ref: ObjectRef = None, # noqa: F821
training_set_metadata: dict[str, Any] = None,
features: dict[str, dict] = None,
predictor_kwargs: dict[str, Any] | None = None,
model_ref: ObjectRef = None,
training_set_metadata: dict[str, Any] | None = None,
features: dict[str, dict] | None = None,
**kwargs,
):
"""Ray Train worker function for distributed evaluation.
@@ -781,7 +779,7 @@ class RayPredictor(BasePredictor):
def _check_dataset(self, dataset):
if not isinstance(dataset, RayDataset):
raise RuntimeError(f"Ray backend requires RayDataset for inference, " f"found: {type(dataset)}")
raise RuntimeError(f"Ray backend requires RayDataset for inference, found: {type(dataset)}")
def shutdown(self):
for handle in self.actor_handles:
@@ -790,7 +788,7 @@ class RayPredictor(BasePredictor):
def get_batch_infer_model(
self,
model: "LudwigModel", # noqa: F821
model: "LudwigModel",
predictor_kwargs: dict[str, Any],
output_columns: list[str],
features: dict[str, dict],
@@ -831,14 +829,14 @@ class RayPredictor(BasePredictor):
def _prepare_batch(self, batch: pd.DataFrame) -> dict[str, np.ndarray]:
res = {}
for c in self.features.keys():
for c in self.features:
if self.features[c][TYPE] not in _SCALAR_TYPES:
# Ensure columns stacked instead of turned into np.array([np.array, ...], dtype=object) objects
res[c] = np.stack(batch[c].values)
else:
res[c] = batch[c].to_numpy()
for c in self.features.keys():
for c in self.features:
reshape = self.reshape_map.get(c)
if reshape is not None:
res[c] = res[c].reshape((-1, *reshape))
@@ -886,7 +884,7 @@ class RayBackend(RemoteTrainingMixin, Backend):
num_cpu = self._preprocessor_kwargs.get("num_cpu")
if not num_cpu:
logger.info(
"Backend config has num_cpu not set." " provision_preprocessing_workers() is a no-op in this case."
"Backend config has num_cpu not set. provision_preprocessing_workers() is a no-op in this case."
)
yield
else:
@@ -919,7 +917,7 @@ class RayBackend(RemoteTrainingMixin, Backend):
initialize_pytorch(gpus=-1)
self._pytorch_kwargs = kwargs
def create_trainer(self, model: BaseModel, **kwargs) -> "BaseTrainer": # noqa: F821
def create_trainer(self, model: BaseModel, **kwargs) -> "BaseTrainer":
executable_kwargs = {**kwargs, **self._pytorch_kwargs}
if model.type() == MODEL_LLM:
from ludwig.trainers.registry import get_llm_ray_trainers_registry
+2 -2
View File
@@ -1,9 +1,9 @@
import contextlib
from typing import Any, Optional, Union
from typing import Any
from ludwig.utils import data_utils
CredInputs = Optional[Union[str, dict[str, Any]]]
CredInputs = str | dict[str, Any] | None
DEFAULTS = "defaults"
+2 -2
View File
@@ -40,9 +40,9 @@ def get_gpu_info():
driver_version = xml.findall("driver_version")[0].text
cuda_version = xml.findall("cuda_version")[0].text
for gpu_id, gpu in enumerate(xml.getiterator("gpu")):
for _gpu_id, gpu in enumerate(xml.getiterator("gpu")):
gpu_data = {}
name = [x for x in gpu.getiterator("product_name")][0].text
name = list(gpu.getiterator("product_name"))[0].text
memory_usage = gpu.findall("fb_memory_usage")[0]
total_memory = memory_usage.findall("total")[0].text
+3 -1
View File
@@ -141,7 +141,9 @@ def get_resource_usage_report(
return info
def get_all_events(kineto_events: list[_KinetoEvent], function_events: profiler_util.EventList) -> tuple[
def get_all_events(
kineto_events: list[_KinetoEvent], function_events: profiler_util.EventList
) -> tuple[
list[_KinetoEvent],
list[profiler_util.FunctionEvent],
list[list[_KinetoEvent | bool]],
+2 -11
View File
@@ -64,11 +64,7 @@ def export_and_print(
os.makedirs(output_path, exist_ok=True)
logger.info(
"Model performance metrics for *{}* vs. *{}* on dataset *{}*".format(
experiment_metric_diff.base_experiment_name,
experiment_metric_diff.experimental_experiment_name,
experiment_metric_diff.dataset_name,
)
f"Model performance metrics for *{experiment_metric_diff.base_experiment_name}* vs. *{experiment_metric_diff.experimental_experiment_name}* on dataset *{experiment_metric_diff.dataset_name}*"
)
logger.info(experiment_metric_diff.to_string())
filename = (
@@ -82,12 +78,7 @@ def export_and_print(
os.makedirs(output_path, exist_ok=True)
for tag_diff in experiment_resource_diff:
logger.info(
"Resource usage for *{}* vs. *{}* on *{}* of dataset *{}*".format(
tag_diff.base_experiment_name,
tag_diff.experimental_experiment_name,
tag_diff.code_block_tag,
dataset_name,
)
f"Resource usage for *{tag_diff.base_experiment_name}* vs. *{tag_diff.experimental_experiment_name}* on *{tag_diff.code_block_tag}* of dataset *{dataset_name}*"
)
logger.info(tag_diff.to_string())
filename = (
+1 -1
View File
@@ -393,7 +393,7 @@ def summarize_resource_usage(path: str, tags: list[str] | None = None) -> list[R
:param path: corresponds to the `output_dir` argument in a ResourceUsageTracker run.
:param tags: (optional) list of tags to create summary for. If None, metrics from all tags will be summarized.
"""
summary = dict()
summary = {}
# metric types: system_resource_usage, torch_ops_resource_usage.
all_metric_types = {"system_resource_usage", "torch_ops_resource_usage"}
for metric_type in all_metric_types.intersection(os.listdir(path)):
+1 -1
View File
@@ -260,7 +260,7 @@ def delete_hyperopt_outputs(output_directory: str):
Args:
output_directory: output directory of the hyperopt run.
"""
for path, currentDirectory, files in os.walk(output_directory):
for path, _currentDirectory, files in os.walk(output_directory):
for file in files:
filename = os.path.join(path, file)
if file not in HYPEROPT_OUTDIR_RETAINED_FILES:
+6 -6
View File
@@ -39,14 +39,14 @@ def collect_activations(
model_path: str,
layers: list[str],
dataset: str,
data_format: str = None,
data_format: str | None = None,
split: str = FULL,
batch_size: int = 128,
output_directory: str = "results",
gpus: list[str] = None,
gpus: list[str] | None = None,
gpu_memory_limit: float | None = None,
allow_parallel_threads: bool = True,
callbacks: list[Callback] = None,
callbacks: list[Callback] | None = None,
backend: Backend | str = None,
**kwargs,
) -> list[str]:
@@ -274,7 +274,7 @@ def cli_collect_activations(sys_argv):
"feather",
"fwf",
"hdf5",
"html" "tables",
"htmltables",
"json",
"jsonl",
"parquet",
@@ -332,7 +332,7 @@ def cli_collect_activations(sys_argv):
parser.add_argument(
"-b",
"--backend",
help="specifies backend to use for parallel / distributed execution, " "defaults to local execution",
help="specifies backend to use for parallel / distributed execution, defaults to local execution",
choices=ALL_BACKENDS,
)
parser.add_argument(
@@ -370,7 +370,7 @@ def cli_collect_weights(sys_argv):
--v: Verbose: Defines the logging level that the user will be exposed to
"""
parser = argparse.ArgumentParser(
description="This script loads a pretrained model " "and uses it collect weights.",
description="This script loads a pretrained model and uses it collect weights.",
prog="ludwig collect_weights",
usage="%(prog)s [options]",
)
+48 -38
View File
@@ -125,7 +125,9 @@ def create_combiner(config: BaseCombinerConfig, **kwargs) -> Combiner:
@register_combiner(ConcatCombinerConfig)
class ConcatCombiner(Combiner):
def __init__(self, input_features: dict[str, "InputFeature"] = None, config: ConcatCombinerConfig = None, **kwargs):
def __init__(
self, input_features: dict[str, "InputFeature"] | None = None, config: ConcatCombinerConfig = None, **kwargs
):
super().__init__(input_features)
self.name = "ConcatCombiner"
logger.debug(f" {self.name}")
@@ -137,7 +139,7 @@ class ConcatCombiner(Combiner):
fc_layers = config.fc_layers
if fc_layers is None:
fc_layers = []
for i in range(config.num_fc_layers):
for _i in range(config.num_fc_layers):
fc_layers.append({"output_size": config.output_size})
self.fc_layers = fc_layers
@@ -202,7 +204,7 @@ class ConcatCombiner(Combiner):
# potential use in decoders, e.g. LSTM state for seq2seq.
# TODO(Justin): Think about how to make this communication work for multi-sequence
# features. Other combiners.
for key, value in [d for d in inputs.values()][0].items():
for key, value in list(inputs.values())[0].items():
if key != ENCODER_OUTPUT:
return_data[key] = value
@@ -293,26 +295,16 @@ class SequenceConcatCombiner(Combiner):
# features have different lengths for some data points.
if if_representation.shape[1] != representation.shape[1]:
raise ValueError(
"The sequence length of the input feature {} "
"is {} and is different from the sequence "
"length of the main sequence feature {} which "
"is {}.\n Shape of {}: {}, shape of {}: {}.\n"
f"The sequence length of the input feature {if_name} "
f"is {if_representation.shape[1]} and is different from the sequence "
f"length of the main sequence feature {self.main_sequence_feature} which "
f"is {representation.shape[1]}.\n Shape of {if_name}: {if_representation.shape}, shape of {if_name}: {representation.shape}.\n"
"Sequence lengths of all sequential features "
"must be the same in order to be concatenated "
"by the sequence concat combiner. "
"Try to impose the same max sequence length "
"as a preprocessing parameter to both features "
"or to reduce the output of {}.".format(
if_name,
if_representation.shape[1],
self.main_sequence_feature,
representation.shape[1],
if_name,
if_representation.shape,
if_name,
representation.shape,
if_name,
)
f"or to reduce the output of {if_name}."
)
# this assumes all sequence representations have the
# same sequence length, 2nd dimension
@@ -325,9 +317,9 @@ class SequenceConcatCombiner(Combiner):
else:
raise ValueError(
"The representation of {} has rank {} and cannot be"
f"The representation of {if_name} has rank {len(if_representation.shape)} and cannot be"
" concatenated by a sequence concat combiner. "
"Only rank 2 and rank 3 tensors are supported.".format(if_name, len(if_representation.shape))
"Only rank 2 and rank 3 tensors are supported."
)
hidden = torch.cat(representations, 2)
@@ -343,7 +335,7 @@ class SequenceConcatCombiner(Combiner):
return_data = {"combiner_output": hidden}
if len(inputs) == 1:
for key, value in [d for d in inputs.values()][0].items():
for key, value in list(inputs.values())[0].items():
if key != ENCODER_OUTPUT:
return_data[key] = value
@@ -363,7 +355,7 @@ class SequenceCombiner(Combiner):
)
logger.debug(
f"combiner input shape {self.combiner.concatenated_shape}, " f"output shape {self.combiner.output_shape}"
f"combiner input shape {self.combiner.concatenated_shape}, output shape {self.combiner.output_shape}"
)
self.encoder_obj = get_from_registry(config.encoder.type, get_sequence_encoder_registry())(
@@ -481,7 +473,7 @@ class TabNetCombiner(Combiner):
}
if len(inputs) == 1:
for key, value in [d for d in inputs.values()][0].items():
for key, value in list(inputs.values())[0].items():
if key != ENCODER_OUTPUT:
return_data[key] = value
@@ -495,7 +487,10 @@ class TabNetCombiner(Combiner):
@register_combiner(TransformerCombinerConfig)
class TransformerCombiner(Combiner):
def __init__(
self, input_features: dict[str, "InputFeature"] = None, config: TransformerCombinerConfig = None, **kwargs
self,
input_features: dict[str, "InputFeature"] | None = None,
config: TransformerCombinerConfig = None,
**kwargs,
):
super().__init__(input_features)
self.name = "TransformerCombiner"
@@ -582,7 +577,7 @@ class TransformerCombiner(Combiner):
return_data = {"combiner_output": hidden}
if len(inputs) == 1:
for key, value in [d for d in inputs.values()][0].items():
for key, value in list(inputs.values())[0].items():
if key != ENCODER_OUTPUT:
return_data[key] = value
@@ -592,7 +587,10 @@ class TransformerCombiner(Combiner):
@register_combiner(TabTransformerCombinerConfig)
class TabTransformerCombiner(Combiner):
def __init__(
self, input_features: dict[str, "InputFeature"] = None, config: TabTransformerCombinerConfig = None, **kwargs
self,
input_features: dict[str, "InputFeature"] | None = None,
config: TabTransformerCombinerConfig = None,
**kwargs,
):
super().__init__(input_features)
self.name = "TabTransformerCombiner"
@@ -619,9 +617,9 @@ class TabTransformerCombiner(Combiner):
raise ValueError(
"TabTransformer parameter "
"`embed_input_feature_name` "
"specified integer value ({}) "
f"specified integer value ({self.embed_input_feature_name}) "
"needs to be smaller than "
"`hidden_size` ({}).".format(self.embed_input_feature_name, config.hidden_size)
f"`hidden_size` ({config.hidden_size})."
)
self.embed_i_f_name_layer = Embed(
vocab,
@@ -635,7 +633,7 @@ class TabTransformerCombiner(Combiner):
"`embed_input_feature_name` "
"should be either None, an integer or `add`, "
"the current value is "
"{}".format(self.embed_input_feature_name)
f"{self.embed_input_feature_name}"
)
else:
projector_size = config.hidden_size
@@ -788,7 +786,7 @@ class TabTransformerCombiner(Combiner):
return_data = {"combiner_output": hidden}
if len(inputs) == 1:
for key, value in [d for d in inputs.values()][0].items():
for key, value in list(inputs.values())[0].items():
if key != ENCODER_OUTPUT:
return_data[key] = value
@@ -939,7 +937,10 @@ class ComparatorCombiner(Combiner):
@register_combiner(ProjectAggregateCombinerConfig)
class ProjectAggregateCombiner(Combiner):
def __init__(
self, input_features: dict[str, "InputFeature"] = None, config: ProjectAggregateCombinerConfig = None, **kwargs
self,
input_features: dict[str, "InputFeature"] | None = None,
config: ProjectAggregateCombinerConfig = None,
**kwargs,
):
super().__init__(input_features)
self.name = "ProjectAggregateCombiner"
@@ -964,7 +965,7 @@ class ProjectAggregateCombiner(Combiner):
fc_layers = config.fc_layers
if fc_layers is None and config.num_fc_layers is not None:
fc_layers = []
for i in range(config.num_fc_layers):
for _i in range(config.num_fc_layers):
fc_layers.append({"output_size": config.output_size})
self.fc_layers = fc_layers
@@ -1014,7 +1015,7 @@ class ProjectAggregateCombiner(Combiner):
# potential use in decoders, e.g. LSTM state for seq2seq.
# TODO(Justin): Think about how to make this communication work for multi-sequence
# features. Other combiners.
for key, value in [d for d in inputs.values()][0].items():
for key, value in list(inputs.values())[0].items():
if key != ENCODER_OUTPUT:
return_data[key] = value
@@ -1030,7 +1031,10 @@ class FTTransformerCombiner(Combiner):
"""
def __init__(
self, input_features: dict[str, "InputFeature"] = None, config: FTTransformerCombinerConfig = None, **kwargs
self,
input_features: dict[str, "InputFeature"] | None = None,
config: FTTransformerCombinerConfig = None,
**kwargs,
):
super().__init__(input_features)
self.name = "FTTransformerCombiner"
@@ -1103,7 +1107,10 @@ class CrossAttentionCombiner(Combiner):
"""
def __init__(
self, input_features: dict[str, "InputFeature"] = None, config: CrossAttentionCombinerConfig = None, **kwargs
self,
input_features: dict[str, "InputFeature"] | None = None,
config: CrossAttentionCombinerConfig = None,
**kwargs,
):
super().__init__(input_features)
self.name = "CrossAttentionCombiner"
@@ -1185,7 +1192,7 @@ class PerceiverCombiner(Combiner):
"""
def __init__(
self, input_features: dict[str, "InputFeature"] = None, config: PerceiverCombinerConfig = None, **kwargs
self, input_features: dict[str, "InputFeature"] | None = None, config: PerceiverCombinerConfig = None, **kwargs
):
super().__init__(input_features)
self.name = "PerceiverCombiner"
@@ -1276,7 +1283,10 @@ class GatedFusionCombiner(Combiner):
"""
def __init__(
self, input_features: dict[str, "InputFeature"] = None, config: GatedFusionCombinerConfig = None, **kwargs
self,
input_features: dict[str, "InputFeature"] | None = None,
config: GatedFusionCombinerConfig = None,
**kwargs,
):
super().__init__(input_features)
self.name = "GatedFusionCombiner"
@@ -1357,7 +1367,7 @@ class HyperNetworkCombiner(Combiner):
Unique Ludwig differentiator. Based on HyperFusion (arXiv 2403.13319, 2024).
"""
def __init__(self, input_features: dict[str, "InputFeature"] = None, config=None, **kwargs):
def __init__(self, input_features: dict[str, "InputFeature"] | None = None, config=None, **kwargs):
super().__init__(input_features)
self.name = "HyperNetworkCombiner"
logger.debug(f" {self.name}")
+2 -2
View File
@@ -64,7 +64,7 @@ class TabPFNV2Combiner(Combiner):
import tabpfn # noqa: F401
except ImportError as exc:
raise ImportError(
"The tabpfn_v2 combiner requires the optional 'tabpfn' package. " "Install with: pip install tabpfn"
"The tabpfn_v2 combiner requires the optional 'tabpfn' package. Install with: pip install tabpfn"
) from exc
# Defer heavy TabPFN loading until _lazy_load_tabpfn() is explicitly called.
@@ -77,7 +77,7 @@ class TabPFNV2Combiner(Combiner):
from tabpfn import TabPFNRegressor
except ImportError as exc:
raise ImportError(
"The tabpfn_v2 combiner requires the optional 'tabpfn' package. " "Install with: pip install tabpfn"
"The tabpfn_v2 combiner requires the optional 'tabpfn' package. Install with: pip install tabpfn"
) from exc
self._tabpfn_model = TabPFNRegressor(
device=self.config.device,
+8 -8
View File
@@ -1,7 +1,7 @@
import copy
import random
from collections import deque, namedtuple
from typing import Any, Deque
from typing import Any
import pandas as pd
@@ -21,9 +21,9 @@ ConfigOption = namedtuple("ConfigOption", ["config_option", "fully_explored"])
def explore_properties(
jsonschema_properties: dict[str, Any],
parent_parameter_path: str,
dq: Deque[ConfigOption],
dq: deque[ConfigOption],
allow_list: list[str] = [],
) -> Deque[tuple[dict, bool]]:
) -> deque[tuple[dict, bool]]:
"""Recursively explores the `properties` part of any subsection of the schema.
Args:
@@ -115,7 +115,7 @@ def get_samples(jsonschema_property: dict[str, Any]) -> list[ParameterBaseTypes]
return get_potential_values(jsonschema_property)
def merge_dq(config_options: dict[str, Any], child_config_options_dq: Deque[ConfigOption]) -> Deque[ConfigOption]:
def merge_dq(config_options: dict[str, Any], child_config_options_dq: deque[ConfigOption]) -> deque[ConfigOption]:
"""Merge config_options with the child_config_options in the dq."""
dq = deque()
while child_config_options_dq:
@@ -125,7 +125,7 @@ def merge_dq(config_options: dict[str, Any], child_config_options_dq: Deque[Conf
return dq
def explore_from_all_of(config_options: dict[str, Any], item: dict[str, Any], key_so_far: str) -> Deque[ConfigOption]:
def explore_from_all_of(config_options: dict[str, Any], item: dict[str, Any], key_so_far: str) -> deque[ConfigOption]:
"""Takes a child of `allOf` and calls `explore_properties` on it."""
for parameter_name_or_section in item["if"]["properties"]:
config_options[key_so_far + "." + parameter_name_or_section] = item["if"]["properties"][
@@ -235,7 +235,7 @@ def create_nested_dict(flat_dict: dict[str, float | str]) -> ModelConfigDict:
def combine_configs(
explored: Deque[tuple[dict, bool]], config: ModelConfigDict
explored: deque[tuple[dict, bool]], config: ModelConfigDict
) -> list[tuple[ModelConfigDict, pd.DataFrame]]:
"""Merge base config with explored sections.
@@ -257,7 +257,7 @@ def combine_configs(
def combine_configs_for_comparator_combiner(
explored: Deque[tuple], config: ModelConfigDict
explored: deque[tuple], config: ModelConfigDict
) -> list[tuple[ModelConfigDict, pd.DataFrame]]:
"""Merge base config with explored sections.
@@ -288,7 +288,7 @@ def combine_configs_for_comparator_combiner(
def combine_configs_for_sequence_combiner(
explored: Deque[tuple], config: ModelConfigDict
explored: deque[tuple], config: ModelConfigDict
) -> list[tuple[ModelConfigDict, pd.DataFrame]]:
"""Merge base config with explored sections.
+4 -4
View File
@@ -1,10 +1,10 @@
import random
from typing import Any, Union
from typing import Any
from ludwig.schema.metadata.parameter_metadata import ExpectedImpact
# base types for ludwig config parameters.
ParameterBaseTypes = Union[str, float, int, bool, None]
ParameterBaseTypes = str | float | int | bool | None
def handle_property_type(
@@ -17,7 +17,7 @@ def handle_property_type(
item: dictionary containing details on the parameter such as default, min and max values.
expected_impact: threshold expected impact that we'd like to include.
"""
parameter_metadata = item.get("parameter_metadata", None)
parameter_metadata = item.get("parameter_metadata")
if not parameter_metadata:
return []
@@ -53,7 +53,7 @@ def explore_array(item: dict[str, Any]) -> list[list[ParameterBaseTypes]]:
"""
candidates = []
if "default" in item and item["default"]:
if item.get("default"):
candidates.append(item["default"])
item_choices = []
+33 -33
View File
@@ -42,7 +42,7 @@ class ConfigCheckRegistry:
def register(self, check_fn):
self._registry.append(check_fn)
def check_config(self, config: "ModelConfig") -> None: # noqa: F821
def check_config(self, config: "ModelConfig") -> None:
for check_fn in self._registry:
check_fn(config)
@@ -66,13 +66,13 @@ class ConfigCheck(ABC):
@staticmethod
@abstractmethod
def check(config: "ModelConfig") -> None: # noqa: F821
def check(config: "ModelConfig") -> None:
"""Checks config for validity."""
raise NotImplementedError
@register_config_check
def check_feature_names_unique(config: "ModelConfig") -> None: # noqa: F821
def check_feature_names_unique(config: "ModelConfig") -> None:
"""Checks that all feature names are unique."""
input_features = config.input_features
input_feature_names = {input_feature.name for input_feature in input_features}
@@ -85,7 +85,7 @@ def check_feature_names_unique(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_tied_features_valid(config: "ModelConfig") -> None: # noqa: F821
def check_tied_features_valid(config: "ModelConfig") -> None:
"""Checks that all tied features are valid."""
input_features = config.input_features
input_feature_names = {input_feature.name for input_feature in input_features}
@@ -99,7 +99,7 @@ def check_tied_features_valid(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_training_runway(config: "ModelConfig") -> None: # noqa: F821
def check_training_runway(config: "ModelConfig") -> None:
"""Checks that checkpoints_per_epoch and steps_per_checkpoint aren't simultaneously defined."""
if config.model_type == MODEL_ECD:
if config.trainer.checkpoints_per_epoch != 0 and config.trainer.steps_per_checkpoint != 0:
@@ -111,7 +111,7 @@ def check_training_runway(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_ray_backend_in_memory_preprocessing(config: "ModelConfig") -> None: # noqa: F821
def check_ray_backend_in_memory_preprocessing(config: "ModelConfig") -> None:
"""Checks that in memory preprocessing is used with Ray backend."""
if config.backend is None:
return
@@ -133,7 +133,7 @@ def check_ray_backend_in_memory_preprocessing(config: "ModelConfig") -> None: #
)
def check_sequence_concat_combiner_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_sequence_concat_combiner_requirements(config: "ModelConfig") -> None:
"""Checks that sequence concat combiner has at least one input feature that's sequential."""
if config.model_type != MODEL_ECD:
return
@@ -151,7 +151,7 @@ def check_sequence_concat_combiner_requirements(config: "ModelConfig") -> None:
@register_config_check
def check_comparator_combiner_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_comparator_combiner_requirements(config: "ModelConfig") -> None:
"""Checks that all of the feature names for entity_1 and entity_2 are valid features."""
if config.model_type != MODEL_ECD:
return
@@ -162,12 +162,12 @@ def check_comparator_combiner_requirements(config: "ModelConfig") -> None: # no
for feature_name in config.combiner.entity_1:
if feature_name not in input_feature_names:
raise ConfigValidationError(
f"Feature {feature_name} in entity_1 for the comparator combiner is not a valid " "input feature name."
f"Feature {feature_name} in entity_1 for the comparator combiner is not a valid input feature name."
)
for feature_name in config.combiner.entity_2:
if feature_name not in input_feature_names:
raise ConfigValidationError(
f"Feature {feature_name} in entity_2 for the comparator combiner is not a valid " "input feature name."
f"Feature {feature_name} in entity_2 for the comparator combiner is not a valid input feature name."
)
if sorted(config.combiner.entity_1 + config.combiner.entity_2) != sorted(input_feature_names):
@@ -175,7 +175,7 @@ def check_comparator_combiner_requirements(config: "ModelConfig") -> None: # no
@register_config_check
def check_class_balance_preprocessing(config: "ModelConfig") -> None: # noqa: F821
def check_class_balance_preprocessing(config: "ModelConfig") -> None:
"""Class balancing is only available for datasets with a single output feature."""
if config.preprocessing.oversample_minority or config.preprocessing.undersample_majority:
if len(config.output_features) != 1:
@@ -185,7 +185,7 @@ def check_class_balance_preprocessing(config: "ModelConfig") -> None: # noqa: F
@register_config_check
def check_sampling_exclusivity(config: "ModelConfig") -> None: # noqa: F821
def check_sampling_exclusivity(config: "ModelConfig") -> None:
"""Oversample minority and undersample majority are mutually exclusive."""
if config.preprocessing.oversample_minority and config.preprocessing.undersample_majority:
raise ConfigValidationError(
@@ -194,7 +194,7 @@ def check_sampling_exclusivity(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_validation_metric_exists(config: "ModelConfig") -> None: # noqa: F821
def check_validation_metric_exists(config: "ModelConfig") -> None:
"""Checks that the specified validation metric exists."""
validation_metric_name = config.trainer.validation_metric
@@ -212,7 +212,7 @@ def check_validation_metric_exists(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_splitter(config: "ModelConfig") -> None: # noqa: F821
def check_splitter(config: "ModelConfig") -> None:
"""Checks the validity of the splitter configuration."""
from ludwig.data.split import get_splitter
@@ -221,7 +221,7 @@ def check_splitter(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_hf_tokenizer_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_hf_tokenizer_requirements(config: "ModelConfig") -> None:
"""Checks that the HuggingFace tokenizer has a pretrained_model_name_or_path specified."""
for input_feature in config.input_features:
@@ -234,7 +234,7 @@ def check_hf_tokenizer_requirements(config: "ModelConfig") -> None: # noqa: F82
@register_config_check
def check_hf_encoder_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_hf_encoder_requirements(config: "ModelConfig") -> None:
"""Checks that a HuggingFace encoder has a pretrained_model_name_or_path specified."""
for input_feature in config.input_features:
@@ -247,7 +247,7 @@ def check_hf_encoder_requirements(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_stacked_transformer_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_stacked_transformer_requirements(config: "ModelConfig") -> None:
"""Checks that the transformer encoder type correctly configures `num_heads` and `hidden_size`"""
def is_divisible(hidden_size: int, num_heads: int) -> bool:
@@ -271,7 +271,7 @@ def check_stacked_transformer_requirements(config: "ModelConfig") -> None: # no
@register_config_check
def check_hyperopt_search_algorithm_dependencies_installed(config: "ModelConfig") -> None: # noqa: F821
def check_hyperopt_search_algorithm_dependencies_installed(config: "ModelConfig") -> None:
"""Check that the hyperopt search algorithm dependencies are installed."""
if config.hyperopt is None:
return
@@ -283,7 +283,7 @@ def check_hyperopt_search_algorithm_dependencies_installed(config: "ModelConfig"
@register_config_check
def check_hyperopt_scheduler_dependencies_installed(config: "ModelConfig") -> None: # noqa: F821
def check_hyperopt_scheduler_dependencies_installed(config: "ModelConfig") -> None:
"""Check that the hyperopt scheduler dependencies are installed."""
if config.hyperopt is None:
return
@@ -295,7 +295,7 @@ def check_hyperopt_scheduler_dependencies_installed(config: "ModelConfig") -> No
@register_config_check
def check_tagger_decoder_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_tagger_decoder_requirements(config: "ModelConfig") -> None:
"""Checks that the tagger decoder has at least one sequence, text or timeseries input feature where the
encoder's reduce_output will produce a 3D shaped output from the combiner."""
# Check if there is a text or sequence output feature using a tagger decoder
@@ -326,12 +326,12 @@ def check_tagger_decoder_requirements(config: "ModelConfig") -> None: # noqa: F
@register_config_check
def check_hyperopt_parameter_dicts(config: "ModelConfig") -> None: # noqa: F821
def check_hyperopt_parameter_dicts(config: "ModelConfig") -> None:
"""Checks for hyperopt parameter dicts against their config objects."""
if config.hyperopt is None:
return
from ludwig.schema.hyperopt.utils import get_parameter_cls, parameter_config_registry # noqa: F401
from ludwig.schema.hyperopt.utils import get_parameter_cls, parameter_config_registry
for parameter, space in config.hyperopt.parameters.items():
# skip nested hyperopt parameters
@@ -369,7 +369,7 @@ def check_hyperopt_parameter_dicts(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_concat_combiner_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_concat_combiner_requirements(config: "ModelConfig") -> None:
"""Checks that if the concat combiner receives a mixture of sequence and non-sequence features, that all
sequence features are configured with reduce_output to be 2D tensors."""
if config.model_type != MODEL_ECD:
@@ -400,12 +400,12 @@ def check_concat_combiner_requirements(config: "ModelConfig") -> None: # noqa:
@register_config_check
def check_hyperopt_nested_parameter_dicts(config: "ModelConfig") -> None: # noqa: F821
def check_hyperopt_nested_parameter_dicts(config: "ModelConfig") -> None:
"""Checks that all nested parameters in a hyperopt config exist."""
if config.hyperopt is None or "." not in config.hyperopt.parameters:
return
from ludwig.schema.hyperopt.utils import get_parameter_cls # noqa: F401
from ludwig.schema.hyperopt.utils import get_parameter_cls
from ludwig.schema.model_types.base import ModelConfig
space = config.hyperopt.parameters["."]
@@ -436,7 +436,7 @@ def check_hyperopt_nested_parameter_dicts(config: "ModelConfig") -> None: # noq
@register_config_check
def check_llm_exactly_one_input_text_feature(config: "ModelConfig"): # noqa: F821
def check_llm_exactly_one_input_text_feature(config: "ModelConfig"):
if config.model_type != MODEL_LLM:
return
@@ -447,7 +447,7 @@ def check_llm_exactly_one_input_text_feature(config: "ModelConfig"): # noqa: F8
@register_config_check
def check_llm_finetuning_output_feature_config(config: "ModelConfig"): # noqa: F821
def check_llm_finetuning_output_feature_config(config: "ModelConfig"):
"""Checks that the output feature config for LLM finetuning is valid."""
if config.model_type != MODEL_LLM:
return
@@ -463,7 +463,7 @@ def check_llm_finetuning_output_feature_config(config: "ModelConfig"): # noqa:
@register_config_check
def check_llm_finetuning_trainer_config(config: "ModelConfig"): # noqa: F821
def check_llm_finetuning_trainer_config(config: "ModelConfig"):
"""Ensures that trainer type is finetune if adapter is not None."""
if config.model_type != MODEL_LLM:
return
@@ -481,7 +481,7 @@ def check_llm_finetuning_trainer_config(config: "ModelConfig"): # noqa: F821
@register_config_check
def check_llm_finetuning_backend_config(config: "ModelConfig"): # noqa: F821
def check_llm_finetuning_backend_config(config: "ModelConfig"):
"""Checks that the LLM finetuning using Ray is configured correctly."""
if config.model_type != MODEL_LLM:
return
@@ -568,7 +568,7 @@ def _get_llm_model_config(model_name: str) -> AutoConfig:
# TODO(geoffrey, arnav): uncomment this when we have reconciled the config with the backend kwarg in api.py
# @register_config_check
def check_llm_quantization_backend_incompatibility(config: "ModelConfig") -> None: # noqa: F821
def check_llm_quantization_backend_incompatibility(config: "ModelConfig") -> None:
"""Checks that LLM model type with quantization uses the local backend."""
if config.model_type != MODEL_LLM:
return
@@ -620,7 +620,7 @@ def check_llm_text_encoder_is_not_used_with_ecd(config: "ModelConfig") -> None:
@register_config_check
def check_qlora_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_qlora_requirements(config: "ModelConfig") -> None:
"""Checks that all the necessary settings are in place for QLoRA."""
if config.model_type != MODEL_LLM or config.trainer.type == "none":
return
@@ -630,7 +630,7 @@ def check_qlora_requirements(config: "ModelConfig") -> None: # noqa: F821
@register_config_check
def check_qlora_merge_and_unload_compatibility(config: "ModelConfig") -> None: # noqa: F821
def check_qlora_merge_and_unload_compatibility(config: "ModelConfig") -> None:
"""Checks that model.merge_and_unload() is supported by underlying model.save_pretrained() when merging QLoRA
layers."""
if config.model_type != MODEL_LLM or config.trainer.type == "none":
@@ -655,7 +655,7 @@ the quantization section from your Ludwig configuration."""
@register_config_check
def check_prompt_requirements(config: "ModelConfig") -> None: # noqa: F821
def check_prompt_requirements(config: "ModelConfig") -> None:
"""Checks that prompt's template and task properties are valid, according to the description on the schema."""
if config.model_type != MODEL_LLM:
return
+4 -4
View File
@@ -5,16 +5,16 @@ def check_global_max_sequence_length_fits_prompt_template(metadata, global_prepr
"global_max_sequence_length" in global_preprocessing_parameters
and global_preprocessing_parameters["global_max_sequence_length"] is not None
):
for feature_name, feature_metadata in metadata.items():
for _feature_name, feature_metadata in metadata.items():
if (
"prompt_template_num_tokens" in feature_metadata
and feature_metadata["prompt_template_num_tokens"]
> global_preprocessing_parameters["global_max_sequence_length"]
):
raise ValueError(
f'The prompt contains ({feature_metadata["prompt_template_num_tokens"]}) tokens, which is more '
f"The prompt contains ({feature_metadata['prompt_template_num_tokens']}) tokens, which is more "
f"than the the global_max_sequence_length "
f'({global_preprocessing_parameters["global_max_sequence_length"]}), which will remove all unique '
f"({global_preprocessing_parameters['global_max_sequence_length']}), which will remove all unique "
"information. Shorten the prompt, or increase the global max sequence length to > "
f'({feature_metadata["prompt_template_num_tokens"]}) to include the full prompt.'
f"({feature_metadata['prompt_template_num_tokens']}) to include the full prompt."
)
+1 -1
View File
@@ -45,7 +45,7 @@ class AimCallback(Callback):
self.aim_run["base_config"] = self.normalize_config(base_config)
params = dict(name=model_name, dir=experiment_directory)
params = {"name": model_name, "dir": experiment_directory}
self.aim_run["params"] = params
def aim_track(self, progress_tracker):
+1 -1
View File
@@ -21,7 +21,7 @@ def _get_runs(experiment_id: str):
@DeveloperAPI
def get_or_create_experiment_id(experiment_name, artifact_uri: str = None):
def get_or_create_experiment_id(experiment_name, artifact_uri: str | None = None):
"""Gets experiment id from mlflow."""
experiment = mlflow.get_experiment_by_name(experiment_name)
if experiment is not None:
+1 -1
View File
@@ -54,7 +54,7 @@ class BucketedBatcher(Batcher):
self.ignore_last = ignore_last
self.batch_size = batch_size
self.total_size = min(map(len, dataset.get_dataset().values()))
self.bucket_sizes = np.array([x for x in map(len, self.buckets_idcs)])
self.bucket_sizes = np.array(list(map(len, self.buckets_idcs)))
self.steps_per_epoch = self._compute_steps_per_epoch()
self.indices = np.array([0] * buckets)
self.step = 0
+1 -2
View File
@@ -21,7 +21,6 @@ import uuid
from abc import ABC, abstractmethod
from dataclasses import dataclass
from pathlib import Path
from typing import Union
from ludwig.api_annotations import DeveloperAPI
from ludwig.utils.fs_utils import checksum
@@ -91,7 +90,7 @@ class CacheablePath(CacheableDataset):
return self.path
CacheInput = Union[str, DataFrame, CacheableDataset]
CacheInput = str | DataFrame | CacheableDataset
def wrap(dataset: CacheInput | None) -> CacheableDataset:
+1 -1
View File
@@ -205,7 +205,7 @@ class DaskEngine(DataFrameEngine):
return df
df_delayed = df.to_delayed()
df_delayed_new = list()
df_delayed_new = []
empty_partition = None
for ix, n in enumerate(ll):
if n == 0:
+1 -3
View File
@@ -571,9 +571,7 @@ def cli_synthesize_dataset(dataset_size: int, features: list[dict], output_path:
]
"""
if dataset_size is None or features is None or output_path is None:
raise ValueError(
"Missing one or more required parameters: '--dataset_size', " "'--features' or '--output_path'"
)
raise ValueError("Missing one or more required parameters: '--dataset_size', '--features' or '--output_path'")
dataset = build_synthetic_dataset(dataset_size, features)
save_csv(output_path, dataset)
+1 -2
View File
@@ -85,8 +85,7 @@ def negative_sample(
for user_idx, interaction_row in enumerate(interactions_dense):
if log_pct > 0 and user_idx % niter_log == 0:
logging.info(
f"Negative sampling progress: {float(user_idx) * 100 / nrows:0.0f}% "
f"in {time.time() - start_time:0.2f}s"
f"Negative sampling progress: {float(user_idx) * 100 / nrows:0.0f}% in {time.time() - start_time:0.2f}s"
)
neg_items_for_user, extra_samples = _negative_sample_user(interaction_row, neg_pos_ratio, extra_samples)
+6 -3
View File
@@ -116,7 +116,10 @@ def convert_dict_to_df(predictions: dict[str, dict[str, list[Any] | torch.Tensor
def convert_predictions(
predictions, output_features, return_type="dict", backend: Optional["Backend"] = None # noqa: F821
predictions,
output_features,
return_type="dict",
backend: Optional["Backend"] = None, # noqa: F821
):
convert_fn = get_from_registry(return_type, conversion_registry)
return convert_fn(predictions, output_features, backend)
@@ -131,6 +134,6 @@ def convert_to_df(
conversion_registry = {
**{format: convert_to_dict for format in DICT_FORMATS},
**{format: convert_to_df for format in DATAFRAME_FORMATS},
**dict.fromkeys(DICT_FORMATS, convert_to_dict),
**dict.fromkeys(DATAFRAME_FORMATS, convert_to_df),
}
+32 -33
View File
@@ -69,11 +69,11 @@ from ludwig.schema.model_types.base import ModelConfig
from ludwig.types import FeatureConfigDict, ModelConfigDict, PreprocessingConfigDict, TrainingSetMetadataDict
from ludwig.utils import data_utils, strings_utils
from ludwig.utils.backward_compatibility import upgrade_metadata
from ludwig.utils.data_utils import DATA_TRAIN_HDF5_FP # legacy, kept for backward compat
from ludwig.utils.data_utils import (
CACHEABLE_FORMATS,
CSV_FORMATS,
DATA_TEST_PARQUET_FP,
DATA_TRAIN_HDF5_FP, # legacy, kept for backward compat
DATA_TRAIN_PARQUET_FP,
DATA_VALIDATION_PARQUET_FP,
DATAFRAME_FORMATS,
@@ -201,7 +201,7 @@ class DictPreprocessor(DataFormatPreprocessor):
):
num_overrides = override_in_memory_flag(features, True)
if num_overrides > 0:
logger.warning("Using in_memory = False is not supported " "with {} data format.".format("dict"))
logger.warning("Using in_memory = False is not supported with {} data format.".format("dict"))
df_engine = backend.df_engine
if dataset is not None:
@@ -261,7 +261,7 @@ class DataFramePreprocessor(DataFormatPreprocessor):
):
num_overrides = override_in_memory_flag(features, True)
if num_overrides > 0:
logger.warning("Using in_memory = False is not supported " "with {} data format.".format("dataframe"))
logger.warning("Using in_memory = False is not supported with {} data format.".format("dataframe"))
if isinstance(dataset, pd.DataFrame):
dataset = backend.df_engine.from_pandas(dataset)
@@ -1098,22 +1098,22 @@ class HDF5Preprocessor(DataFormatPreprocessor):
not_none_set = dataset if dataset is not None else training_set
if not training_set_metadata:
raise ValueError("When providing HDF5 data, " "training_set_metadata must not be None.")
raise ValueError("When providing HDF5 data, training_set_metadata must not be None.")
logger.info("Using full hdf5 and json")
if DATA_TRAIN_HDF5_FP not in training_set_metadata:
logger.warning(
"data_train_hdf5_fp not present in training_set_metadata. "
"Adding it with the current HDF5 file path {}".format(not_none_set)
f"Adding it with the current HDF5 file path {not_none_set}"
)
training_set_metadata[DATA_TRAIN_HDF5_FP] = not_none_set
elif training_set_metadata[DATA_TRAIN_HDF5_FP] != not_none_set:
logger.warning(
"data_train_hdf5_fp in training_set_metadata is {}, "
"different from the current HDF5 file path {}. "
"Replacing it".format(training_set_metadata[DATA_TRAIN_HDF5_FP], not_none_set)
f"data_train_hdf5_fp in training_set_metadata is {training_set_metadata[DATA_TRAIN_HDF5_FP]}, "
f"different from the current HDF5 file path {not_none_set}. "
"Replacing it"
)
training_set_metadata[DATA_TRAIN_HDF5_FP] = not_none_set
@@ -1123,7 +1123,7 @@ class HDF5Preprocessor(DataFormatPreprocessor):
)
elif training_set is not None:
kwargs = dict(preprocessing_params=preprocessing_params, backend=backend, split_data=False)
kwargs = {"preprocessing_params": preprocessing_params, "backend": backend, "split_data": False}
training_set = load_hdf5(training_set, shuffle_training=True, **kwargs)
if validation_set is not None:
@@ -1136,23 +1136,23 @@ class HDF5Preprocessor(DataFormatPreprocessor):
data_format_preprocessor_registry = {
**{fmt: DictPreprocessor for fmt in DICT_FORMATS},
**{fmt: DataFramePreprocessor for fmt in DATAFRAME_FORMATS},
**{fmt: CSVPreprocessor for fmt in CSV_FORMATS},
**{fmt: TSVPreprocessor for fmt in TSV_FORMATS},
**{fmt: JSONPreprocessor for fmt in JSON_FORMATS},
**{fmt: JSONLPreprocessor for fmt in JSONL_FORMATS},
**{fmt: ExcelPreprocessor for fmt in EXCEL_FORMATS},
**{fmt: ParquetPreprocessor for fmt in PARQUET_FORMATS},
**{fmt: PicklePreprocessor for fmt in PICKLE_FORMATS},
**{fmt: FWFPreprocessor for fmt in FWF_FORMATS},
**{fmt: FatherPreprocessor for fmt in FEATHER_FORMATS},
**{fmt: HTMLPreprocessor for fmt in HTML_FORMATS},
**{fmt: ORCPreprocessor for fmt in ORC_FORMATS},
**{fmt: SASPreprocessor for fmt in SAS_FORMATS},
**{fmt: SPSSPreprocessor for fmt in SPSS_FORMATS},
**{fmt: StataPreprocessor for fmt in STATA_FORMATS},
**{fmt: HDF5Preprocessor for fmt in HDF5_FORMATS},
**dict.fromkeys(DICT_FORMATS, DictPreprocessor),
**dict.fromkeys(DATAFRAME_FORMATS, DataFramePreprocessor),
**dict.fromkeys(CSV_FORMATS, CSVPreprocessor),
**dict.fromkeys(TSV_FORMATS, TSVPreprocessor),
**dict.fromkeys(JSON_FORMATS, JSONPreprocessor),
**dict.fromkeys(JSONL_FORMATS, JSONLPreprocessor),
**dict.fromkeys(EXCEL_FORMATS, ExcelPreprocessor),
**dict.fromkeys(PARQUET_FORMATS, ParquetPreprocessor),
**dict.fromkeys(PICKLE_FORMATS, PicklePreprocessor),
**dict.fromkeys(FWF_FORMATS, FWFPreprocessor),
**dict.fromkeys(FEATHER_FORMATS, FatherPreprocessor),
**dict.fromkeys(HTML_FORMATS, HTMLPreprocessor),
**dict.fromkeys(ORC_FORMATS, ORCPreprocessor),
**dict.fromkeys(SAS_FORMATS, SASPreprocessor),
**dict.fromkeys(SPSS_FORMATS, SPSSPreprocessor),
**dict.fromkeys(STATA_FORMATS, StataPreprocessor),
**dict.fromkeys(HDF5_FORMATS, HDF5Preprocessor),
}
@@ -1238,7 +1238,7 @@ def build_dataset(
else:
logger.warning(
f"Specified split column {global_preprocessing_parameters['split']['column']} for fixed "
f"split strategy was not found in dataset." # noqa: E713
f"split strategy was not found in dataset."
)
# update input features with prompt configs during preprocessing (as opposed to during the model forward pass)
@@ -1474,7 +1474,7 @@ def cast_columns(dataset_cols, features, backend) -> None:
)
except KeyError as e:
raise KeyError(
f"Feature name {e} specified in the config was not found in dataset with columns: " # noqa: E713
f"Feature name {e} specified in the config was not found in dataset with columns: "
+ f"{list(dataset_cols.keys())}"
)
@@ -1675,8 +1675,7 @@ def precompute_fill_value(
elif missing_value_strategy == FILL_WITH_MEAN:
if feature[TYPE] != NUMBER:
raise ValueError(
f"Filling missing values with mean is supported "
f"only for number types, not for type {feature[TYPE]}.",
f"Filling missing values with mean is supported only for number types, not for type {feature[TYPE]}.",
)
return backend.df_engine.compute(dataset_cols[feature[COLUMN]].astype(float).mean())
elif missing_value_strategy in {FILL_WITH_FALSE, FILL_WITH_TRUE}:
@@ -2119,7 +2118,7 @@ def _preprocess_file_for_training(
if dataset:
# Use data and ignore _train, _validation and _test.
# Also ignore data and train set metadata needs preprocessing
logger.info("Using full raw dataset, no hdf5 and json file " "with the same name have been found")
logger.info("Using full raw dataset, no hdf5 and json file with the same name have been found")
logger.info("Building dataset (it may take a while)")
dataset_df = read_fn(dataset, backend.df_engine.df_lib)
@@ -2142,7 +2141,7 @@ def _preprocess_file_for_training(
# use data_train (including _validation and _test if they are present)
# and ignore data and train set metadata
# needs preprocessing
logger.info("Using training raw csv, no hdf5 and json " "file with the same name have been found")
logger.info("Using training raw csv, no hdf5 and json file with the same name have been found")
logger.info("Building dataset (it may take a while)")
concatenated_df = concatenate_files(training_set, validation_set, test_set, read_fn, backend)
@@ -2292,7 +2291,7 @@ def preprocess_for_prediction(
if data_format not in HDF5_FORMATS:
num_overrides = override_in_memory_flag(config_dict["input_features"], True)
if num_overrides > 0:
logger.warning("Using in_memory = False is not supported " "with {} data format.".format(data_format))
logger.warning(f"Using in_memory = False is not supported with {data_format} data format.")
preprocessing_params = {}
config_defaults = config_dict.get(DEFAULTS, {})
+4 -4
View File
@@ -226,8 +226,8 @@ class StratifySplitter(Splitter):
return df_train, df_val, df_test
def validate(self, config: "ModelConfig"): # noqa: F821
features = [f for f in config.input_features] + [f for f in config.output_features]
def validate(self, config: "ModelConfig"):
features = list(config.input_features) + list(config.output_features)
feature_cols = {f.column for f in features}
if self.column not in feature_cols:
logging.info(
@@ -295,8 +295,8 @@ class DatetimeSplitter(Splitter):
# For Dask, split by partition, as splitting by row is very inefficient.
return tuple(backend.df_engine.split(df, self.probabilities))
def validate(self, config: "ModelConfig"): # noqa: F821
features = [f for f in config.input_features] + [f for f in config.output_features]
def validate(self, config: "ModelConfig"):
features = list(config.input_features) + list(config.output_features)
feature_cols = {f.column for f in features}
if self.column not in feature_cols:
logging.info(
+1 -1
View File
@@ -15,7 +15,7 @@ def convert_to_dict(
):
"""Convert predictions from DataFrame format to a dictionary."""
output = {}
for of_name, output_feature in output_features.items():
for of_name, _output_feature in output_features.items():
feature_keys = {k for k in predictions.columns if k.startswith(of_name)}
feature_dict = {}
for key in feature_keys:
+2 -2
View File
@@ -231,7 +231,7 @@ def list_datasets() -> list[str]:
@PublicAPI
def get_datasets_output_features(
dataset: str = None, include_competitions: bool = True, include_data_modalities: bool = False
dataset: str | None = None, include_competitions: bool = True, include_data_modalities: bool = False
) -> dict:
"""Returns a dictionary with the output features for each dataset. Optionally, you can pass a dataset name
which will then cause the function to return a dictionary with the output features for that dataset.
@@ -303,7 +303,7 @@ def download_dataset(dataset_name: str, output_dir: str = "."):
@DeveloperAPI
def get_buffer(dataset_name: str, kaggle_username: str = None, kaggle_key: str = None) -> BytesIO:
def get_buffer(dataset_name: str, kaggle_username: str | None = None, kaggle_key: str | None = None) -> BytesIO:
"""Returns a byte buffer for the specified dataset."""
try:
if dataset_name.startswith(HF_PREFIX):
+2 -2
View File
@@ -55,7 +55,7 @@ class TqdmUpTo(tqdm):
Total size (in tqdm units). If [default: None] remains unchanged.
"""
if tsize is not None:
self.total = tsize # noqa W0201
self.total = tsize
self.update(b * bsize - self.n) # will also set self.n = b * bsize
@@ -64,7 +64,7 @@ def _list_of_strings(list_or_string: str | list[str]) -> list[str]:
return [list_or_string] if isinstance(list_or_string, str) else list_or_string
def _glob_multiple(pathnames: list[str], root_dir: str = None, recursive: bool = True) -> set[str]:
def _glob_multiple(pathnames: list[str], root_dir: str | None = None, recursive: bool = True) -> set[str]:
"""Recursive glob multiple patterns, returns set of matches.
Note: glob's root_dir argument was added in python 3.10, not using it for compatibility.
+9 -9
View File
@@ -30,16 +30,16 @@ class ForestCoverLoader(DatasetLoader):
# Elevation quantitative meters Elevation in meters
# Aspect quantitative azimuth Aspect in degrees azimuth
# Slope quantitative degrees Slope in degrees
# Horizontal_Distance_To_Hydrology quantitative meters Horz Dist to nearest surface water features # noqa: E501
# Vertical_Distance_To_Hydrology quantitative meters Vert Dist to nearest surface water features # noqa: E501
# Horizontal_Distance_To_Roadways quantitative meters Horz Dist to nearest roadway # noqa: E501
# Hillshade_9am quantitative 0 to 255 index Hillshade index at 9am, summer solstice # noqa: E501
# Hillshade_Noon quantitative 0 to 255 index Hillshade index at noon, summer soltice # noqa: E501
# Hillshade_3pm quantitative 0 to 255 index Hillshade index at 3pm, summer solstice # noqa: E501
# Horizontal_Distance_To_Fire_Points quantitative meters Horz Dist to nearest wildfire ignition points # noqa: E501
# Wilderness_Area (4 binary columns) qualitative 0 (absence) or 1 (presence) Wilderness area designation # noqa: E501
# Horizontal_Distance_To_Hydrology quantitative meters Horz Dist to nearest surface water features
# Vertical_Distance_To_Hydrology quantitative meters Vert Dist to nearest surface water features
# Horizontal_Distance_To_Roadways quantitative meters Horz Dist to nearest roadway
# Hillshade_9am quantitative 0 to 255 index Hillshade index at 9am, summer solstice
# Hillshade_Noon quantitative 0 to 255 index Hillshade index at noon, summer soltice
# Hillshade_3pm quantitative 0 to 255 index Hillshade index at 3pm, summer solstice
# Horizontal_Distance_To_Fire_Points quantitative meters Horz Dist to nearest wildfire ignition points
# Wilderness_Area (4 binary columns) qualitative 0 (absence) or 1 (presence) Wilderness area designation
# Soil_Type (40 binary columns) qualitative 0 (absence) or 1 (presence) Soil Type designation
# Cover_Type (7 types) integer 1 to 7 Forest Cover Type designation # noqa: E501
# Cover_Type (7 types) integer 1 to 7 Forest Cover Type designation
# Map the 40 soil types to a single integer instead of 40 binary columns
st_cols = [
+4 -4
View File
@@ -1,6 +1,6 @@
# register all decoders
import ludwig.decoders.generic_decoders # noqa
import ludwig.decoders.image_decoders # noqa
import ludwig.decoders.llm_decoders # noqa
import ludwig.decoders.sequence_decoders # noqa
import ludwig.decoders.generic_decoders
import ludwig.decoders.image_decoders
import ludwig.decoders.llm_decoders
import ludwig.decoders.sequence_decoders
import ludwig.decoders.sequence_tagger # noqa
+5 -5
View File
@@ -43,7 +43,7 @@ logger = logging.getLogger(__name__)
# TODO(Arnav): Re-enable once we add DotProduct Combiner: https://github.com/ludwig-ai/ludwig/issues/3150
# @register_decoder("passthrough", [BINARY, CATEGORY, NUMBER, SET, VECTOR, SEQUENCE, TEXT])
class PassthroughDecoder(Decoder):
def __init__(self, input_size: int = 1, num_classes: int = None, decoder_config=None, **kwargs):
def __init__(self, input_size: int = 1, num_classes: int | None = None, decoder_config=None, **kwargs):
super().__init__()
self.config = decoder_config
@@ -144,8 +144,8 @@ class Projector(Decoder):
self.clip = partial(torch.clip, min=clip[0], max=clip[1])
else:
raise ValueError(
"The clip parameter of {} is {}. "
"It must be a list or a tuple of length 2.".format(self.feature_name, self.clip)
f"The clip parameter of {self.feature_name} is {self.clip}. "
"It must be a list or a tuple of length 2."
)
else:
self.clip = None
@@ -229,7 +229,7 @@ class AnomalyDecoder(Decoder):
decoder_config: AnomalyDecoderConfig instance.
"""
def __init__(self, input_size: int = None, decoder_config=None, **kwargs):
def __init__(self, input_size: int | None = None, decoder_config=None, **kwargs):
super().__init__()
self.config = decoder_config
self.input_size = input_size or 1
@@ -305,7 +305,7 @@ class MLPClassifier(Decoder):
def __init__(
self,
input_size: int,
num_classes: int = None,
num_classes: int | None = None,
num_fc_layers: int = 1,
output_size: int = 256,
activation: str = "relu",
+1 -1
View File
@@ -406,7 +406,7 @@ class CategoryExtractorDecoder(Decoder):
return None
if not hasattr(self.tokenizer, "tokenizer"):
logger.warning(
"constrain_to_vocabulary=True requires an HF tokenizer. " "Falling back to unconstrained generation."
"constrain_to_vocabulary=True requires an HF tokenizer. Falling back to unconstrained generation."
)
return None
+12 -12
View File
@@ -100,18 +100,18 @@ class DateEncoderBase(Encoder):
)
# Store FC stack params for use after subclass sets up component encoders.
self._fc_stack_params = dict(
fc_layers=fc_layers,
num_fc_layers=num_fc_layers,
output_size=output_size,
use_bias=use_bias,
weights_initializer=weights_initializer,
bias_initializer=bias_initializer,
norm=norm,
norm_params=norm_params,
activation=activation,
dropout=dropout,
)
self._fc_stack_params = {
"fc_layers": fc_layers,
"num_fc_layers": num_fc_layers,
"output_size": output_size,
"use_bias": use_bias,
"weights_initializer": weights_initializer,
"bias_initializer": bias_initializer,
"norm": norm,
"norm_params": norm_params,
"activation": activation,
"dropout": dropout,
}
def _build_fc_stack(self, component_output_size: int):
"""Build the final FC stack given the total size of encoded components.
+3 -3
View File
@@ -61,8 +61,8 @@ class H3Embed(Encoder):
use_bias: bool = True,
weights_initializer: str = "xavier_uniform",
bias_initializer: str = "zeros",
norm: str = None,
norm_params: dict = None,
norm: str | None = None,
norm_params: dict | None = None,
activation: str = "relu",
dropout: float = 0,
reduce_output: str = "sum",
@@ -237,7 +237,7 @@ class H3WeightedSum(Encoder):
weights_initializer: str = "xavier_uniform",
bias_initializer: str = "zeros",
norm: str | None = None,
norm_params: dict = None,
norm_params: dict | None = None,
activation: str = "relu",
dropout: float = 0,
encoder_config=None,
+2 -2
View File
@@ -1,4 +1,4 @@
import ludwig.encoders.image.base
import ludwig.encoders.image.pretrained # noqa
import ludwig.encoders.image.timm # noqa
import ludwig.encoders.image.pretrained
import ludwig.encoders.image.timm
import ludwig.encoders.image.torchvision # noqa
+3 -3
View File
@@ -50,7 +50,7 @@ class Stacked2DCNN(ImageEncoder):
width: int,
conv_layers: list[dict] | None = None,
num_conv_layers: int | None = None,
num_channels: int = None,
num_channels: int | None = None,
out_channels: int = 32,
kernel_size: int | tuple[int] = 3,
stride: int | tuple[int] = 1,
@@ -64,7 +64,7 @@ class Stacked2DCNN(ImageEncoder):
conv_dropout: int = 0,
pool_function: str = "max",
pool_kernel_size: int | tuple[int] = 2,
pool_stride: int | tuple[int] = None,
pool_stride: int | tuple[int] | None = None,
pool_padding: int | tuple[int] = 0,
pool_dilation: int | tuple[int] = 1,
groups: int = 1,
@@ -173,7 +173,7 @@ class MLPMixerEncoder(ImageEncoder):
self,
height: int,
width: int,
num_channels: int = None,
num_channels: int | None = None,
patch_size: int = 16,
embed_size: int = 512,
token_size: int = 2048,
+1 -1
View File
@@ -49,7 +49,7 @@ class TVBaseEncoder(ImageEncoder):
def __init__(
self,
model_variant: str | int = None,
model_variant: str | int | None = None,
use_pretrained: bool = True,
saved_weights_in_checkpoint: bool = False,
model_cache_dir: str | None = None,
+7 -7
View File
@@ -75,9 +75,9 @@ class SequencePassthroughEncoder(SequenceEncoder):
def __init__(
self,
reduce_output: str = None,
reduce_output: str | None = None,
max_sequence_length: int = 256,
encoding_size: int = None,
encoding_size: int | None = None,
encoder_config=None,
**kwargs,
):
@@ -851,7 +851,7 @@ class StackedCNN(SequenceEncoder):
]
self.num_conv_layers = 6
else:
raise ValueError("Invalid layer parametrization, use either conv_layers or " "num_conv_layers")
raise ValueError("Invalid layer parametrization, use either conv_layers or num_conv_layers")
# The user is expected to provide fc_layers or num_fc_layers
# The following logic handles the case where the user either provides
@@ -861,7 +861,7 @@ class StackedCNN(SequenceEncoder):
fc_layers = [{"output_size": 512}, {"output_size": 256}]
num_fc_layers = 2
elif fc_layers is not None and num_fc_layers is not None:
raise ValueError("Invalid layer parametrization, use either fc_layers or " "num_fc_layers only. Not both.")
raise ValueError("Invalid layer parametrization, use either fc_layers or num_fc_layers only. Not both.")
self.max_sequence_length = max_sequence_length
self.num_filters = num_filters
@@ -1193,7 +1193,7 @@ class StackedParallelCNN(SequenceEncoder):
]
self.num_stacked_layers = 6
else:
raise ValueError("Invalid layer parametrization, use either stacked_layers or" " num_stacked_layers")
raise ValueError("Invalid layer parametrization, use either stacked_layers or num_stacked_layers")
# The user is expected to provide fc_layers or num_fc_layers
# The following logic handles the case where the user either provides
@@ -1203,7 +1203,7 @@ class StackedParallelCNN(SequenceEncoder):
fc_layers = [{"output_size": 512}, {"output_size": 256}]
num_fc_layers = 2
elif fc_layers is not None and num_fc_layers is not None:
raise ValueError("Invalid layer parametrization, use either fc_layers or " "num_fc_layers only. Not both.")
raise ValueError("Invalid layer parametrization, use either fc_layers or num_fc_layers only. Not both.")
self.should_embed = should_embed
self.embed_sequence = None
@@ -1776,7 +1776,7 @@ class StackedCNNRNN(SequenceEncoder):
self.conv_layers = [{"pool_size": 3}, {"pool_size": None}]
self.num_conv_layers = 2
else:
raise ValueError("Invalid layer parametrization, use either conv_layers or " "num_conv_layers")
raise ValueError("Invalid layer parametrization, use either conv_layers or num_conv_layers")
self.max_sequence_length = max_sequence_length
self.should_embed = should_embed
+270 -270
View File
@@ -290,7 +290,7 @@ class ALBERTEncoder(HFTextEncoder):
pad_token_id: int = 0,
bos_token_id: int = 2,
eos_token_id: int = 3,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -298,28 +298,28 @@ class ALBERTEncoder(HFTextEncoder):
from transformers import AlbertConfig, AlbertModel
hf_config_params = dict(
vocab_size=vocab_size,
embedding_size=embedding_size,
hidden_size=hidden_size,
num_hidden_layers=num_hidden_layers,
num_hidden_groups=num_hidden_groups,
num_attention_heads=num_attention_heads,
intermediate_size=intermediate_size,
inner_group_num=inner_group_num,
hidden_act=hidden_act,
hidden_dropout_prob=hidden_dropout_prob,
attention_probs_dropout_prob=attention_probs_dropout_prob,
max_position_embeddings=max_position_embeddings,
type_vocab_size=type_vocab_size,
initializer_range=initializer_range,
layer_norm_eps=layer_norm_eps,
classifier_dropout_prob=classifier_dropout_prob,
position_embedding_type=position_embedding_type,
pad_token_id=pad_token_id,
bos_token_id=bos_token_id,
eos_token_id=eos_token_id,
)
hf_config_params = {
"vocab_size": vocab_size,
"embedding_size": embedding_size,
"hidden_size": hidden_size,
"num_hidden_layers": num_hidden_layers,
"num_hidden_groups": num_hidden_groups,
"num_attention_heads": num_attention_heads,
"intermediate_size": intermediate_size,
"inner_group_num": inner_group_num,
"hidden_act": hidden_act,
"hidden_dropout_prob": hidden_dropout_prob,
"attention_probs_dropout_prob": attention_probs_dropout_prob,
"max_position_embeddings": max_position_embeddings,
"type_vocab_size": type_vocab_size,
"initializer_range": initializer_range,
"layer_norm_eps": layer_norm_eps,
"classifier_dropout_prob": classifier_dropout_prob,
"position_embedding_type": position_embedding_type,
"pad_token_id": pad_token_id,
"bos_token_id": bos_token_id,
"eos_token_id": eos_token_id,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -416,7 +416,7 @@ class MT5Encoder(HFTextEncoder):
d_kv: int = 64,
d_ff: int = 1024,
num_layers: int = 8,
num_decoder_layers: int = None,
num_decoder_layers: int | None = None,
num_heads: int = 6,
relative_attention_num_buckets: int = 32,
dropout_rate: float = 0.1,
@@ -430,7 +430,7 @@ class MT5Encoder(HFTextEncoder):
pad_token_id: int = 0,
eos_token_id: int = 1,
decoder_start_token_id: int = 0,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -438,27 +438,27 @@ class MT5Encoder(HFTextEncoder):
from transformers import MT5Config, MT5EncoderModel
hf_config_params = dict(
vocab_size=vocab_size,
d_model=d_model,
d_kv=d_kv,
d_ff=d_ff,
num_layers=num_layers,
num_decoder_layers=num_decoder_layers,
num_heads=num_heads,
relative_attention_num_buckets=relative_attention_num_buckets,
dropout_rate=dropout_rate,
layer_norm_epsilon=layer_norm_epsilon,
initializer_factor=initializer_factor,
feed_forward_proj=feed_forward_proj,
is_encoder_decoder=is_encoder_decoder,
use_cache=use_cache,
tokenizer_class=tokenizer_class,
tie_word_embeddings=tie_word_embeddings,
pad_token_id=pad_token_id,
eos_token_id=eos_token_id,
decoder_start_token_id=decoder_start_token_id,
)
hf_config_params = {
"vocab_size": vocab_size,
"d_model": d_model,
"d_kv": d_kv,
"d_ff": d_ff,
"num_layers": num_layers,
"num_decoder_layers": num_decoder_layers,
"num_heads": num_heads,
"relative_attention_num_buckets": relative_attention_num_buckets,
"dropout_rate": dropout_rate,
"layer_norm_epsilon": layer_norm_epsilon,
"initializer_factor": initializer_factor,
"feed_forward_proj": feed_forward_proj,
"is_encoder_decoder": is_encoder_decoder,
"use_cache": use_cache,
"tokenizer_class": tokenizer_class,
"tie_word_embeddings": tie_word_embeddings,
"pad_token_id": pad_token_id,
"eos_token_id": eos_token_id,
"decoder_start_token_id": decoder_start_token_id,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -547,14 +547,14 @@ class XLMRoBERTaEncoder(HFTextEncoder):
reduce_output: str = "cls_pooled",
trainable: bool = False,
adapter: BaseAdapterConfig | None = None,
vocab_size: int = None,
vocab_size: int | None = None,
pad_token_id: int = 1,
bos_token_id: int = 0,
eos_token_id: int = 2,
max_position_embeddings: int = 514,
type_vocab_size: int = 1,
add_pooling_layer: bool = True,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -562,13 +562,13 @@ class XLMRoBERTaEncoder(HFTextEncoder):
from transformers import XLMRobertaConfig, XLMRobertaModel
hf_config_params = dict(
pad_token_id=pad_token_id,
bos_token_id=bos_token_id,
eos_token_id=eos_token_id,
max_position_embeddings=max_position_embeddings,
type_vocab_size=type_vocab_size,
)
hf_config_params = {
"pad_token_id": pad_token_id,
"bos_token_id": bos_token_id,
"eos_token_id": eos_token_id,
"max_position_embeddings": max_position_embeddings,
"type_vocab_size": type_vocab_size,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -678,8 +678,8 @@ class BERTEncoder(HFTextEncoder):
pad_token_id: int = 0,
gradient_checkpointing: bool = False,
position_embedding_type: str = "absolute",
classifier_dropout: float = None,
pretrained_kwargs: dict = None,
classifier_dropout: float | None = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -687,24 +687,24 @@ class BERTEncoder(HFTextEncoder):
from transformers import BertConfig, BertModel
hf_config_params = dict(
vocab_size=vocab_size,
hidden_size=hidden_size,
num_hidden_layers=num_hidden_layers,
num_attention_heads=num_attention_heads,
intermediate_size=intermediate_size,
hidden_act=hidden_act,
hidden_dropout_prob=hidden_dropout_prob,
attention_probs_dropout_prob=attention_probs_dropout_prob,
max_position_embeddings=max_position_embeddings,
type_vocab_size=type_vocab_size,
initializer_range=initializer_range,
layer_norm_eps=layer_norm_eps,
pad_token_id=pad_token_id,
gradient_checkpointing=gradient_checkpointing,
position_embedding_type=position_embedding_type,
classifier_dropout=classifier_dropout,
)
hf_config_params = {
"vocab_size": vocab_size,
"hidden_size": hidden_size,
"num_hidden_layers": num_hidden_layers,
"num_attention_heads": num_attention_heads,
"intermediate_size": intermediate_size,
"hidden_act": hidden_act,
"hidden_dropout_prob": hidden_dropout_prob,
"attention_probs_dropout_prob": attention_probs_dropout_prob,
"max_position_embeddings": max_position_embeddings,
"type_vocab_size": type_vocab_size,
"initializer_range": initializer_range,
"layer_norm_eps": layer_norm_eps,
"pad_token_id": pad_token_id,
"gradient_checkpointing": gradient_checkpointing,
"position_embedding_type": position_embedding_type,
"classifier_dropout": classifier_dropout,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -827,7 +827,7 @@ class XLMEncoder(HFTextEncoder):
lang_id: int = 0,
pad_token_id: int = 2,
bos_token_id: int = 0,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -835,36 +835,36 @@ class XLMEncoder(HFTextEncoder):
from transformers import XLMConfig, XLMModel
hf_config_params = dict(
vocab_size=vocab_size,
emb_dim=emb_dim,
n_layers=n_layers,
n_heads=n_heads,
dropout=dropout,
attention_dropout=attention_dropout,
gelu_activation=gelu_activation,
sinusoidal_embeddings=sinusoidal_embeddings,
causal=causal,
asm=asm,
n_langs=n_langs,
use_lang_emb=use_lang_emb,
max_position_embeddings=max_position_embeddings,
embed_init_std=embed_init_std,
layer_norm_eps=layer_norm_eps,
init_std=init_std,
bos_index=bos_index,
eos_index=eos_index,
pad_index=pad_index,
unk_index=unk_index,
mask_index=mask_index,
is_encoder=is_encoder,
start_n_top=start_n_top,
end_n_top=end_n_top,
mask_token_id=mask_token_id,
lang_id=lang_id,
pad_token_id=pad_token_id,
bos_token_id=bos_token_id,
)
hf_config_params = {
"vocab_size": vocab_size,
"emb_dim": emb_dim,
"n_layers": n_layers,
"n_heads": n_heads,
"dropout": dropout,
"attention_dropout": attention_dropout,
"gelu_activation": gelu_activation,
"sinusoidal_embeddings": sinusoidal_embeddings,
"causal": causal,
"asm": asm,
"n_langs": n_langs,
"use_lang_emb": use_lang_emb,
"max_position_embeddings": max_position_embeddings,
"embed_init_std": embed_init_std,
"layer_norm_eps": layer_norm_eps,
"init_std": init_std,
"bos_index": bos_index,
"eos_index": eos_index,
"pad_index": pad_index,
"unk_index": unk_index,
"mask_index": mask_index,
"is_encoder": is_encoder,
"start_n_top": start_n_top,
"end_n_top": end_n_top,
"mask_token_id": mask_token_id,
"lang_id": lang_id,
"pad_token_id": pad_token_id,
"bos_token_id": bos_token_id,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -963,7 +963,7 @@ class GPTEncoder(HFTextEncoder):
attn_pdrop: float = 0.1,
layer_norm_epsilon: float = 1e-5,
initializer_range: float = 0.02,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -971,20 +971,20 @@ class GPTEncoder(HFTextEncoder):
from transformers import OpenAIGPTConfig, OpenAIGPTModel
hf_config_params = dict(
vocab_size=vocab_size,
n_positions=n_positions,
n_ctx=n_ctx,
n_embd=n_embd,
n_layer=n_layer,
n_head=n_head,
afn=afn,
resid_pdrop=resid_pdrop,
embd_pdrop=embd_pdrop,
attn_pdrop=attn_pdrop,
layer_norm_epsilon=layer_norm_epsilon,
initializer_range=initializer_range,
)
hf_config_params = {
"vocab_size": vocab_size,
"n_positions": n_positions,
"n_ctx": n_ctx,
"n_embd": n_embd,
"n_layer": n_layer,
"n_head": n_head,
"afn": afn,
"resid_pdrop": resid_pdrop,
"embd_pdrop": embd_pdrop,
"attn_pdrop": attn_pdrop,
"layer_norm_epsilon": layer_norm_epsilon,
"initializer_range": initializer_range,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1082,7 +1082,7 @@ class GPT2Encoder(HFTextEncoder):
layer_norm_epsilon: float = 1e-5,
initializer_range: float = 0.02,
scale_attn_weights: bool = True,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -1090,22 +1090,22 @@ class GPT2Encoder(HFTextEncoder):
from transformers import GPT2Config, GPT2Model
hf_config_params = dict(
vocab_size=vocab_size,
n_positions=n_positions,
n_ctx=n_ctx,
n_embd=n_embd,
n_layer=n_layer,
n_head=n_head,
n_inner=n_inner,
activation_function=activation_function,
resid_pdrop=resid_pdrop,
embd_pdrop=embd_pdrop,
attn_pdrop=attn_pdrop,
layer_norm_epsilon=layer_norm_epsilon,
initializer_range=initializer_range,
scale_attn_weights=scale_attn_weights,
)
hf_config_params = {
"vocab_size": vocab_size,
"n_positions": n_positions,
"n_ctx": n_ctx,
"n_embd": n_embd,
"n_layer": n_layer,
"n_head": n_head,
"n_inner": n_inner,
"activation_function": activation_function,
"resid_pdrop": resid_pdrop,
"embd_pdrop": embd_pdrop,
"attn_pdrop": attn_pdrop,
"layer_norm_epsilon": layer_norm_epsilon,
"initializer_range": initializer_range,
"scale_attn_weights": scale_attn_weights,
}
if use_pretrained:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1216,13 +1216,13 @@ class RoBERTaEncoder(HFTextEncoder):
reduce_output: str = "cls_pooled",
trainable: bool = False,
adapter: BaseAdapterConfig | None = None,
vocab_size: int = None,
vocab_size: int | None = None,
pad_token_id: int = 1,
bos_token_id: int = 0,
eos_token_id: int = 2,
max_position_embeddings: int = 514,
type_vocab_size: int = 1,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -1230,13 +1230,13 @@ class RoBERTaEncoder(HFTextEncoder):
from transformers import RobertaConfig, RobertaModel
hf_config_params = dict(
pad_token_id=pad_token_id,
bos_token_id=bos_token_id,
eos_token_id=eos_token_id,
max_position_embeddings=max_position_embeddings,
type_vocab_size=type_vocab_size,
)
hf_config_params = {
"pad_token_id": pad_token_id,
"bos_token_id": bos_token_id,
"eos_token_id": eos_token_id,
"max_position_embeddings": max_position_embeddings,
"type_vocab_size": type_vocab_size,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1350,7 +1350,7 @@ class XLNetEncoder(HFTextEncoder):
pad_token_id: int = 5,
bos_token_id: int = 1,
eos_token_id: int = 2,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -1358,35 +1358,35 @@ class XLNetEncoder(HFTextEncoder):
from transformers import XLNetConfig, XLNetModel
hf_config_params = dict(
vocab_size=vocab_size,
d_model=d_model,
n_layer=n_layer,
n_head=n_head,
d_inner=d_inner,
ff_activation=ff_activation,
untie_r=untie_r,
attn_type=attn_type,
initializer_range=initializer_range,
layer_norm_eps=layer_norm_eps,
dropout=dropout,
mem_len=mem_len,
reuse_len=reuse_len,
use_mems_eval=use_mems_eval,
use_mems_train=use_mems_train,
bi_data=bi_data,
clamp_len=clamp_len,
same_length=same_length,
summary_type=summary_type,
summary_use_proj=summary_use_proj,
summary_activation=summary_activation,
summary_last_dropout=summary_last_dropout,
start_n_top=start_n_top,
end_n_top=end_n_top,
pad_token_id=pad_token_id,
bos_token_id=bos_token_id,
eos_token_id=eos_token_id,
)
hf_config_params = {
"vocab_size": vocab_size,
"d_model": d_model,
"n_layer": n_layer,
"n_head": n_head,
"d_inner": d_inner,
"ff_activation": ff_activation,
"untie_r": untie_r,
"attn_type": attn_type,
"initializer_range": initializer_range,
"layer_norm_eps": layer_norm_eps,
"dropout": dropout,
"mem_len": mem_len,
"reuse_len": reuse_len,
"use_mems_eval": use_mems_eval,
"use_mems_train": use_mems_train,
"bi_data": bi_data,
"clamp_len": clamp_len,
"same_length": same_length,
"summary_type": summary_type,
"summary_use_proj": summary_use_proj,
"summary_activation": summary_activation,
"summary_last_dropout": summary_last_dropout,
"start_n_top": start_n_top,
"end_n_top": end_n_top,
"pad_token_id": pad_token_id,
"bos_token_id": bos_token_id,
"eos_token_id": eos_token_id,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1482,7 +1482,7 @@ class DistilBERTEncoder(HFTextEncoder):
initializer_range: float = 0.02,
qa_dropout: float = 0.1,
seq_classif_dropout: float = 0.2,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -1490,21 +1490,21 @@ class DistilBERTEncoder(HFTextEncoder):
from transformers import DistilBertConfig, DistilBertModel
hf_config_params = dict(
vocab_size=vocab_size,
max_position_embeddings=max_position_embeddings,
sinusoidal_pos_embds=sinusoidal_pos_embds,
n_layers=n_layers,
n_heads=n_heads,
dim=dim,
hidden_dim=hidden_dim,
dropout=dropout,
attention_dropout=attention_dropout,
activation=activation,
initializer_range=initializer_range,
qa_dropout=qa_dropout,
seq_classif_dropout=seq_classif_dropout,
)
hf_config_params = {
"vocab_size": vocab_size,
"max_position_embeddings": max_position_embeddings,
"sinusoidal_pos_embds": sinusoidal_pos_embds,
"n_layers": n_layers,
"n_heads": n_heads,
"dim": dim,
"hidden_dim": hidden_dim,
"dropout": dropout,
"attention_dropout": attention_dropout,
"activation": activation,
"initializer_range": initializer_range,
"qa_dropout": qa_dropout,
"seq_classif_dropout": seq_classif_dropout,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1610,8 +1610,8 @@ class CamemBERTEncoder(HFTextEncoder):
pad_token_id: int = 0,
gradient_checkpointing: bool = False,
position_embedding_type: str = "absolute",
classifier_dropout: float = None,
pretrained_kwargs: dict = None,
classifier_dropout: float | None = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -1619,24 +1619,24 @@ class CamemBERTEncoder(HFTextEncoder):
from transformers import CamembertConfig, CamembertModel
hf_config_params = dict(
vocab_size=vocab_size,
hidden_size=hidden_size,
num_hidden_layers=num_hidden_layers,
num_attention_heads=num_attention_heads,
intermediate_size=intermediate_size,
hidden_act=hidden_act,
hidden_dropout_prob=hidden_dropout_prob,
attention_probs_dropout_prob=attention_probs_dropout_prob,
max_position_embeddings=max_position_embeddings,
type_vocab_size=type_vocab_size,
initializer_range=initializer_range,
layer_norm_eps=layer_norm_eps,
pad_token_id=pad_token_id,
gradient_checkpointing=gradient_checkpointing,
position_embedding_type=position_embedding_type,
classifier_dropout=classifier_dropout,
)
hf_config_params = {
"vocab_size": vocab_size,
"hidden_size": hidden_size,
"num_hidden_layers": num_hidden_layers,
"num_attention_heads": num_attention_heads,
"intermediate_size": intermediate_size,
"hidden_act": hidden_act,
"hidden_dropout_prob": hidden_dropout_prob,
"attention_probs_dropout_prob": attention_probs_dropout_prob,
"max_position_embeddings": max_position_embeddings,
"type_vocab_size": type_vocab_size,
"initializer_range": initializer_range,
"layer_norm_eps": layer_norm_eps,
"pad_token_id": pad_token_id,
"gradient_checkpointing": gradient_checkpointing,
"position_embedding_type": position_embedding_type,
"classifier_dropout": classifier_dropout,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1744,7 +1744,7 @@ class T5Encoder(HFTextEncoder):
layer_norm_eps: float = 1e-6,
initializer_factor: float = 1,
feed_forward_proj: str = "relu",
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -1752,20 +1752,20 @@ class T5Encoder(HFTextEncoder):
from transformers import T5Config, T5Model
hf_config_params = dict(
vocab_size=vocab_size,
d_model=d_model,
d_kv=d_kv,
d_ff=d_ff,
num_layers=num_layers,
num_decoder_layers=num_decoder_layers,
num_heads=num_heads,
relative_attention_num_buckets=relative_attention_num_buckets,
dropout_rate=dropout_rate,
layer_norm_eps=layer_norm_eps,
initializer_factor=initializer_factor,
feed_forward_proj=feed_forward_proj,
)
hf_config_params = {
"vocab_size": vocab_size,
"d_model": d_model,
"d_kv": d_kv,
"d_ff": d_ff,
"num_layers": num_layers,
"num_decoder_layers": num_decoder_layers,
"num_heads": num_heads,
"relative_attention_num_buckets": relative_attention_num_buckets,
"dropout_rate": dropout_rate,
"layer_norm_eps": layer_norm_eps,
"initializer_factor": initializer_factor,
"feed_forward_proj": feed_forward_proj,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1871,7 +1871,7 @@ class ELECTRAEncoder(HFTextEncoder):
layer_norm_eps: float = 1e-12,
position_embedding_type: str = "absolute",
classifier_dropout: float | None = None,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -1879,23 +1879,23 @@ class ELECTRAEncoder(HFTextEncoder):
from transformers import ElectraConfig, ElectraModel
hf_config_params = dict(
vocab_size=vocab_size,
embedding_size=embedding_size,
hidden_size=hidden_size,
num_hidden_layers=num_hidden_layers,
num_attention_heads=num_attention_heads,
intermediate_size=intermediate_size,
hidden_act=hidden_act,
hidden_dropout_prob=hidden_dropout_prob,
attention_probs_dropout_prob=attention_probs_dropout_prob,
max_position_embeddings=max_position_embeddings,
type_vocab_size=type_vocab_size,
initializer_range=initializer_range,
layer_norm_eps=layer_norm_eps,
position_embedding_type=position_embedding_type,
classifier_dropout=classifier_dropout,
)
hf_config_params = {
"vocab_size": vocab_size,
"embedding_size": embedding_size,
"hidden_size": hidden_size,
"num_hidden_layers": num_hidden_layers,
"num_attention_heads": num_attention_heads,
"intermediate_size": intermediate_size,
"hidden_act": hidden_act,
"hidden_dropout_prob": hidden_dropout_prob,
"attention_probs_dropout_prob": attention_probs_dropout_prob,
"max_position_embeddings": max_position_embeddings,
"type_vocab_size": type_vocab_size,
"initializer_range": initializer_range,
"layer_norm_eps": layer_norm_eps,
"position_embedding_type": position_embedding_type,
"classifier_dropout": classifier_dropout,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -1989,7 +1989,7 @@ class LongformerEncoder(HFTextEncoder):
adapter: BaseAdapterConfig | None = None,
vocab_size: int = 50265,
num_tokens: int | None = None,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -2110,7 +2110,7 @@ class ModernBERTEncoder(HFTextEncoder):
initializer_range: float = 0.02,
layer_norm_eps: float = 1e-5,
pad_token_id: int = 50283,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -2118,19 +2118,19 @@ class ModernBERTEncoder(HFTextEncoder):
from transformers import ModernBertConfig, ModernBertModel
hf_config_params = dict(
vocab_size=vocab_size,
hidden_size=hidden_size,
num_hidden_layers=num_hidden_layers,
num_attention_heads=num_attention_heads,
intermediate_size=intermediate_size,
hidden_act=hidden_act,
hidden_dropout_prob=hidden_dropout_prob,
max_position_embeddings=max_position_embeddings,
initializer_range=initializer_range,
layer_norm_eps=layer_norm_eps,
pad_token_id=pad_token_id,
)
hf_config_params = {
"vocab_size": vocab_size,
"hidden_size": hidden_size,
"num_hidden_layers": num_hidden_layers,
"num_attention_heads": num_attention_heads,
"intermediate_size": intermediate_size,
"hidden_act": hidden_act,
"hidden_dropout_prob": hidden_dropout_prob,
"max_position_embeddings": max_position_embeddings,
"initializer_range": initializer_range,
"layer_norm_eps": layer_norm_eps,
"pad_token_id": pad_token_id,
}
if use_pretrained and not saved_weights_in_checkpoint:
pretrained_kwargs = pretrained_kwargs or {}
@@ -2227,7 +2227,7 @@ class AutoTransformerEncoder(HFTextEncoder):
trainable: bool = False,
adapter: BaseAdapterConfig | None = None,
vocab_size: int | None = None,
pretrained_kwargs: dict = None,
pretrained_kwargs: dict | None = None,
encoder_config=None,
**kwargs,
):
@@ -2269,11 +2269,11 @@ class AutoTransformerEncoder(HFTextEncoder):
# The forward signature of AutoModel is not consistent across implementations, so we need to make sure we're
# only passing in params included in the forward signature.
kwargs = dict(
input_ids=inputs,
attention_mask=mask,
token_type_ids=torch.zeros_like(inputs),
)
kwargs = {
"input_ids": inputs,
"attention_mask": mask,
"token_type_ids": torch.zeros_like(inputs),
}
kwargs = {k: v for k, v in kwargs.items() if k in self.forward_kwargs}
transformer_outputs = self.transformer.module(**kwargs)
@@ -2343,7 +2343,7 @@ class TfIdfEncoder(Encoder):
encoder_config=None,
str2idf=None,
vocab=None,
vocab_size: int = None,
vocab_size: int | None = None,
ngram_range: tuple[int, int] = (1, 1),
max_df: float = 1.0,
min_df: int = 1,
@@ -2604,7 +2604,7 @@ class LLMEncoder(Encoder):
if self.config.adapter and self.adapter_is_initialized:
adapter_type_prefix = self.ADAPTER_PARAM_NAME_PREFIX[self.config.adapter.type]
exclude_model_keys = [k for k in destination.keys() if adapter_type_prefix not in k]
exclude_model_keys = [k for k in destination if adapter_type_prefix not in k]
for k in exclude_model_keys:
del destination[k]
@@ -2679,6 +2679,6 @@ class LLMEncoder(Encoder):
from peft.utils.save_and_load import get_peft_model_state_dict
sd = get_peft_model_state_dict(self.model)
for k in sd.keys():
for k in sd:
if k in unexpected_keys:
unexpected_keys.remove(k)
+5 -5
View File
@@ -33,7 +33,7 @@ logger = logging.getLogger(__name__)
def evaluate_cli(
model_path: str,
dataset: str | dict | pd.DataFrame = None,
data_format: str = None,
data_format: str | None = None,
split: str = FULL,
batch_size: int = 128,
skip_save_unprocessed_output: bool = False,
@@ -42,10 +42,10 @@ def evaluate_cli(
skip_collect_predictions: bool = False,
skip_collect_overall_stats: bool = False,
output_directory: str = "results",
gpus: str | int | list[int] = None,
gpus: str | int | list[int] | None = None,
gpu_memory_limit: float | None = None,
allow_parallel_threads: bool = True,
callbacks: list[Callback] = None,
callbacks: list[Callback] | None = None,
backend: Backend | str = None,
logging_level: int = logging.INFO,
**kwargs,
@@ -150,7 +150,7 @@ def cli(sys_argv):
"feather",
"fwf",
"hdf5",
"html" "tables",
"htmltables",
"json",
"jsonl",
"parquet",
@@ -227,7 +227,7 @@ def cli(sys_argv):
parser.add_argument(
"-b",
"--backend",
help="specifies backend to use for parallel / distributed execution, " "defaults to local execution",
help="specifies backend to use for parallel / distributed execution, defaults to local execution",
choices=ALL_BACKENDS,
)
parser.add_argument(
+8 -8
View File
@@ -39,12 +39,12 @@ def experiment_cli(
training_set: str | dict | pd.DataFrame = None,
validation_set: str | dict | pd.DataFrame = None,
test_set: str | dict | pd.DataFrame = None,
training_set_metadata: str | dict = None,
data_format: str = None,
training_set_metadata: str | dict | None = None,
data_format: str | None = None,
experiment_name: str = "experiment",
model_name: str = "run",
model_load_path: str = None,
model_resume_path: str = None,
model_load_path: str | None = None,
model_resume_path: str | None = None,
eval_split: str = TEST,
skip_save_training_description: bool = False,
skip_save_training_statistics: bool = False,
@@ -58,10 +58,10 @@ def experiment_cli(
skip_collect_predictions: bool = False,
skip_collect_overall_stats: bool = False,
output_directory: str = "results",
gpus: str | int | list[int] = None,
gpus: str | int | list[int] | None = None,
gpu_memory_limit: float | None = None,
allow_parallel_threads: bool = True,
callbacks: list[Callback] = None,
callbacks: list[Callback] | None = None,
backend: Backend | str = None,
random_seed: int = default_random_seed,
logging_level: int = logging.INFO,
@@ -327,7 +327,7 @@ def cli(sys_argv):
"feather",
"fwf",
"hdf5",
"html" "tables",
"htmltables",
"json",
"jsonl",
"parquet",
@@ -492,7 +492,7 @@ def cli(sys_argv):
parser.add_argument(
"-b",
"--backend",
help="specifies backend to use for parallel / distributed execution, " "defaults to local execution",
help="specifies backend to use for parallel / distributed execution, defaults to local execution",
choices=ALL_BACKENDS,
)
parser.add_argument(
+1 -1
View File
@@ -110,7 +110,7 @@ class WrapperModule(torch.nn.Module):
self.input_maps.update(
{
arg_name: InputIdentity(arg_name)
for arg_name in self.model.input_features.keys()
for arg_name in self.model.input_features
if self.model.input_features.get(arg_name).type() not in EMBEDDED_TYPES
}
)
+1 -1
View File
@@ -24,7 +24,7 @@ from ludwig.utils.torch_utils import get_torch_device
@PublicAPI(stability="experimental")
class RayIntegratedGradientsExplainer(IntegratedGradientsExplainer):
def __init__(self, *args, resources_per_task: dict[str, Any] = None, num_workers: int = 1, **kwargs):
def __init__(self, *args, resources_per_task: dict[str, Any] | None = None, num_workers: int = 1, **kwargs):
super().__init__(*args, **kwargs)
self.resources_per_task = resources_per_task or {}
self.num_workers = num_workers
+2 -2
View File
@@ -29,7 +29,7 @@ class LabelExplanation:
# The attribution for each input feature.
feature_attributions: list[FeatureAttribution] = field(default_factory=list)
def add(self, feature_name: str, attribution: float, token_attributions: list[tuple[str, float]] = None):
def add(self, feature_name: str, attribution: float, token_attributions: list[tuple[str, float]] | None = None):
"""Add the attribution for a single input feature."""
self.feature_attributions.append(FeatureAttribution(feature_name, attribution, token_attributions))
@@ -55,7 +55,7 @@ class Explanation:
self,
feat_names: list[str],
feat_attributions: npt.NDArray[np.float64],
feat_to_token_attributions: dict[str, list[tuple[str, float]]] = None,
feat_to_token_attributions: dict[str, list[tuple[str, float]]] | None = None,
prepend: bool = False,
):
"""Add the feature attributions for a single label."""
+2 -2
View File
@@ -33,7 +33,7 @@ def prepare_data(model: LudwigModel, inputs_df: pd.DataFrame, sample_df: pd.Data
def get_pred_col(preds, target):
t = target.lower()
for c in preds.keys():
for c in preds:
if c.lower() == t:
if "probabilities" in preds[c]:
return preds[c]["probabilities"]
@@ -44,7 +44,7 @@ def get_pred_col(preds, target):
def get_feature_name(model: LudwigModel, target: str) -> str:
t = target.lower()
for c in model.training_set_metadata.keys():
for c in model.training_set_metadata:
if c.lower() == t:
return c
raise ValueError(f"Unable to find target column {t} in {model.training_set_metadata.keys()}")
+9 -9
View File
@@ -301,9 +301,9 @@ class AudioFeatureMixin(BaseFeatureMixin):
if num_fft_points < window_length_in_samp:
raise ValueError(
"num_fft_points: {} < window length in "
"samples: {} (corresponds to window length"
" in s: {}".format(num_fft_points, window_length_in_s, window_length_in_samp)
f"num_fft_points: {num_fft_points} < window length in "
f"samples: {window_length_in_s} (corresponds to window length"
f" in s: {window_length_in_samp}"
)
else:
num_fft_points = window_length_in_samp
@@ -366,8 +366,8 @@ class AudioFeatureMixin(BaseFeatureMixin):
if not isinstance(first_audio_entry, str) and not isinstance(first_audio_entry, torch.Tensor):
raise ValueError(
"Invalid audio feature data type. Detected type is {}, "
"expected either string for local/remote file path or Torch Tensor.".format(type(first_audio_entry))
f"Invalid audio feature data type. Detected type is {type(first_audio_entry)}, "
"expected either string for local/remote file path or Torch Tensor."
)
src_path = None
@@ -420,8 +420,8 @@ class AudioFeatureMixin(BaseFeatureMixin):
if not audio_length_limit_in_samp.is_integer():
raise ValueError(
"Audio_file_length_limit has to be chosen "
"so that {} (in s) * {} (sampling rate in Hz) "
"is an integer.".format(audio_length_limit_in_s, sampling_rate_in_hz)
f"so that {audio_length_limit_in_s} (in s) * {sampling_rate_in_hz} (sampling rate in Hz) "
"is an integer."
)
audio_length_limit_in_samp = int(audio_length_limit_in_samp)
@@ -442,9 +442,9 @@ class AudioInputFeature(AudioFeatureMixin, SequenceInputFeature):
super().__init__(input_feature_config, encoder_obj=encoder_obj, **kwargs)
if not getattr(self.encoder_obj.config, "embedding_size", None):
raise ValueError("embedding_size has to be defined - " 'check "update_config_with_metadata()"')
raise ValueError('embedding_size has to be defined - check "update_config_with_metadata()"')
if not getattr(self.encoder_obj.config, "max_sequence_length", None):
raise ValueError("max_sequence_length has to be defined - " 'check "update_config_with_metadata()"')
raise ValueError('max_sequence_length has to be defined - check "update_config_with_metadata()"')
def forward(self, inputs, mask=None):
if not isinstance(inputs, torch.Tensor):
+2 -2
View File
@@ -47,7 +47,7 @@ class _BinaryPreprocessing(torch.nn.Module):
def __init__(self, metadata: TrainingSetMetadataDict):
super().__init__()
str2bool = metadata.get("str2bool")
self.str2bool = str2bool or {v: True for v in strings_utils.BOOL_TRUE_STRS}
self.str2bool = str2bool or dict.fromkeys(strings_utils.BOOL_TRUE_STRS, True)
self.should_lower = str2bool is None
def forward(self, v: PreprocessingInput) -> torch.Tensor:
@@ -71,7 +71,7 @@ class _BinaryPostprocessing(torch.nn.Module):
def __init__(self, metadata: TrainingSetMetadataDict):
super().__init__()
bool2str = metadata.get("bool2str")
self.bool2str = {i: v for i, v in enumerate(bool2str)} if bool2str is not None else None
self.bool2str = dict(enumerate(bool2str)) if bool2str is not None else None
self.predictions_key = PREDICTIONS
self.probabilities_key = PROBABILITIES
+13 -17
View File
@@ -80,7 +80,7 @@ class _CategoryPreprocessing(torch.nn.Module):
class _CategoryPostprocessing(torch.nn.Module):
def __init__(self, metadata: TrainingSetMetadataDict):
super().__init__()
self.idx2str = {i: v for i, v in enumerate(metadata["idx2str"])}
self.idx2str = dict(enumerate(metadata["idx2str"]))
self.unk = UNKNOWN_SYMBOL
self.predictions_key = PREDICTIONS
self.probabilities_key = PROBABILITIES
@@ -158,7 +158,7 @@ class CategoryFeatureMixin(BaseFeatureMixin):
processor=backend.df_engine,
)
if "vocab" in preprocessing_parameters and preprocessing_parameters["vocab"]: # Check that vocab is non-empty
if preprocessing_parameters.get("vocab"): # Check that vocab is non-empty
# If vocab was explciitly provided, override the inferred vocab
idx2str = preprocessing_parameters["vocab"]
str2idx = {s: i for i, s in enumerate(idx2str)}
@@ -180,13 +180,13 @@ class CategoryFeatureMixin(BaseFeatureMixin):
CATEGORY,
f"""
At least 2 distinct values are required for category output features, but column
only contains {str(idx2str)}.
only contains {idx2str!s}.
""",
)
if vocab_size <= 1:
# Category input feature with vocab size 1
logger.info(
f"Input feature '{column.name}' contains only 1 distinct value {str(idx2str)}. This is not useful"
f"Input feature '{column.name}' contains only 1 distinct value {idx2str!s}. This is not useful"
" for machine learning models because this feature has zero variance. Consider removing this feature"
" from your input features."
)
@@ -413,7 +413,7 @@ class CategoryOutputFeature(CategoryFeatureMixin, OutputFeature):
if feature_metadata["str2idx"].keys() != feature_config.loss.class_weights.keys():
raise ValueError(
f"The class_weights keys ({feature_config.loss.class_weights.keys()}) are not compatible with "
f'the classes ({feature_metadata["str2idx"].keys()}) of feature {feature_config.column}. '
f"the classes ({feature_metadata['str2idx'].keys()}) of feature {feature_config.column}. "
"Check the metadata JSON file to see the classes "
"and consider there needs to be a weight "
"for the <UNK> class too."
@@ -441,12 +441,10 @@ class CategoryOutputFeature(CategoryFeatureMixin, OutputFeature):
curr_row_length = len(row)
if curr_row_length != first_row_length:
raise ValueError(
"The length of row {} of the class_similarities "
"of {} is {}, different from the length of "
"the first row {}. All rows must have "
"the same length.".format(
curr_row, feature_config.column, curr_row_length, first_row_length
)
f"The length of row {curr_row} of the class_similarities "
f"of {feature_config.column} is {curr_row_length}, different from the length of "
f"the first row {first_row_length}. All rows must have "
"the same length."
)
else:
curr_row += 1
@@ -454,11 +452,9 @@ class CategoryOutputFeature(CategoryFeatureMixin, OutputFeature):
if all_rows_length != len(similarities):
raise ValueError(
"The class_similarities matrix of {} has "
"{} rows and {} columns, "
"their number must be identical.".format(
feature_config.column, len(similarities), all_rows_length
)
f"The class_similarities matrix of {feature_config.column} has "
f"{len(similarities)} rows and {all_rows_length} columns, "
"their number must be identical."
)
if all_rows_length != feature_config.num_classes:
@@ -479,7 +475,7 @@ class CategoryOutputFeature(CategoryFeatureMixin, OutputFeature):
raise ValueError(
"class_similarities_temperature > 0, "
"but no class_similarities are provided "
"for feature {}".format(feature_config.column)
f"for feature {feature_config.column}"
)
@staticmethod
+1 -1
View File
@@ -90,7 +90,7 @@ class DateFeatureMixin(BaseFeatureMixin):
"in the config. "
"The preprocessing fill in value will be used."
"For more details: "
"https://ludwig-ai.github.io/ludwig-docs/latest/configuration/features/date_features/#date-features-preprocessing" # noqa
"https://ludwig-ai.github.io/ludwig-docs/latest/configuration/features/date_features/#date-features-preprocessing"
)
fill_value = preprocessing_parameters["fill_value"]
if fill_value != "":
+11 -12
View File
@@ -29,9 +29,9 @@ FEATURE_NAME_SUFFIX_LENGTH = len(FEATURE_NAME_SUFFIX)
def should_regularize(regularize_layers):
regularize = False
if isinstance(regularize_layers, bool) and regularize_layers:
regularize = True
elif isinstance(regularize_layers, (list, tuple)) and regularize_layers and regularize_layers[-1]:
if (isinstance(regularize_layers, bool) and regularize_layers) or (
isinstance(regularize_layers, (list, tuple)) and regularize_layers and regularize_layers[-1]
):
regularize = True
return regularize
@@ -117,15 +117,17 @@ def compute_feature_hash(feature: dict) -> str:
Returns: Feature hash name
"""
feature_data = dict(
preprocessing=feature.get(PREPROCESSING, {}),
type=feature[TYPE],
)
feature_data = {
"preprocessing": feature.get(PREPROCESSING, {}),
"type": feature[TYPE],
}
return sanitize(feature[NAME]) + "_" + hash_dict(feature_data).decode("ascii")
def get_input_size_with_dependencies(
combiner_output_size: int, dependencies: list[str], other_output_features # Dict[str, "OutputFeature"]
combiner_output_size: int,
dependencies: list[str],
other_output_features, # Dict[str, "OutputFeature"]
):
"""Returns the input size for the first layer of this output feature's FC stack, accounting for dependencies on
other output features.
@@ -193,10 +195,7 @@ class LudwigFeatureDict(torch.nn.Module):
return iter(self.keys())
def keys(self) -> list[str]:
return [
get_name_from_module_dict_key(feature_name)
for feature_name in self.internal_key_to_original_name_map.keys()
]
return [get_name_from_module_dict_key(feature_name) for feature_name in self.internal_key_to_original_name_map]
def values(self) -> list[torch.nn.Module]:
return [module for _, module in self.module_dict.items()]
+16 -18
View File
@@ -513,31 +513,31 @@ class ImageFeatureMixin(BaseFeatureMixin):
if img_num_channels != num_channels:
logger.warning(
"Image has {} channels, where as {} "
f"Image has {img_num_channels} channels, where as {num_channels} "
"channels are expected. Dropping/adding channels "
"with 0s as appropriate".format(img_num_channels, num_channels)
"with 0s as appropriate"
)
else:
# If the image isn't like the first image, raise exception
if img_num_channels != num_channels:
raise ValueError(
"Image has {} channels, unlike the first image, which "
"has {} channels. Make sure all the images have the same "
f"Image has {img_num_channels} channels, unlike the first image, which "
f"has {num_channels} channels. Make sure all the images have the same "
"number of channels or use the num_channels property in "
"image preprocessing".format(img_num_channels, num_channels)
"image preprocessing"
)
if img.shape[1] != img_height or img.shape[2] != img_width:
raise ValueError(
"Images are not of the same size. "
"Expected size is {}, "
"current image size is {}."
f"Expected size is {[img_height, img_width, num_channels]}, "
f"current image size is {img.shape}."
"Images are expected to be all of the same size "
"or explicit image width and height are expected "
"to be provided. "
"Additional information: "
"https://ludwig-ai.github.io/ludwig-docs/latest/configuration/features/image_features"
"#image-features-preprocessing".format([img_height, img_width, num_channels], img.shape)
"#image-features-preprocessing"
)
# Create class-masked image if required
@@ -599,9 +599,7 @@ class ImageFeatureMixin(BaseFeatureMixin):
# Update preprocessing parameters dictionary to reflect new height and width values
preprocessing_parameters["width"] = width
preprocessing_parameters["height"] = height
logger.info(
f"Set image feature height and width to {width} to be compatible with" f" {encoder_type} encoder."
)
logger.info(f"Set image feature height and width to {width} to be compatible with {encoder_type} encoder.")
return width, height
@staticmethod
@@ -706,8 +704,8 @@ class ImageFeatureMixin(BaseFeatureMixin):
)
elif num_classes > inferred_num_classes:
logger.warning(
"Images inferred num classes {} does not match `num_classes` {}. "
"Using inferred num classes {}.".format(inferred_num_classes, num_classes, inferred_num_classes)
f"Images inferred num classes {inferred_num_classes} does not match `num_classes` {num_classes}. "
f"Using inferred num classes {inferred_num_classes}."
)
return channel_class_map
@@ -777,7 +775,7 @@ class ImageFeatureMixin(BaseFeatureMixin):
width, height, preprocessing_parameters, encoder_type
)
except ValueError as e:
raise ValueError("Image height and width must be set and have " "positive integer values: " + str(e))
raise ValueError("Image height and width must be set and have positive integer values: " + str(e))
if height <= 0 or width <= 0:
raise ValueError("Image height and width must be positive integers")
else:
@@ -861,7 +859,7 @@ class ImageFeatureMixin(BaseFeatureMixin):
name = feature_config[NAME]
column = input_df[feature_config[COLUMN]]
encoder_type = feature_config[ENCODER][TYPE] if ENCODER in feature_config.keys() else None
encoder_type = feature_config[ENCODER][TYPE] if ENCODER in feature_config else None
src_path = None
if SRC in metadata:
@@ -872,8 +870,8 @@ class ImageFeatureMixin(BaseFeatureMixin):
)
# determine if specified encoder is a torchvision model
model_type = feature_config[ENCODER].get("type", None) if ENCODER in feature_config.keys() else None
model_variant = feature_config[ENCODER].get("model_variant") if ENCODER in feature_config.keys() else None
model_type = feature_config[ENCODER].get("type", None) if ENCODER in feature_config else None
model_variant = feature_config[ENCODER].get("model_variant") if ENCODER in feature_config else None
if model_variant:
torchvision_parameters = _get_torchvision_parameters(model_type, model_variant)
else:
@@ -1090,7 +1088,7 @@ class ImageOutputFeature(ImageFeatureMixin, OutputFeature):
return self.decoder_obj(inputs, target=target)
def metric_kwargs(self):
return dict(num_outputs=self.output_shape[0])
return {"num_outputs": self.output_shape[0]}
def create_predict_module(self) -> PredictModule:
return _ImagePredict()
+6 -7
View File
@@ -64,7 +64,7 @@ class NumberTransformer(nn.Module, ABC):
class ZScoreTransformer(NumberTransformer):
def __init__(self, mean: float = None, std: float = None, **kwargs: dict):
def __init__(self, mean: float | None = None, std: float | None = None, **kwargs: dict):
super().__init__()
self.mu = float(mean) if mean is not None else mean
self.sigma = float(std) if std is not None else std
@@ -99,7 +99,7 @@ class ZScoreTransformer(NumberTransformer):
class MinMaxTransformer(NumberTransformer):
def __init__(self, min: float = None, max: float = None, **kwargs: dict):
def __init__(self, min: float | None = None, max: float | None = None, **kwargs: dict):
super().__init__()
self.min_value = float(min) if min is not None else min
self.max_value = float(max) if max is not None else max
@@ -113,7 +113,7 @@ class MinMaxTransformer(NumberTransformer):
def inverse_transform(self, x: np.ndarray) -> np.ndarray:
if self.range is None:
raise ValueError("Numeric transformer needs to be instantiated with " "min and max values.")
raise ValueError("Numeric transformer needs to be instantiated with min and max values.")
return x * self.range + self.min_value
def transform_inference(self, x: torch.Tensor) -> torch.Tensor:
@@ -121,7 +121,7 @@ class MinMaxTransformer(NumberTransformer):
def inverse_transform_inference(self, x: torch.Tensor) -> torch.Tensor:
if self.range is None:
raise ValueError("Numeric transformer needs to be instantiated with " "min and max values.")
raise ValueError("Numeric transformer needs to be instantiated with min and max values.")
return x * self.range + self.min_value
@staticmethod
@@ -134,7 +134,7 @@ class MinMaxTransformer(NumberTransformer):
class InterQuartileTransformer(NumberTransformer):
def __init__(self, q1: float = None, q2: float = None, q3: float = None, **kwargs: dict):
def __init__(self, q1: float | None = None, q2: float | None = None, q3: float | None = None, **kwargs: dict):
super().__init__()
self.q1 = float(q1) if q1 is not None else q1
self.q2 = float(q2) if q2 is not None else q2
@@ -475,8 +475,7 @@ class NumberOutputFeature(NumberFeatureMixin, OutputFeature):
def create_predict_module(self) -> PredictModule:
if getattr(self, "clip", None) and not (isinstance(self.clip, (list, tuple)) and len(self.clip) == 2):
raise ValueError(
f"The clip parameter of {self.feature_name} is {self.clip}. "
f"It must be a list or a tuple of length 2."
f"The clip parameter of {self.feature_name} is {self.clip}. It must be a list or a tuple of length 2."
)
return _NumberPredict(getattr(self, "clip", None))
+5 -7
View File
@@ -387,7 +387,7 @@ class SequenceOutputFeature(SequenceFeatureMixin, OutputFeature):
if feature_metadata["str2idx"].keys() != feature_config.loss.class_weights.keys():
raise ValueError(
f"The class_weights keys ({feature_config.loss.class_weights.keys()}) are not compatible with "
f'the classes ({feature_metadata["str2idx"].keys()}) of feature {feature_config.column}. '
f"the classes ({feature_metadata['str2idx'].keys()}) of feature {feature_config.column}. "
"Check the metadata JSON file to see the classes "
"and consider there needs to be a weight "
"for the <UNK> class too."
@@ -415,12 +415,10 @@ class SequenceOutputFeature(SequenceFeatureMixin, OutputFeature):
curr_row_length = len(row)
if curr_row_length != first_row_length:
raise ValueError(
"The length of row {} of the class_similarities "
"of {} is {}, different from the length of "
"the first row {}. All rows must have "
"the same length.".format(
curr_row, feature_config.column, curr_row_length, first_row_length
)
f"The length of row {curr_row} of the class_similarities "
f"of {feature_config.column} is {curr_row_length}, different from the length of "
f"the first row {first_row_length}. All rows must have "
"the same length."
)
else:
curr_row += 1
+2 -2
View File
@@ -97,7 +97,7 @@ class _SetPostprocessing(torch.nn.Module):
def __init__(self, metadata: TrainingSetMetadataDict):
super().__init__()
self.idx2str = {i: v for i, v in enumerate(metadata["idx2str"])}
self.idx2str = dict(enumerate(metadata["idx2str"]))
self.predictions_key = PREDICTIONS
self.probabilities_key = PROBABILITIES
self.unk = UNKNOWN_SYMBOL
@@ -304,7 +304,7 @@ class SetOutputFeature(SetFeatureMixin, OutputFeature):
if feature_metadata["str2idx"].keys() != feature_config.loss.class_weights.keys():
raise ValueError(
f"The class_weights keys ({feature_config.loss.class_weights.keys()}) are not compatible with "
f'the classes ({feature_metadata["str2idx"].keys()}) of feature {feature_config.name}. '
f"the classes ({feature_metadata['str2idx'].keys()}) of feature {feature_config.name}. "
"Check the metadata JSON file to see the classes "
"and consider there needs to be a weight "
"for the <UNK> and <PAD> class too."
+2 -2
View File
@@ -380,7 +380,7 @@ class TextOutputFeature(TextFeatureMixin, SequenceOutputFeature):
if feature_metadata["str2idx"].keys() != feature_config.loss.class_weights.keys():
raise ValueError(
f"The class_weights keys ({feature_config.loss.class_weights.keys()}) are not compatible with "
f'the classes ({feature_metadata["str2idx"].keys()}) of feature {feature_config.column}. '
f"the classes ({feature_metadata['str2idx'].keys()}) of feature {feature_config.column}. "
"Check the metadata JSON file to see the classes "
"and consider there needs to be a weight "
"for the <UNK> class too."
@@ -402,7 +402,7 @@ class TextOutputFeature(TextFeatureMixin, SequenceOutputFeature):
raise ValueError(
"class_similarities_temperature > 0,"
"but no class similarities are provided "
"for feature {}".format(feature_config.column)
f"for feature {feature_config.column}"
)
@staticmethod
+1 -1
View File
@@ -328,7 +328,7 @@ class TimeseriesOutputFeature(TimeseriesFeatureMixin, OutputFeature):
return self.loss.to_dict()
def metric_kwargs(self):
return dict(num_outputs=self.output_shape[0])
return {"num_outputs": self.output_shape[0]}
def create_predict_module(self) -> PredictModule:
return _VectorPredict()
+3 -3
View File
@@ -132,8 +132,8 @@ class VectorFeatureMixin:
# expectations?
if vector_size != vector_size_param:
raise ValueError(
"The user provided value for vector size ({}) does not "
"match the value observed in the data: {}".format(preprocessing_parameters, vector_size)
f"The user provided value for vector size ({preprocessing_parameters}) does not "
f"match the value observed in the data: {vector_size}"
)
else:
logger.debug(f"Detected vector size: {vector_size}")
@@ -205,7 +205,7 @@ class VectorOutputFeature(VectorFeatureMixin, OutputFeature):
return self.decoder_obj(hidden)
def metric_kwargs(self):
return dict(num_outputs=self.output_shape[0])
return {"num_outputs": self.output_shape[0]}
def create_predict_module(self) -> PredictModule:
return _VectorPredict()
+2 -2
View File
@@ -21,7 +21,7 @@ def forecast_cli(
horizon: int = 1,
output_directory: str | None = None,
output_format: str = "parquet",
callbacks: list[Callback] = None,
callbacks: list[Callback] | None = None,
backend: Backend | str = None,
logging_level: int = logging.INFO,
**kwargs,
@@ -130,7 +130,7 @@ def cli(sys_argv):
parser.add_argument(
"-b",
"--backend",
help="specifies backend to use for parallel / distributed execution, " "defaults to local execution",
help="specifies backend to use for parallel / distributed execution, defaults to local execution",
choices=ALL_BACKENDS,
)
+40 -42
View File
@@ -70,9 +70,7 @@ def _patch_bohb_configspace_conversion():
def convert_search_space(spec):
resolved_vars, domain_vars, grid_vars = parse_spec_vars(spec)
if grid_vars:
raise ValueError(
"Grid search parameters cannot be automatically converted " "to a TuneBOHB search space."
)
raise ValueError("Grid search parameters cannot be automatically converted to a TuneBOHB search space.")
spec = flatten_dict(spec, prevent_delimiter=True)
resolved_vars, domain_vars, grid_vars = parse_spec_vars(spec)
@@ -127,7 +125,7 @@ def _patch_bohb_configspace_conversion():
raise ValueError(
"TuneBOHB does not support parameters of type "
"`{}` with samplers of type `{}`".format(type(domain).__name__, type(domain.sampler).__name__)
f"`{type(domain).__name__}` with samplers of type `{type(domain.sampler).__name__}`"
)
cs = ConfigSpace.ConfigurationSpace()
@@ -228,10 +226,10 @@ class RayTuneExecutor:
goal: str,
split: str,
search_alg: dict | None = None,
cpu_resources_per_trial: int = None,
gpu_resources_per_trial: int = None,
kubernetes_namespace: str = None,
time_budget_s: int | float | datetime.timedelta = None,
cpu_resources_per_trial: int | None = None,
gpu_resources_per_trial: int | None = None,
kubernetes_namespace: str | None = None,
time_budget_s: int | float | datetime.timedelta | None = None,
max_concurrent_trials: int | None = None,
num_samples: int = 1,
scheduler: dict | None = None,
@@ -348,7 +346,7 @@ class RayTuneExecutor:
logger.info("Returning metric score from training (validation) statistics")
return self.get_metric_score_from_train_stats(train_stats, VALIDATION)
elif self._has_metric(train_stats, TRAINING):
logger.info("Returning metric score from training split statistics, " "as no validation was given")
logger.info("Returning metric score from training split statistics, as no validation was given")
return self.get_metric_score_from_train_stats(train_stats, TRAINING)
else:
raise RuntimeError("Unable to obtain metric score from missing training (validation) statistics")
@@ -485,7 +483,7 @@ class RayTuneExecutor:
checkpoint = None
except Exception:
logger.warning(
f"Cannot get best model path for {trial_path} due to exception below:" f"\n{traceback.format_exc()}"
f"Cannot get best model path for {trial_path} due to exception below:\n{traceback.format_exc()}"
)
yield None
return
@@ -587,7 +585,7 @@ class RayTuneExecutor:
modified_config = merge_with_defaults(modified_config)
hyperopt_dict["config"] = modified_config
hyperopt_dict["experiment_name "] = f'{hyperopt_dict["experiment_name"]}_{trial_id}'
hyperopt_dict["experiment_name "] = f"{hyperopt_dict['experiment_name']}_{trial_id}"
hyperopt_dict["output_directory"] = str(trial_dir)
tune_executor = self
@@ -829,35 +827,35 @@ class RayTuneExecutor:
# Enforce fractional GPU utilization
gpu_memory_limit = self.gpu_resources_per_trial
hyperopt_dict = dict(
config=config,
dataset=dataset,
training_set=training_set,
validation_set=validation_set,
test_set=test_set,
training_set_metadata=training_set_metadata,
data_format=data_format,
experiment_name=experiment_name,
model_name=model_name,
eval_split=self.split,
skip_save_training_description=skip_save_training_description,
skip_save_training_statistics=skip_save_training_statistics,
skip_save_model=skip_save_model,
skip_save_progress=skip_save_progress,
skip_save_log=skip_save_log,
skip_save_processed_input=skip_save_processed_input,
skip_save_unprocessed_output=skip_save_unprocessed_output,
skip_save_predictions=skip_save_predictions,
skip_save_eval_stats=skip_save_eval_stats,
output_directory=output_directory,
gpus=gpus,
gpu_memory_limit=gpu_memory_limit,
allow_parallel_threads=allow_parallel_threads,
callbacks=callbacks,
backend=backend,
random_seed=random_seed,
debug=debug,
)
hyperopt_dict = {
"config": config,
"dataset": dataset,
"training_set": training_set,
"validation_set": validation_set,
"test_set": test_set,
"training_set_metadata": training_set_metadata,
"data_format": data_format,
"experiment_name": experiment_name,
"model_name": model_name,
"eval_split": self.split,
"skip_save_training_description": skip_save_training_description,
"skip_save_training_statistics": skip_save_training_statistics,
"skip_save_model": skip_save_model,
"skip_save_progress": skip_save_progress,
"skip_save_log": skip_save_log,
"skip_save_processed_input": skip_save_processed_input,
"skip_save_unprocessed_output": skip_save_unprocessed_output,
"skip_save_predictions": skip_save_predictions,
"skip_save_eval_stats": skip_save_eval_stats,
"output_directory": output_directory,
"gpus": gpus,
"gpu_memory_limit": gpu_memory_limit,
"allow_parallel_threads": allow_parallel_threads,
"callbacks": callbacks,
"backend": backend,
"random_seed": random_seed,
"debug": debug,
}
mode = "min" if self.goal != MAXIMIZE else "max"
metric = "metric_score"
@@ -865,7 +863,7 @@ class RayTuneExecutor:
self.search_algorithm.check_for_random_seed(random_seed)
if self.search_algorithm.search_alg_dict is not None:
if TYPE not in self.search_algorithm.search_alg_dict:
candiate_search_algs = [search_alg for search_alg in SEARCH_ALG_IMPORT.keys()]
candiate_search_algs = list(SEARCH_ALG_IMPORT.keys())
logger.warning(
"WARNING: search_alg type parameter missing, using 'variant_generator' as default. "
f"These are possible values for the type parameter: {candiate_search_algs}."
@@ -1122,7 +1120,7 @@ def set_values(params: dict[str, Any], model_dict: dict[str, Any]):
if isinstance(value, dict):
for sub_key, sub_value in value.items():
if key not in model_dict:
model_dict[key] = dict()
model_dict[key] = {}
model_dict[key][sub_key] = sub_value
else:
model_dict[key] = value
+14 -14
View File
@@ -65,8 +65,8 @@ def hyperopt(
training_set: str | dict | pd.DataFrame = None,
validation_set: str | dict | pd.DataFrame = None,
test_set: str | dict | pd.DataFrame = None,
training_set_metadata: str | dict = None,
data_format: str = None,
training_set_metadata: str | dict | None = None,
data_format: str | None = None,
experiment_name: str = "hyperopt",
model_name: str = "run",
resume: bool | None = None,
@@ -81,11 +81,11 @@ def hyperopt(
skip_save_eval_stats: bool = False,
skip_save_hyperopt_statistics: bool = False,
output_directory: str = "results",
gpus: str | int | list[int] = None,
gpus: str | int | list[int] | None = None,
gpu_memory_limit: float | None = None,
allow_parallel_threads: bool = True,
callbacks: list[Callback] = None,
tune_callbacks: list[TuneCallback] = None,
callbacks: list[Callback] | None = None,
tune_callbacks: list[TuneCallback] | None = None,
backend: Backend | str = None,
random_seed: int = default_random_seed,
hyperopt_log_verbosity: int = 3,
@@ -256,30 +256,30 @@ def hyperopt(
if split == TRAINING:
if training_set is None and not splitter.has_split(0):
raise ValueError(
'The data for the specified split for hyperopt "{}" '
f'The data for the specified split for hyperopt "{split}" '
"was not provided, "
"or the split amount specified in the preprocessing section "
"of the config is not greater than 0".format(split)
"of the config is not greater than 0"
)
elif split == VALIDATION:
if validation_set is None and not splitter.has_split(1):
raise ValueError(
'The data for the specified split for hyperopt "{}" '
f'The data for the specified split for hyperopt "{split}" '
"was not provided, "
"or the split amount specified in the preprocessing section "
"of the config is not greater than 0".format(split)
"of the config is not greater than 0"
)
elif split == TEST:
if test_set is None and not splitter.has_split(2):
raise ValueError(
'The data for the specified split for hyperopt "{}" '
f'The data for the specified split for hyperopt "{split}" '
"was not provided, "
"or the split amount specified in the preprocessing section "
"of the config is not greater than 0".format(split)
"of the config is not greater than 0"
)
else:
raise ValueError(
'unrecognized hyperopt split "{}". ' "Please provide one of: {}".format(split, {TRAINING, VALIDATION, TEST})
f'unrecognized hyperopt split "{split}". Please provide one of: { ({TRAINING, VALIDATION, TEST}) }'
)
if output_feature == COMBINED:
if metric != LOSS:
@@ -288,9 +288,9 @@ def hyperopt(
output_feature_names = {of[NAME] for of in full_config[OUTPUT_FEATURES]}
if output_feature not in output_feature_names:
raise ValueError(
'The output feature specified for hyperopt "{}" '
f'The output feature specified for hyperopt "{output_feature}" '
"cannot be found in the config. "
'Available ones are: {} and "combined"'.format(output_feature, output_feature_names)
f'Available ones are: {output_feature_names} and "combined"'
)
hyperopt_executor = get_build_hyperopt_executor(executor[TYPE])(
+1 -1
View File
@@ -81,7 +81,7 @@ def load_json_values(d):
def should_tune_preprocessing(config):
parameters = config[HYPEROPT][PARAMETERS]
for param_name in parameters.keys():
for param_name in parameters:
if f"{PREPROCESSING}." in param_name:
return True
return False
+10 -10
View File
@@ -31,12 +31,12 @@ logger = logging.getLogger(__name__)
def hyperopt_cli(
config: str | dict,
dataset: str = None,
training_set: str = None,
validation_set: str = None,
test_set: str = None,
training_set_metadata: str = None,
data_format: str = None,
dataset: str | None = None,
training_set: str | None = None,
validation_set: str | None = None,
test_set: str | None = None,
training_set_metadata: str | None = None,
data_format: str | None = None,
experiment_name: str = "experiment",
model_name: str = "run",
# model_load_path=None,
@@ -52,10 +52,10 @@ def hyperopt_cli(
skip_save_eval_stats: bool = False,
skip_save_hyperopt_statistics: bool = False,
output_directory: str = "results",
gpus: str | int | list[int] = None,
gpus: str | int | list[int] | None = None,
gpu_memory_limit: float | None = None,
allow_parallel_threads: bool = True,
callbacks: list[Callback] = None,
callbacks: list[Callback] | None = None,
backend: Backend | str = None,
random_seed: int = default_random_seed,
hyperopt_log_verbosity: int = 3,
@@ -254,7 +254,7 @@ def cli(sys_argv):
"feather",
"fwf",
"hdf5",
"html" "tables",
"htmltables",
"json",
"jsonl",
"parquet",
@@ -381,7 +381,7 @@ def cli(sys_argv):
parser.add_argument(
"-b",
"--backend",
help="specifies backend to use for parallel / distributed execution, " "defaults to local execution",
help="specifies backend to use for parallel / distributed execution, defaults to local execution",
choices=ALL_BACKENDS,
)
parser.add_argument(
+3 -3
View File
@@ -38,7 +38,7 @@ class BaseModel(LudwigModule, metaclass=ABCMeta):
def type() -> str:
"""Returns the model type."""
def __init__(self, random_seed: int = None):
def __init__(self, random_seed: int | None = None):
self._random_seed = random_seed
# TODO: with change to misc_utils.set_random_seed() this may be redundant
@@ -108,7 +108,7 @@ class BaseModel(LudwigModule, metaclass=ABCMeta):
for output_feature_def in output_features_def:
# TODO(Justin): Check that the semantics of input_size align with what the combiner's output shape returns
# for seq2seq.
setattr(getattr(output_feature_configs, output_feature_def[NAME]), "input_size", combiner.output_shape[-1])
getattr(output_feature_configs, output_feature_def[NAME]).input_size = combiner.output_shape[-1]
output_features[output_feature_def[NAME]] = cls.build_single_output(
getattr(output_feature_configs, output_feature_def[NAME]), output_features
)
@@ -301,7 +301,7 @@ class BaseModel(LudwigModule, metaclass=ABCMeta):
weight_names = {name for name, _ in self.named_parameters()}
for name in tensor_names:
if name not in weight_names:
raise ValueError(f'Requested tensor name filter "{name}" not present in the model graph') # noqa: E713
raise ValueError(f'Requested tensor name filter "{name}" not present in the model graph')
# Apply filter.
tensor_set = set(tensor_names)
+1 -1
View File
@@ -191,7 +191,7 @@ class ECD(BaseModel):
return decoder_outputs
def unskip(self):
for k in self.input_features.keys():
for k in self.input_features:
self.input_features.set(k, self.input_features.get(k).unskip())
def save(self, save_path):
+14 -14
View File
@@ -154,7 +154,7 @@ class LLM(BaseModel):
self.generation.pad_token_id = self.tokenizer.pad_token_id
self.max_new_tokens = self.generation.max_new_tokens
# max input length value copied from FastChat
# https://github.com/lm-sys/FastChat/blob/0e958b852a14f4bef5f0e9d7a5e7373477329cf2/fastchat/serve/inference.py#L183 # noqa E501
# https://github.com/lm-sys/FastChat/blob/0e958b852a14f4bef5f0e9d7a5e7373477329cf2/fastchat/serve/inference.py#L183
self.max_input_length = self.context_len - self.max_new_tokens - 8
@property
@@ -394,7 +394,7 @@ class LLM(BaseModel):
f"Input length {input_ids_sample_no_padding.shape[1]} is "
f"greater than max input length {self.max_input_length}. Truncating."
)
input_ids_sample_no_padding = input_ids_sample_no_padding[:, -self.max_input_length :] # noqa E203
input_ids_sample_no_padding = input_ids_sample_no_padding[:, -self.max_input_length :]
input_lengths.append(input_ids_sample_no_padding.shape[1])
@@ -402,13 +402,13 @@ class LLM(BaseModel):
model_device = next(self.model.parameters()).device
input_ids_sample_no_padding = input_ids_sample_no_padding.to(model_device)
generate_kwargs = dict(
input_ids=input_ids_sample_no_padding,
attention_mask=mask,
generation_config=self.generation,
return_dict_in_generate=True,
output_scores=True,
)
generate_kwargs = {
"input_ids": input_ids_sample_no_padding,
"attention_mask": mask,
"generation_config": self.generation,
"return_dict_in_generate": True,
"output_scores": True,
}
if logits_processor is not None:
generate_kwargs["logits_processor"] = logits_processor
@@ -727,7 +727,7 @@ class LLM(BaseModel):
# Override properties of the model to indicate that it is no longer quantized.
# This is also necessary to ensure that the model can be saved, otherwise it will raise an error like
# "You are calling `save_pretrained` on a 4-bit converted model. This is currently not supported"
# See: https://github.com/huggingface/transformers/blob/0ad4e7e6dad670a7151aaceb1af3c272a3bf73a8/src/transformers/modeling_utils.py#L2054 # noqa
# See: https://github.com/huggingface/transformers/blob/0ad4e7e6dad670a7151aaceb1af3c272a3bf73a8/src/transformers/modeling_utils.py#L2054
self.model.is_loaded_in_4bit = False
self.model.is_loaded_in_8bit = False
@@ -750,7 +750,7 @@ class LLM(BaseModel):
# Check if the saved weights are merged (no adapter_config.json) or adapter-only
adapter_config_path = os.path.join(weights_save_path, "adapter_config.json")
if os.path.exists(adapter_config_path):
from peft import PeftModel # noqa
from peft import PeftModel
if isinstance(self.model, PeftModel):
# Unwrap and reload PeftModel
@@ -780,7 +780,7 @@ class LLM(BaseModel):
other named adapter it is loaded the same way, no special case needed. Finally we activate whichever adapter
the config declares via `set_adapter(active)`.
"""
from peft import PeftModel # noqa
from peft import PeftModel
adapters_cfg = self.config_obj.adapters
names = list(adapters_cfg.adapters.keys())
@@ -867,8 +867,8 @@ class LLM(BaseModel):
# tensors. Padding left with -100 to match the length of the target tensor masks the input ids during
# softmax cross entropy loss computation. This ensures that the loss is computed only for the target
# token IDs. Examples:
# BERTLMHead: https://github.com/huggingface/transformers/blob/v4.29.1/src/transformers/models/bert/modeling_bert.py#L1216-L1219 # noqa
# GPTNeoForCausalLM: https://github.com/huggingface/transformers/blob/v4.29.1/src/transformers/models/gpt_neo/modeling_gpt_neo.py#L736 # noqa
# BERTLMHead: https://github.com/huggingface/transformers/blob/v4.29.1/src/transformers/models/bert/modeling_bert.py#L1216-L1219
# GPTNeoForCausalLM: https://github.com/huggingface/transformers/blob/v4.29.1/src/transformers/models/gpt_neo/modeling_gpt_neo.py#L736
_targets = pad_target_tensor_for_fine_tuning(targets, predictions, self.model_inputs, of_name)
return _targets
+1 -1
View File
@@ -121,7 +121,7 @@ class Predictor(BasePredictor):
self.batch_predict = self._distributed.return_first(self.batch_predict)
self.batch_evaluation = self._distributed.return_first(self.batch_evaluation)
def batch_predict(self, dataset: Dataset, dataset_name: str = None, collect_logits: bool = False):
def batch_predict(self, dataset: Dataset, dataset_name: str | None = None, collect_logits: bool = False):
self.dist_model = self._distributed.to_device(self.dist_model)
prev_model_training_mode = self.dist_model.training # store previous model training mode
self.dist_model.eval() # set model to eval mode

Some files were not shown because too many files have changed in this diff Show More