diff --git a/agent_config.yaml b/agent_config.yaml index 7d65a84c..b972779e 100644 --- a/agent_config.yaml +++ b/agent_config.yaml @@ -23,5 +23,6 @@ agent: # openrouter_api_key: # Open router API key (can also be set as env variable OPENROUTER_API_KEY) # openrouter_base_url: https://openrouter.ai/api/v1 # Open router base URL (can also be set as env variable OPENROUTER_BASE_URL) # openrouter_request_timeout: 15.0 # Timeout for OpenRouter API requests in seconds (can also be set as env variable OPENROUTER_REQUEST_TIMEOUT) + # openrouter_max_tokens: 2048 # Max completion length. OpenRouter reserves max_tokens*price against the key budget BEFORE generating, so an uncapped value can 402 ("more credits, or fewer max_tokens") on a credit/weekly-limited key. Raise only if responses get truncated (env: OPENROUTER_MAX_TOKENS) # openrouter_provider_sort: throughput # Bias OpenRouter's upstream routing to avoid slow providers: 'throughput' | 'latency' | 'price'. Empty string = let OpenRouter choose (env: OPENROUTER_PROVIDER_SORT) # openrouter_structured_output: false # Ask the model for a schema-validated plan directly (skips free-form JSON + regex repair). More reliable, but only works on models whose OpenRouter route supports structured/JSON-schema output (e.g. Gemini, GPT-4o). env: OPENROUTER_STRUCTURED_OUTPUT=1 diff --git a/docs/examples/pytorch/generation.rst b/docs/examples/pytorch/generation.rst index 58923714..ebdf3d68 100644 --- a/docs/examples/pytorch/generation.rst +++ b/docs/examples/pytorch/generation.rst @@ -101,12 +101,3 @@ filter by pair distance to find the hardest negatives. weightslab ui launch # 1. deploy the studio weightslab start example --gen # 2. start the generation / anomaly demo - - -.. raw:: html - -
- - Open In Colab - -
diff --git a/docs/usage/parameters.rst b/docs/usage/parameters.rst index c23ae9c9..1f5aca41 100644 --- a/docs/usage/parameters.rst +++ b/docs/usage/parameters.rst @@ -511,6 +511,16 @@ LLM / agent integration (optional) * - ``OPENROUTER_REQUEST_TIMEOUT`` - *(unset)* - Per-request timeout in seconds for OpenRouter calls. + * - ``OPENROUTER_MAX_TOKENS`` + - ``2048`` + - Maximum completion length requested from OpenRouter. OpenRouter + pre-authorizes ``max_tokens × completion_price`` against the key's + remaining budget *before* generating, so leaving this uncapped makes + the model request its full output window and can fail with a ``402`` + ("requires more credits, or fewer max_tokens") on a credit- or + weekly-limited key — even though the model is otherwise usable. The + default is ample for intent planning; raise it only if you see + truncated responses. Telemetry ~~~~~~~~~~ diff --git a/pyproject.toml b/pyproject.toml index 42cc7b6c..c346ae07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,11 @@ dependencies = [ # versions pip resolves. Range left open (<3) so Colab's numpy 2.x installs # without a downgrade. Verified: import + torch/pandas interop + gRPC on np 2.4. "numpy>=1.24,<3", - "pandas>=2.2.3,<3", + # Floor at 2.2.2 (not 2.2.3) so Colab's pinned pandas==2.2.2 is accepted + # as-is: google-colab 1.0.0 requires pandas==2.2.2, and a >=2.2.3 floor forced + # an upgrade to 2.3.x that breaks that pin. Clean installs still pick the + # newest numpy-2-safe pandas (2.3.x). + "pandas>=2.2.2,<3", "duckdb>=1.1,<2", # signal/sample/instance history store "PyYAML>=6.0.3,<7", "dill>=0.3.8,<0.5", @@ -53,10 +57,14 @@ dependencies = [ # Serving / schema "grpcio>=1.80,<2", - # protobuf <8 so Colab's protobuf 7.x is accepted (its <7 cap forced the - # downgrade cascade). Verified: all 70 generated gRPC messages serialize/parse - # and the descriptor pool builds under protobuf 7.35. - "protobuf>=4.25,<8", + # Floor tracks the checked-in gencode: the *_pb2.py are regenerated with + # grpcio-tools ~=1.68 (protoc gencode 5.28.1), so any runtime >=5.28.1 satisfies + # protobuf's runtime>=gencode rule — including Colab's stock protobuf 5.29.6, so + # NO upgrade is forced on Colab. IMPORTANT: regenerate the stubs with + # grpcio-tools ~=1.68 to keep the gencode Colab-compatible; a newer grpcio-tools + # bumps the gencode and reintroduces the VersionError on Colab. Upper bound <8 + # keeps modern runtimes (7.x) working. + "protobuf>=5.28.1,<8", "pydantic>=2.7,<3", # Imaging diff --git a/tests/backend/test_data_loader_interface.py b/tests/backend/test_data_loader_interface.py index 4da94914..000e45c9 100644 --- a/tests/backend/test_data_loader_interface.py +++ b/tests/backend/test_data_loader_interface.py @@ -353,6 +353,35 @@ def test_multiple_sequential_epochs_with_auto_reset(self): f"Each epoch should yield the same batch count, got {epoch_counts}", ) + def test_reset_is_callable_through_ledger_proxy(self): + """`reset()` must exist and be callable via the ledger Proxy handle. + + Ultralytics' trainer calls ``train_loader.reset()`` after closing mosaic + augmentation. The loader handed to it is a ledger Proxy wrapping this + interface, so a real ``reset()`` method (mapped to ``_reset_iterator``) + must be reachable through the proxy — otherwise the call resolves to a + non-callable None. Regression for the Ultralytics YOLO notebook crash + ``TypeError: 'NoneType' object is not callable``. + """ + DataLoaderInterface( + self.train_ds, batch_size=self.batch_size, + loader_name="reset_proxy_loader", register=True, compute_hash=True, + ) + loader = ledgers.get_dataloader("reset_proxy_loader") + + self.assertTrue(hasattr(loader, "reset")) + self.assertTrue(callable(loader.reset)) + + # A full epoch before reset. + batches_before = sum(1 for _ in loader) + self.assertGreater(batches_before, 0) + + # reset() must not raise, and the loader stays fully usable afterwards + # (a fresh full epoch with the same batch count can be iterated). + loader.reset() + batches_after = sum(1 for _ in loader) + self.assertEqual(batches_after, batches_before) + class TestDataLoaderReproducibility(unittest.TestCase): """Test RNG and iteration state reproducibility for dataloaders.""" diff --git a/tests/backend/test_ledgers.py b/tests/backend/test_ledgers.py index 8db41501..513b1caf 100644 --- a/tests/backend/test_ledgers.py +++ b/tests/backend/test_ledgers.py @@ -672,6 +672,49 @@ def __init__(self): delattr(proxy, "flag") self.assertFalse(hasattr(wrapped, "flag")) + def test_proxy_missing_attr_raises_attributeerror(self): + """Missing attributes on the wrapped object must raise AttributeError. + + Regression: __getattr__ used to `return None`, which made + `hasattr(proxy, missing)` report True and silently resolved + `proxy.missing` to a non-callable None. That broke capability probes + such as Ultralytics' `if not hasattr(loader, "reset")` shim, leaving + `loader.reset` as None -> `'NoneType' object is not callable`. + """ + proxy = Proxy(Dummy("x")) + + # Existing attribute still forwards. + self.assertEqual(proxy.name, "x") + + # Missing attribute raises (does not return None). + with self.assertRaises(AttributeError): + _ = proxy.definitely_not_here + + # hasattr therefore reports False, and getattr honors the default. + self.assertFalse(hasattr(proxy, "definitely_not_here")) + self.assertIsNone(getattr(proxy, "definitely_not_here", None)) + self.assertEqual(getattr(proxy, "definitely_not_here", 42), 42) + + def test_proxy_forwards_callable_capability(self): + """A method present on the wrapped object is discoverable and callable. + + Mirrors how a ledger Proxy wraps a DataLoaderInterface whose real + `reset()` must be reachable through the proxy. + """ + class _HasReset: + def __init__(self): + self.calls = 0 + + def reset(self): + self.calls += 1 + + wrapped = _HasReset() + proxy = Proxy(wrapped) + + self.assertTrue(hasattr(proxy, "reset")) + self.assertTrue(callable(proxy.reset)) + proxy.reset() + self.assertEqual(wrapped.calls, 1) if __name__ == "__main__": diff --git a/tests/backend/test_write_dataframe.py b/tests/backend/test_write_dataframe.py index 163eec03..5582623c 100644 --- a/tests/backend/test_write_dataframe.py +++ b/tests/backend/test_write_dataframe.py @@ -48,6 +48,19 @@ def _call(path, manager, **kwargs): return write_dataframe(path, **kwargs) +def _records(path): + """Read a JSON export back into a list of row records. + + write_dataframe defaults to ``orient="columns"`` (``{column: {row: value}}``) + — compact (~6x smaller than records for wide sparse tables) and round-trips + with ``pd.read_json``'s default orient. These tests assert row-level + behavior, so normalize the columnar layout back to records here. Empty + exports (``{}`` / ``{"index": {}}``) collapse to ``[]``. + """ + raw = json.loads(open(path, encoding="utf-8").read()) + return pd.DataFrame(raw).to_dict(orient="records") + + # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @@ -89,20 +102,22 @@ def test_flush_failure_does_not_abort(self, tmp_json): # --------------------------------------------------------------------------- class TestWriteDataframeJsonStructure: - def test_json_is_list_of_records(self, mgr, tmp_json): + def test_json_is_columnar_dict(self, mgr, tmp_json): _call(tmp_json, mgr) - data = json.loads(open(tmp_json).read()) - assert isinstance(data, list) + raw = json.loads(open(tmp_json).read()) + # Default orient="columns": {column: {row_index: value}} + assert isinstance(raw, dict) + assert isinstance(raw["sample_id"], dict) def test_json_includes_index_as_columns(self, mgr, tmp_json): _call(tmp_json, mgr) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert "sample_id" in data[0] assert "annotation_id" in data[0] def test_json_all_rows_present_by_default(self, mgr, tmp_json): _call(tmp_json, mgr) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert len(data) == 4 def test_returns_written_path(self, mgr, tmp_json): @@ -207,30 +222,30 @@ def test_returns_path_when_no_manager(self, tmp_json): class TestWriteDataframeColumnFilters: def test_columns_all_keeps_everything(self, mgr, tmp_json): _call(tmp_json, mgr, columns="all") - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert "signals_loss" in data[0] or any("signals_loss" in r for r in data) def test_columns_tags_only(self, mgr, tmp_json): _call(tmp_json, mgr, columns="tags") - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) non_index = [k for k in data[0] if k not in ("sample_id", "annotation_id")] assert all(k.startswith("tag:") or k.startswith("TAG:") for k in non_index) def test_columns_signals_only(self, mgr, tmp_json): _call(tmp_json, mgr, columns="signals") - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) non_index = [k for k in data[0] if k not in ("sample_id", "annotation_id")] assert all(str(k).lower().startswith("signals") for k in non_index) def test_columns_discarded_only(self, mgr, tmp_json): _call(tmp_json, mgr, columns="discarded") - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) non_index = [k for k in data[0] if k not in ("sample_id", "annotation_id")] assert non_index == ["discarded"] def test_columns_list_of_groups(self, mgr, tmp_json): _call(tmp_json, mgr, columns=["tags", "discarded"]) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) non_index = set(k for r in data for k in r if k not in ("sample_id", "annotation_id")) assert "discarded" in non_index assert any(k.startswith("tag:") for k in non_index) @@ -238,20 +253,20 @@ def test_columns_list_of_groups(self, mgr, tmp_json): def test_columns_exact_name(self, mgr, tmp_json): _call(tmp_json, mgr, columns=["signals_loss"]) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) non_index = [k for k in data[0] if k not in ("sample_id", "annotation_id")] assert non_index == ["signals_loss"] def test_columns_nonexistent_name_yields_empty_cols(self, mgr, tmp_json): _call(tmp_json, mgr, columns=["nonexistent_col"]) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) # Index columns always present; no extra columns for row in data: assert set(row.keys()) <= {"sample_id", "annotation_id"} def test_columns_none_keeps_all(self, mgr, tmp_json): _call(tmp_json, mgr, columns=None) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert "signals_loss" in data[0] or any("signals_loss" in r for r in data) @@ -262,38 +277,38 @@ def test_columns_none_keeps_all(self, mgr, tmp_json): class TestWriteDataframeIndexFilters: def test_sample_id_single(self, mgr, tmp_json): _call(tmp_json, mgr, sample_id="s1") - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert all(r["sample_id"] == "s1" for r in data) assert len(data) == 2 # s1 has annotation_ids 0 and 1 def test_sample_id_list(self, mgr, tmp_json): _call(tmp_json, mgr, sample_id=["s1", "s2"]) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) sids = {r["sample_id"] for r in data} assert sids == {"s1", "s2"} assert len(data) == 4 def test_instance_id_zero_keeps_sample_rows(self, mgr, tmp_json): _call(tmp_json, mgr, instance_id=0) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert all(r["annotation_id"] == 0 for r in data) assert len(data) == 2 # s1 and s2 both have annotation_id=0 def test_instance_id_list(self, mgr, tmp_json): _call(tmp_json, mgr, instance_id=[1, 2]) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert all(r["annotation_id"] in (1, 2) for r in data) def test_sample_and_instance_combined(self, mgr, tmp_json): _call(tmp_json, mgr, sample_id="s1", instance_id=1) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert len(data) == 1 assert data[0]["sample_id"] == "s1" assert data[0]["annotation_id"] == 1 def test_empty_result_when_no_match(self, mgr, tmp_json): _call(tmp_json, mgr, sample_id="nonexistent") - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert data == [] @@ -305,7 +320,7 @@ class TestWriteDataframeEmpty: def test_empty_df_writes_empty_json(self, tmp_json): mgr = _make_manager(df=pd.DataFrame()) _call(tmp_json, mgr) - data = json.loads(open(tmp_json).read()) + data = _records(tmp_json) assert data == [] def test_empty_df_writes_empty_csv(self, tmp_csv): diff --git a/tests/gRPC/test_grpc_user_actions.py b/tests/gRPC/test_grpc_user_actions.py index 1b71da74..530d8e7d 100644 --- a/tests/gRPC/test_grpc_user_actions.py +++ b/tests/gRPC/test_grpc_user_actions.py @@ -61,7 +61,7 @@ def get_combined_df(self): def get_df_view(self): return self.df.reset_index().copy()\ - def get_collapse_annotations_to_samples_df(self): + def get_collapse_annotations_to_samples_df(self, df=None): # This fixture is already one row per sample (no instance rows), so the # per-sample collapsed view is just the frame with its index restored to # (origin, sample_id) columns — matching what the real manager returns. diff --git a/weightslab/backend/dataloader_interface.py b/weightslab/backend/dataloader_interface.py index d9b16e94..9f76f660 100644 --- a/weightslab/backend/dataloader_interface.py +++ b/weightslab/backend/dataloader_interface.py @@ -1045,6 +1045,26 @@ def reset_iterator(self) -> None: """ self._reset_iterator() + def reset(self) -> None: + """Recreate the internal iterator (Ultralytics / ``InfiniteDataLoader``-compatible). + + Some training frameworks call ``loader.reset()`` to force a fresh + iterator after mutating the dataset mid-run — e.g. Ultralytics closes + mosaic augmentation near the end of training and then calls + ``train_loader.reset()`` so the change takes effect on the next epoch. + We map it to ``_reset_iterator()``. + + Exposing this as a real method (rather than relying on a caller-side + shim like ``if not hasattr(loader, "reset"): loader.reset = ...``) is + important: this loader is handed to callers wrapped in a ledger + ``Proxy`` whose ``__getattr__`` returns ``None`` for genuinely-missing + attributes instead of raising ``AttributeError``. That makes + ``hasattr(proxy, "reset")`` return True and silently resolves + ``proxy.reset`` to ``None``, so any such shim is skipped and + ``reset()`` blows up with ``'NoneType' object is not callable``. + """ + self._reset_iterator() + # ------------------------------------------------------------------------- # Iteration state capture/restore for deterministic resume # ------------------------------------------------------------------------- diff --git a/weightslab/backend/ledgers.py b/weightslab/backend/ledgers.py index 5d01cea7..df75f90d 100644 --- a/weightslab/backend/ledgers.py +++ b/weightslab/backend/ledgers.py @@ -500,7 +500,16 @@ def __getattr__(self, item): except (KeyError, TypeError): pass - return None + # Attribute genuinely absent on the wrapped object. Raise AttributeError + # (as the class docstring promises) rather than returning None: returning + # None makes `hasattr(proxy, x)` report True for missing attributes, which + # breaks capability probes (e.g. `if not hasattr(loader, "reset")`) and + # silently resolves `proxy.missing` to a non-callable None. Callers that + # want a soft default should use `getattr(proxy, x, default)`, which now + # works correctly. + raise AttributeError( + f"'{type(obj).__name__}' object has no attribute {item!r}" + ) def __delattr__(self, name: str) -> None: """Delete an attribute from the proxy or wrapped object safely. diff --git a/weightslab/components/global_monitoring.py b/weightslab/components/global_monitoring.py index 4dcfa184..a9d9d124 100644 --- a/weightslab/components/global_monitoring.py +++ b/weightslab/components/global_monitoring.py @@ -1,12 +1,13 @@ import os -from typing import Any, Optional -from enum import Enum import contextvars - -from threading import Event import threading import time import logging +import traceback + +from typing import Any, Optional +from enum import Enum +from threading import Event from weightslab.backend.ledgers import get_hyperparams, set_hyperparam, resolve_hp_name, get_checkpoint_manager, get_model from weightslab.components.tracking import TrackingMode @@ -276,7 +277,7 @@ def __enter__(self, f: bool = False): self.model.set_tracking_mode(TrackingMode.EVAL) self.model.eval() - def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any, f: bool = False) -> bool: + def __exit__(self, exc_type: Any, exc_value: Any, traceback_: Any, f: bool = False) -> bool: """ Executed upon exiting the 'with' block (after user code runs). Reverts the model state. @@ -296,11 +297,11 @@ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any, f: bool = Fals if exc_type is RuntimeError: logger.debug(f"Suppressing exception: {exc_value} in GuardContext.__exit__:") - traceback.print_exc() if os.getenv("WL_DEBUG", "0") == "1" else None - self.architecture_guard.__exit__(exc_type, exc_value, traceback) + traceback.print_exc() if os.getenv("WEIGHTSLAB_LOG_LEVEL", "0") == "1" else None + self.architecture_guard.__exit__(exc_type, exc_value, traceback_) return True # suppress the exception - self.architecture_guard.__exit__(exc_type, exc_value, traceback) + self.architecture_guard.__exit__(exc_type, exc_value, traceback_) return False diff --git a/weightslab/data/dataframe_manager.py b/weightslab/data/dataframe_manager.py index 0fef7c37..64b92f9c 100644 --- a/weightslab/data/dataframe_manager.py +++ b/weightslab/data/dataframe_manager.py @@ -2151,7 +2151,7 @@ def get_combined_df( return df - def get_collapse_annotations_to_samples_df(self) -> pd.DataFrame: + def get_collapse_annotations_to_samples_df(self, df: pd.DataFrame | None = None) -> pd.DataFrame: """Collapse a (sample_id, annotation_id) multi-index df to one row per sample. The shared dataframe manager now expands every sample into one row per @@ -2182,8 +2182,16 @@ def get_collapse_annotations_to_samples_df(self) -> pd.DataFrame: Returns a single-level ``sample_id``-indexed dataframe (origin stays a column). Dataframes that are not annotation-expanded are returned as-is. + + Args: + df: An already-materialized combined frame (as returned by + :meth:`get_combined_df`) to collapse. When ``None`` (default) a + fresh combined frame is pulled here. Callers that just pulled the + combined frame should pass it in to avoid a second full copy + + buffer merge + proxy conversion over the whole dataset. """ - df = self.get_combined_df() + if df is None: + df = self.get_combined_df() SID = SampleStatsEx.SAMPLE_ID.value ANNOT = SampleStatsEx.INSTANCE_ID.value diff --git a/weightslab/examples/Notebooks/Colab/ws-colab-quickstart.ipynb b/weightslab/examples/Notebooks/Colab/ws-colab-quickstart.ipynb new file mode 100644 index 00000000..a2e225b1 --- /dev/null +++ b/weightslab/examples/Notebooks/Colab/ws-colab-quickstart.ipynb @@ -0,0 +1,77 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Welcome to the WeightsLab Colab quickstart — a blank starter for streaming your own training run into Weights Studio.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# WeightsLab · Colab quickstart\n", + "\n", + "> **This notebook is read-only.** Run it as-is to try it, or use **File ▸ Save a copy in Drive** to make it your own and edit freely.\n", + "\n", + "Colab gives you a free GPU; **Weights Studio runs on your own machine** and connects to this Colab backend through a public [`bore`](https://github.com/ekzhang/bore) tunnel.\n", + "\n", + "### What the cell below does\n", + "1. Installs WeightsLab.\n", + "2. Imports it as `wl`.\n", + "3. (Your turn) registers your model / optimizer / dataloaders with `wl.watch_or_edit(...)`.\n", + "4. Serves the backend over a bore tunnel and prints the `weightslab tunnel ...` command to run locally." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 1 · Install WeightsLab (+ Colab compatibility overrides)\n", + "%pip install weightslab\n", + "\n", + "# 2 · Import\n", + "import weightslab as wl\n", + "\n", + "# 3 · Serve to Weights Studio over a public bore tunnel.\n", + "# Prints a `bore.pub:PORT` endpoint. Then, on your own machine (Docker running):\n", + "# weightslab ui launch # opens http://localhost:5173\n", + "# weightslab tunnel bore.pub:PORT # PORT = the port printed below\n", + "wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "# 4 · Register YOUR training objects here (uncomment and fill in):\n", + "# wl.watch_or_edit(model, optimizer, train_loader, test_loader)\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "ws-colab-quickstart.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/weightslab/examples/Notebooks/PyTorch/ws-classification.ipynb b/weightslab/examples/Notebooks/PyTorch/ws-classification.ipynb index 29386845..bcf6d449 100644 --- a/weightslab/examples/Notebooks/PyTorch/ws-classification.ipynb +++ b/weightslab/examples/Notebooks/PyTorch/ws-classification.ipynb @@ -2,7 +2,19 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d1Yi--O7FETe" + }, "source": [ "
\n", "\n", @@ -20,7 +32,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "POIbm5qMFETh" + }, "source": [ "# Image Classification with WeightsLab\n", "\n", @@ -38,7 +52,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "P6gYbKq5FETh" + }, "source": [ "## Setup\n", "\n", @@ -47,7 +63,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "gRQuuS90FETi" + }, "source": [ "\"PyPI\n", "\"PyPI\n", @@ -56,19 +74,94 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HW0Mrb23FETi", + "outputId": "d649cd01-8f95-4599-a5c1-d10ff377964e" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: torch in /usr/local/lib/python3.12/dist-packages (2.9.0)\n", + "Requirement already satisfied: torchvision in /usr/local/lib/python3.12/dist-packages (0.24.0)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch) (3.29.4)\n", + "Requirement already satisfied: typing-extensions>=4.10.0 in /usr/local/lib/python3.12/dist-packages (from torch) (4.15.0)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch) (3.5.0)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.12/dist-packages (from torchvision) (2.0.2)\n", + "Requirement already satisfied: pillow!=8.3.*,>=5.3.0 in /usr/local/lib/python3.12/dist-packages (from torchvision) (11.3.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch) (1.3.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch) (3.0.3)\n" + ] + } + ], "source": [ - "%pip install -q weightslab\n", - "# Override these for Colab compatibility.\n", - "!pip install --upgrade \"protobuf>=6.31.1\"\n", - "%pip install \"torchvision>=0.16\"" + "%pip install torch torchvision" ] }, { "cell_type": "markdown", - "metadata": {}, + "source": [], + "metadata": { + "id": "nQscI28FGE4Z" + } + }, + { + "cell_type": "code", + "source": [ + "%pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev6\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "collapsed": true, + "id": "PVreJLYJIod6", + "outputId": "9e77d75f-ef33-4807-a6c6-c69bb8e4a29a" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Looking in indexes: https://test.pypi.org/simple/, https://pypi.org/simple/\n", + "\u001b[31mERROR: Could not find a version that satisfies the requirement weightslab==1.3.3.dev6 (from versions: 0.0.0, 1.1.1, 1.1.2, 1.1.3.dev0, 1.1.3, 1.1.4, 1.1.5, 1.1.6, 1.1.7.dev3, 1.1.7, 1.1.8, 1.1.9, 1.2.0, 1.2.1.dev0, 1.2.1, 1.2.2, 1.2.3.dev0, 1.2.3, 1.2.4, 1.2.5, 1.2.6, 1.3.0.dev0, 1.3.0, 1.3.1.dev1, 1.3.1.dev2, 1.3.1.dev3, 1.3.1, 1.3.2.dev0, 1.3.2, 1.3.3.dev0, 1.3.3.dev1, 1.3.3.dev2, 1.3.3.dev3, 1.3.3.dev4, 1.3.3.dev5, 1.3.3, 1.9.1.dev1, 1.9.1.dev2, 1.9.1.dev3, 1.9.1.dev4, 20260427.dev1, 20260427.dev2, 20260605.dev1, 20260310110810, 20260310112657.dev2659810514, 20260310112936.dev2035376017, 20260310114507.dev36735734, 20260310122733.dev571756140, 20260310130048.dev500189762, 20260310142756.dev2389960633, 20260310172643.dev1790296826, 20260310184554.dev3572723963, 20260310190409.dev16171358, 20260311001628.dev82842326, 20260311001938.dev237981957, 20260311094631.dev2433054235, 20260311100556.dev2759443978, 20260311111421.dev1419930837, 20260311152850.dev375337088, 20260415152016.dev1529547259, 20260515094038.dev295368024, 20260515094810.dev295368024, 20260515100544.dev709462904, 20260518115533.dev1831822349, 20260518120723.dev3543420334, 20260518121158.dev2454375021, 20260518125331.dev2144318793, 20260518152946.dev2688573905, 20260518153709.dev457831340, 20260518154038.dev3380177004, 20260518154550.dev2597851767, 20260518160221.dev67736641, 20260518160351.dev500880749, 20260518160424.dev3971069761, 20260519121616.dev3380177004, 20260519122038.dev2371413592, 20260519153002.dev2989691760, 20260519160247.dev4080968838, 20260519160445.dev1656037640, 20260519160647.dev1656037640, 20260519161900.dev3430116169, 20260520150146.dev2702511536, 20260520151257.dev3739150100, 20260520151549.dev3739150100, 20260520160858.dev3843696446, 20260520161330.dev950128852, 20260521100142.dev3061163053, 20260521100237.dev3763458477, 20260521100304.dev2708183098, 20260521101422.dev1111427347, 20260521101942.dev1751890316, 20260521102210.dev697219115, 20260521112427.dev1815380353, 20260528101213.dev1156931848, 20260528101621.dev3002779928, 20260528102838.dev1225638335, 20260528104815.dev146316976, 20260528104912.dev2432105952, 20260528144750.dev228483024, 20260529124654.dev1088603485, 20260602102247.dev3028334291, 20260602133031.dev612843591, 20260603164444.dev1140145833, 20260604132920.dev4091502713, 20260604141626.dev180590797, 20260604154956.dev1777185457, 20260604161731.dev1492593462, 20260605101712.dev3201556731, 20260605102734.dev307817433, 20260605153744.dev1218078270, 20260605154211.dev1284003932, 20260605155425.dev2249789890, 20260608122954.dev616519929, 20260608130348.dev103073330, 20260608142704.dev1750894834, 20260608143725.dev279338996, 20260608144113.dev1776325482, 20260608151812.dev1493920450, 20260609145543.dev4094763614, 20260609150002.dev1824924853, 20260609150942.dev1916930876, 20260609151231.dev2007644555, 20260609164013.dev3320873074, 20260609170327.dev427086775, 20260609170903.dev4090827397, 20260609170910.dev878249345, 20260609171010.dev315170550, 20260610134434.dev2867115601, 20260610135809.dev2659845748, 20260610140822.dev3917850211, 20260610142006.dev1779011735, 20260610145358.dev827089089, 20260611100101.dev2435580864, 20260611122329.dev2942846670, 20260612094419.dev318051075, 20260612174112.dev1945936079, 20260612175512.dev558737864, 20260612175811.dev4293577391, 20260614170924.dev435351911, 20260617105222.dev3481250246, 20260617105337.dev4027879073, 20260617150202.dev2472499979, 20260617150338.dev3507587562, 20260618091144.dev3638457713, 20260618091310.dev2980634153, 20260618091437.dev666220125, 20260618161401.dev2672707490, 20260618161455.dev1062308657, 20260618161503.dev869421065, 20260619154839.dev3124849231, 20260619154914.dev1825884740, 20260619155132.dev3059317318, 20260619155148.dev2195359802, 20260619155831.dev2547505056, 20260619160048.dev2036037367, 20260619160133.dev887552290, 20260619164552.dev545225221, 20260620125803.dev57300911, 20260620155947.dev163709547, 20260620160335.dev978317758, 20260620161239.dev1614881684, 20260620163624.dev3186857944, 20260620163931.dev174614870, 20260620170427.dev937264444, 20260621140029.dev2748921241, 20260622131949.dev1993441264, 20260622132042.dev41563737, 20260626162054.dev815684860, 20260626162149.dev1517909447, 20260629095034.dev2864733358, 20260629095109.dev2365834914, 20260629100925.dev3295139208, 20260705162125.dev3118493333, 20260705162448.dev4054744437, 20260705162608.dev112194667, 20260705163739.dev337968460, 20260705163915.dev1702540858, 20260706102840.dev1428624302, 20260706103324.dev4063235919, 20260706103956.dev1761667072, 20260706105255.dev612849548, 20260706122808.dev397480003, 20260706123234.dev2929755776, 20260706124917.dev3042106597, 20260706132510.dev3579337194, 20260707110009.dev944740769, 20260707110041.dev949537649, 20260707130443.dev1726432854, 20260707130503.dev2999317701, 20260707130900.dev2393574598, 20260708143958.dev3881013472, 20260708144357.dev578891966, 20260708144457.dev658501659, 20260708144826.dev1757704307, 20260708144934.dev3945788796, 20260708145114.dev663145800, 20260710162317.dev3300605375, 20260710162608.dev3232863539)\u001b[0m\u001b[31m\n", + "\u001b[0m\u001b[31mERROR: No matching distribution found for weightslab==1.3.3.dev6\u001b[0m\u001b[31m\n", + "\u001b[0m" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pKzQf_BzFETj" + }, "source": [ "## 1. Imports\n", "\n", @@ -77,9 +170,45 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-uAGOoqVFETj", + "outputId": "1d806744-f7be-4da8-9a8c-4b68ae436a13" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:06:00.794 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmpn4he5yzg/weightslab_logs/weightslab_20260715_140600.log\n", + "15/07/2026-14:06:00.796 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "15/07/2026-14:06:00.797 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev5 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "Using device: cuda\n" + ] + } + ], "source": [ "import os\n", "import tempfile\n", @@ -107,7 +236,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "bwyu_n8VFETj" + }, "source": [ "## 2. A dataset that carries sample identity\n", "\n", @@ -116,31 +247,28 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 4, + "metadata": { + "id": "NoYmyRYwFETk" + }, "outputs": [], "source": [ "class MNISTCustomDataset(Dataset):\n", " \"\"\"MNIST that returns (image, index, label) and tracks a virtual filepath.\"\"\"\n", "\n", - " def __init__(self, root, train=True, download=False, transform=None, max_samples=None):\n", + " def __init__(self, root, train=True, download=False, transform=None):\n", " self.mnist = datasets.MNIST(root=root, train=train, download=download, transform=None)\n", " self.transform = transform\n", " self.train = train\n", - " self.max_samples = max_samples\n", " split = \"train\" if train else \"test\"\n", " self.filepaths = {}\n", " for idx in range(len(self.mnist)):\n", - " if max_samples is not None and idx >= max_samples:\n", - " break\n", " label = self.mnist.targets[idx].item()\n", " self.filepaths[idx] = os.path.join(\n", " \"MNIST\", \"processed\", split, f\"class_{label}\", f\"sample_{idx:05d}.pt\"\n", " )\n", "\n", " def __len__(self):\n", - " if self.max_samples is not None:\n", - " return min(len(self.mnist), self.max_samples)\n", " return len(self.mnist)\n", "\n", " def __getitem__(self, idx):\n", @@ -152,7 +280,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "J-jUVL-cFETk" + }, "source": [ "## 3. Configuration\n", "\n", @@ -161,39 +291,79 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pHaJYoqNFETk", + "outputId": "6bae5503-1a48-48c3-de01-8534009157a8" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:23:06.251 INFO:weightslab.components.checkpoint_manager:__init__: CheckpointManager initialized at /tmp/weightslab_mnist_yonuysxx\n", + "15/07/2026-14:23:06.253 INFO:weightslab.src:watch_or_edit: Registered new checkpoint manager in ledger\n", + "Experiment logs -> /tmp/weightslab_mnist_yonuysxx\n" + ] + } + ], "source": [ "log_dir = tempfile.mkdtemp(prefix=\"weightslab_mnist_\")\n", "\n", "config = {\n", " # -- Experiment -------------------------------------------------------\n", - " \"experiment_name\": \"mnist_classification\", # name shown in Weights Studio\n", - " \"device\": str(device), # \"cuda\" if a GPU is available, else \"cpu\"\n", + " \"experiment_name\": \"mnist_classification\", # name shown in Weights Studio\n", + " \"device\": str(device), # \"cuda\" if a GPU is available, else \"cpu\"\n", " \"root_log_dir\": log_dir, # where signal history / dataframes are written\n", " \"num_classes\": 10, # MNIST digits 0-9\n", "\n", " # -- Training schedule ------------------------------------------------\n", - " \"training_steps_to_do\": 2000, # total optimizer steps (raise for a longer live run)\n", - " \"eval_full_to_train_steps_ratio\": 100, # run a full eval every N steps\n", - " \"write_export_ratio\": 100, # export signal history + dataframe every N steps\n", + " \"training_steps_to_do\": 8000, # total optimizer steps (raise for a longer live run)\n", + " \"eval_full_to_train_steps_ratio\": 500, # run a full eval every N steps\n", + " \"experiment_dump_to_train_steps_ratio\": 250, # dump model weights ratio\n", + " \"write_export_ratio\": 1000, # export signal history + dataframe every N steps\n", + "\n", + " # Configure global dataframe storage\n", + " \"ledger_enable_flushing_threads\": True,\n", + " \"ledger_enable_h5_persistence\": True,\n", + " \"ledger_flush_max_rows\": 1024,\n", + " \"ledger_flush_interval\": 20.0,\n", "\n", " # -- Optimizer --------------------------------------------------------\n", - " \"learning_rate\": 0.01, # Adam learning rate\n", + " \"learning_rate\": 0.001, # Adam learning rate\n", "\n", " # -- Data loaders -----------------------------------------------------\n", - " \"train_batch_size\": 16, # training batch size\n", - " \"test_batch_size\": 128, # evaluation batch size\n", - " \"max_train_samples\": None, # cap train set (None = full 60k)\n", - " \"max_test_samples\": None, # cap test set (None = full 10k)\n", + " # One block per loader, keyed by loader_name. EVERY kwarg passed to\n", + " # wl.watch_or_edit(..., flag=\"data\") lives here so the wrap cell below stays\n", + " # declarative — no dataloader settings hardcoded in the cells. Add a new\n", + " # loader by adding another block here.\n", + " \"data\": {\n", + " \"train_loader\": {\n", + " \"batch_size\": 128, # training batch size\n", + " \"shuffle\": True, # shuffle each epoch\n", + " \"is_training\": True, # marks this loader as the training split\n", + " \"compute_hash\": False, # skip per-sample content hashing (faster init)\n", + " \"preload_labels\": True, # preload labels into the ledger at startup\n", + " \"preload_metadata\": False, # load metadata lazily on first access\n", + " \"enable_h5_persistence\": True, # persist per-sample stats to the H5 store\n", + " },\n", + " \"test_loader\": {\n", + " \"batch_size\": 128, # evaluation batch size\n", + " \"shuffle\": False, # keep test order stable\n", + " \"is_training\": False, # marks this loader as an eval split\n", + " \"compute_hash\": False,\n", + " \"preload_labels\": True,\n", + " \"preload_metadata\": False,\n", + " \"enable_h5_persistence\": True,\n", + " },\n", + " },\n", "\n", " # -- Services ---------------------------------------------------------\n", " \"serving_grpc\": True, # expose the gRPC backend for Weights Studio\n", - "\n", - " # -- Live UI tunnel (see the Expose section) -------------------------\n", - " \"expose_ui\": True, # open a bore tunnel so a local Studio can connect\n", - " \"backend_port\": 50051, # gRPC port the tunnel forwards\n", + " \"serving_bore\": True, # expose the bore tunnel for Weights Studio\n", "}\n", "\n", "wl.watch_or_edit(config, flag=\"hyperparameters\", poll_interval=1.0)\n", @@ -202,7 +372,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "5jiuWkNBFETk" + }, "source": [ "## 4. Wrap the training objects\n", "\n", @@ -211,37 +383,133 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d7HMuDrCFETk", + "outputId": "b5bbf0b2-9464-46ce-dc04-4841e15d0c97" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:06:03.264 INFO:weightslab.backend.model_interface:__init__: Using checkpoint manager from ledger\n", + "15/07/2026-14:06:03.266 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loading checkpoint 124b3608e98f2d94...\n", + "15/07/2026-14:06:03.267 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Target: HP=124b3608 MODEL=e98f2d94 DATA=9e70c5a5\n", + "15/07/2026-14:06:03.268 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Current: HP=124b3608 MODEL=e98f2d94 DATA=9e70c5a5\n", + "15/07/2026-14:06:03.269 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Model architecture unchanged, using current model\n", + "15/07/2026-14:06:03.292 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [OK] Loaded weights from step 6750 with RNG state\n", + "15/07/2026-14:06:03.294 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Config unchanged, using current config\n", + "15/07/2026-14:06:03.296 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loaded components: {'weights'}\n", + "15/07/2026-14:06:03.300 INFO:weightslab.backend.model_interface:__init__: Auto-loaded model weights from checkpoint 124b3608e98f2d94 (step 6750)\n", + "15/07/2026-14:06:03.304 INFO:weightslab.data.data_samples_with_ops:__init__: [DataSampleTrackingWrapper] H5 persistence enabled at /tmp/weightslab_mnist_kg8215pb/checkpoints/data/data.h5\n", + "15/07/2026-14:06:03.402 INFO:weightslab.data.data_samples_with_ops:__init__: Preloading sample statistics for PHYSICAL indices in split 'train_loader' with: preload_labels=True,\n", + "15/07/2026-14:06:03.404 INFO:weightslab.data.data_samples_with_ops:__init__: Metadata will be loaded on demand from the wrapped dataset for split 'train_loader' when accessed, which may increase latency on first access but reduces initialization time.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Initializing ledger for split 'train_loader': 100%|██████████| 60000/60000 [00:12<00:00, 4884.89it/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:06:15.708 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] Registering split 'train_loader' with 60000 samples.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:06:16.062 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] After annotation expansion: 60000 samples → 60000 annotation rows.\n", + "15/07/2026-14:06:16.064 INFO:weightslab.data.h5_array_store:__init__: [H5ArrayStore] Initialized with cache limit: 2048MB\n", + "15/07/2026-14:06:17.387 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loading checkpoint 124b3608e98f2d94...\n", + "15/07/2026-14:06:17.389 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Target: HP=124b3608 MODEL=e98f2d94 DATA=9e70c5a5\n", + "15/07/2026-14:06:17.390 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Current: HP=124b3608 MODEL=e98f2d94 DATA=9e70c5a5\n", + "15/07/2026-14:06:17.393 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Model architecture unchanged, using current model\n", + "15/07/2026-14:06:17.393 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Config unchanged, using current config\n", + "15/07/2026-14:06:17.593 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [OK] Loaded data snapshot (70000 rows) with RNG state\n", + "15/07/2026-14:06:17.594 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loaded components: {'data'}\n", + "15/07/2026-14:06:17.918 INFO:weightslab.backend.dataloader_interface:_load_checkpoint_data: Applied data snapshot from checkpoint (70000 rows)\n", + "15/07/2026-14:06:17.922 INFO:weightslab.data.data_samples_with_ops:__init__: [DataSampleTrackingWrapper] H5 persistence enabled at /tmp/weightslab_mnist_kg8215pb/checkpoints/data/data.h5\n", + "15/07/2026-14:06:17.932 INFO:weightslab.data.data_samples_with_ops:__init__: Preloading sample statistics for PHYSICAL indices in split 'test_loader' with: preload_labels=True,\n", + "15/07/2026-14:06:17.935 INFO:weightslab.data.data_samples_with_ops:__init__: Metadata will be loaded on demand from the wrapped dataset for split 'test_loader' when accessed, which may increase latency on first access but reduces initialization time.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Initializing ledger for split 'test_loader': 100%|██████████| 10000/10000 [00:01<00:00, 7865.73it/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:06:19.213 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] Registering split 'test_loader' with 10000 samples.\n", + "15/07/2026-14:06:19.273 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] After annotation expansion: 10000 samples → 10000 annotation rows.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:06:19.798 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loading checkpoint 124b3608e98f2d94...\n", + "15/07/2026-14:06:19.799 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Target: HP=124b3608 MODEL=e98f2d94 DATA=9e70c5a5\n", + "15/07/2026-14:06:19.802 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Current: HP=124b3608 MODEL=e98f2d94 DATA=9e70c5a5\n", + "15/07/2026-14:06:19.803 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Model architecture unchanged, using current model\n", + "15/07/2026-14:06:19.804 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Config unchanged, using current config\n", + "15/07/2026-14:06:19.965 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [OK] Loaded data snapshot (70000 rows) with RNG state\n", + "15/07/2026-14:06:19.966 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loaded components: {'data'}\n", + "15/07/2026-14:06:20.277 INFO:weightslab.backend.dataloader_interface:_load_checkpoint_data: Applied data snapshot from checkpoint (70000 rows)\n" + ] + } + ], "source": [ "data_root = os.path.join(log_dir, \"data\")\n", "os.makedirs(data_root, exist_ok=True)\n", "\n", "train_ds = MNISTCustomDataset(root=data_root, train=True, download=True,\n", - " transform=transforms.ToTensor(),\n", - " max_samples=config[\"max_train_samples\"])\n", + " transform=transforms.ToTensor())\n", "test_ds = MNISTCustomDataset(root=data_root, train=False, download=True,\n", - " transform=transforms.ToTensor(),\n", - " max_samples=config[\"max_test_samples\"])\n", + " transform=transforms.ToTensor())\n", "\n", "# Model + optimizer\n", - "model = wl.watch_or_edit(CNN().to(device), flag=\"model\", device=device)\n", + "model = wl.watch_or_edit(CNN().to(device), flag=\"model\", device=device, compute_dependencies=True)\n", "optimizer = wl.watch_or_edit(\n", " optim.Adam(model.parameters(), lr=config[\"learning_rate\"]), flag=\"optimizer\")\n", "\n", - "# Tracked dataloaders\n", + "# Tracked dataloaders — all loader settings come from config[\"data\"][],\n", + "# so nothing is hardcoded here. Unpack each block straight into the wrapper.\n", "train_loader = wl.watch_or_edit(\n", " train_ds, flag=\"data\", loader_name=\"train_loader\",\n", - " batch_size=config[\"train_batch_size\"], shuffle=True, is_training=True,\n", - " compute_hash=False, preload_labels=True,\n", - " preload_metadata=False, enable_h5_persistence=True,\n", + " **config[\"data\"][\"train_loader\"],\n", ")\n", "test_loader = wl.watch_or_edit(\n", " test_ds, flag=\"data\", loader_name=\"test_loader\",\n", - " batch_size=config[\"test_batch_size\"], shuffle=False, is_training=False,\n", - " compute_hash=False, preload_labels=True,\n", - " preload_metadata=False, enable_h5_persistence=True,\n", + " **config[\"data\"][\"test_loader\"],\n", ")\n", "\n", "# Watched losses + metric (they log themselves per sample)\n", @@ -256,7 +524,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "-nWmYOC1FETk" + }, "source": [ "## 5. Train and evaluate steps\n", "\n", @@ -265,8 +535,10 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 7, + "metadata": { + "id": "LFFmdEMTFETl" + }, "outputs": [], "source": [ "def train(loader, model, optimizer, criterion, device):\n", @@ -285,8 +557,10 @@ " return total_loss.detach().cpu().item()\n", "\n", "\n", + "import time\n", "def test(loader, model, criterion, metric, device, n_batches):\n", " losses = torch.tensor(0.0, device=device)\n", + " st = time.time()\n", " for inputs, ids, labels in loader:\n", " with guard_testing_context:\n", " inputs, labels = inputs.to(device), labels.to(device)\n", @@ -305,84 +579,344 @@ " \"test_metric/Inverse_Accuracy_per_sample\": 1.0 - correct,\n", " },\n", " )\n", + " print(f\"Compute test in {time.time()-st}s\")\n", " return (losses / n_batches).item(), (metric.compute() * 100).item()" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "nBNH0B0-FETl" + }, "source": [ - "## 6. (Optional) Expose the backend for the live UI\n", + "## 6. Serve and train\n", "\n", - "Skip this if you only want the inline results below. To watch training **live in Weights Studio on your own machine**, keep `config[\"expose_ui\"] = True`.\n", - "\n", - "This opens a **raw-TCP** tunnel over [`bore`](https://github.com/ekzhang/bore) and its free public relay (`bore.pub`) - **no signup, no account, no credit card** - and prints the exact `weightslab tunnel ...` command to run locally.\n", - "\n", - "> Note: raw TCP is required so gRPC's HTTP/2 frames pass through untouched (this is also why we avoid `ngrok`, which now needs a credit card for TCP endpoints).\n", - ">\n", - "> Note: `bore.pub` is a shared public relay; the random port is the only thing protecting your endpoint. Fine for a demo, not for sensitive data." + "`wl.serve(serving_grpc=True)` starts the background gRPC server (non-blocking) that Weights Studio connects to. `wl.start_training(...)` flips the experiment into the *training* state, then we run the loop, periodically evaluating and exporting signals." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], "source": [ - "import re, tarfile, threading, urllib.request, subprocess\n", - "\n", - "endpoint = None\n", - "if config[\"expose_ui\"]:\n", - " BORE = \"v0.6.0\"\n", - " if not os.path.exists(\"bore\"):\n", - " urllib.request.urlretrieve(\n", - " f\"https://github.com/ekzhang/bore/releases/download/{BORE}/bore-{BORE}-x86_64-unknown-linux-musl.tar.gz\",\n", - " \"bore.tar.gz\")\n", - " with tarfile.open(\"bore.tar.gz\") as t:\n", - " t.extractall()\n", - " os.chmod(\"bore\", 0o755)\n", - "\n", - " proc = subprocess.Popen(\n", - " [\"./bore\", \"local\", str(config[\"backend_port\"]), \"--to\", \"bore.pub\"],\n", - " stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)\n", - " for line in proc.stdout:\n", - " m = re.search(r\"bore\\.pub:(\\d+)\", line)\n", - " if m:\n", - " endpoint = f\"bore.pub:{m.group(1)}\"\n", - " break\n", - " threading.Thread(target=lambda: [None for _ in proc.stdout], daemon=True).start()\n", - "\n", - " print(\"=\" * 60)\n", - " print(\"Backend exposed at:\", endpoint)\n", - " print(\"On your OWN machine (Docker running), run these two commands:\")\n", - " print(\" weightslab ui launch\")\n", - " print(f\" weightslab tunnel {endpoint}\")\n", - " print(\"=\" * 60)\n", - "else:\n", - " print(\"expose_ui is False - inline results only, no tunnel.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Serve and train\n", - "\n", - "`wl.serve(serving_grpc=True)` starts the background gRPC server (non-blocking) that Weights Studio connects to. `wl.start_training(...)` flips the experiment into the *training* state, then we run the loop, periodically evaluating and exporting signals.\n", - "\n", - "Leave this cell **running** while you watch it stream in the UI." + "wl.serve(serving_grpc=True, serving_bore=True)" + ], + "metadata": { + "id": "YnEB98kvRBJt", + "outputId": "590e5572-23b7-4bc1-8592-b666ce7f71dc", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 886 + } + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15/07/2026-14:06:20.303 INFO:weightslab.trainer.trainer_services:_run_security_preflight: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "[gRPC] Security preflight checks:\n", + "\tTLS: DISABLED (unencrypted traffic)\n", + "\tAuth tokens: NONE configured\n", + "\t! WARNING: GRPC_TLS_ENABLED=0. Traffic will be unencrypted. Use only for development.\n", + "\t! WARNING: No GRPC_AUTH_TOKEN/GRPC_AUTH_TOKENS configured. Only transport-level trust (TLS/mTLS) will protect RPC access.\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "15/07/2026-14:06:20.305 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Watchdogs disabled via WEIGHTSLAB_DISABLE_WATCHDOGS.\n", + "15/07/2026-14:06:20.306 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Thread callback started\n", + "15/07/2026-14:06:20.307 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Creating ThreadPoolExecutor with 6 worker threads (n_workers_grpc=None, max_concurrent_rpcs=None)\n", + "15/07/2026-14:06:20.308 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Server started with watchdogs disabled (host=0.0.0.0 port=50051 workers=None)\n", + "15/07/2026-14:06:20.315 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Server object created\n", + "15/07/2026-14:06:20.525 INFO:weightslab.trainer.services.agent.agent:__init__: Initializing DataManipulationAgent\n", + "15/07/2026-14:06:20.623 WARNING:weightslab.trainer.services.agent.agent:_load_config: Error loading config from /usr/local/lib/python3.12/dist-packages: [Errno 21] Is a directory: '/usr/local/lib/python3.12/dist-packages'\n", + "15/07/2026-14:06:20.625 INFO:weightslab.trainer.services.agent.agent:_load_config: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "Agent initialized from configuration /content/agent_config.yaml: \n", + "\tFinal Agent Configuration: Preferred Provider=openrouter, \n", + "\tFallback to Local=True, \n", + "\tOpenRouter Model=~google/gemini-flash-latest with:\n", + "\t\tAPI Key=None\n", + "\t\tBase URL=https://openrouter.ai/api/v1, \n", + "\tOllama Model=llama3.2:3b\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "15/07/2026-14:06:20.625 INFO:weightslab.trainer.services.agent.agent:_setup_providers: Setting up Ollama with model llama3.2:3b\n", + "15/07/2026-14:06:20.710 INFO:weightslab.trainer.services.agent.agent:_setup_providers: [Agent] Ollama enabled: llama3.2:3b\n", + "15/07/2026-14:06:20.711 INFO:weightslab.trainer.services.data_service:_build_preview_cache: [PreviewCache] Building 64×64 or less or less preview cache for 2000 samples …\n", + "15/07/2026-14:06:20.712 INFO:weightslab.trainer.services.data_service:__init__: DataService initialized.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\r[PreviewCache]: 0%| | 0/2000 [00:00 0 and age % eval_ratio == 0:\n", + " if age == 0 and age % eval_ratio == 0:\n", " test_loss, test_acc = test(test_loader, model, test_criterion, metric, device, n_test_batches)\n", "\n", - " if age > 0 and age % export_ratio == 0:\n", - " wl.write_history()\n", - " wl.write_dataframe()\n", - "\n", " postfix = {\"loss\": f\"{train_loss:.3f}\"}\n", " if test_acc is not None:\n", " postfix[\"test_acc\"] = f\"{test_acc:.1f}%\"\n", @@ -413,39 +943,9 @@ }, { "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Which samples hurt the most?\n", - "\n", - "WeightsLab exports a per-sample dataframe to `root_log_dir`. Ranking by loss surfaces the samples the model struggles with - the usual suspects for **mislabels and outliers**. This is the same signal the Studio UI visualizes; here we peek at it inline." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import glob\n", - "import pandas as pd\n", - "\n", - "paths = sorted(glob.glob(os.path.join(log_dir, \"**\", \"*.json\"), recursive=True),\n", - " key=os.path.getmtime)\n", - "if paths:\n", - " df = pd.read_json(paths[-1])\n", - " print(f\"Loaded {paths[-1]} (shape={df.shape})\")\n", - " loss_cols = [c for c in df.columns if \"loss\" in c.lower()]\n", - " if loss_cols:\n", - " display(df.sort_values(loss_cols[-1], ascending=False).head(50))\n", - " else:\n", - " display(df.head())\n", - "else:\n", - " print(\"No export found - run the training cell first.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "a2GIwrD6FETl" + }, "source": [ "## 9. See it live in Weights Studio\n", "\n", @@ -473,7 +973,14 @@ }, { "cell_type": "markdown", - "metadata": {}, + "source": "## 10. Curate in the UI to boost performance\n\nThis is where WeightsLab pays off: instead of training longer on all 60k samples, use the live signals to **find the samples that matter, keep only those, and fine-tune**. Everything below happens in Weights Studio while the backend keeps running — no code changes, no restart.\n\n> **Baseline before curating:** the evaluation metric (test accuracy) sits around **0.86**.\n\n**1. See more of the dataset.** In the **grid explorer**, increase the number of images per page so you can scan many samples at once.\n\n**2. Find the easy and hard samples.** Sort the grid by **`train-loss-CE` in decreasing order**, then **generate the histogram** for that signal. The distribution is bimodal — a tail of **hard** examples (training loss **> 2.45**) and a bulk of **easy** ones (training loss **< 1.461**). These two extremes carry the signal; the vast middle is largely redundant.\n\n**3. Let the agent tag them.** Initialize the **agent** (the panel in the UI), then ask it:\n\n> *Tag training samples with train loss greater than 2.45 as \"hard_ex\" and training samples with train loss lower than 1.45 as \"easy_ex\".*\n\nInspect the new `tag:hard_ex` / `tag:easy_ex` columns in the grid to confirm the tagging looks right.\n\n**4. Drop the redundant middle.** Once the tags look good, ask the agent:\n\n> *Discard train samples that have no \"easy_ex\" and no \"hard_ex\" tag.*\n\nThe training set collapses from **~60k to roughly 3–4k** samples — only the informative extremes remain.\n\n**5. Fine-tune on the curated set.** **Resume** training (from the UI, or by re-running the training cell) and let it run for about **6,500 steps** on this much smaller, higher-signal dataset.\n\n**6. Evaluate.** Trigger a full evaluation from the UI: **right-click `resume` → `evaluate` → click `evaluate` → select `test_loader`**, then wait for it to finish.\n\n> **Result:** test accuracy climbs to roughly **0.95–0.99**, a large jump over the 0.86 baseline — reached by training on **~15× fewer** samples. Curating *which* data the model sees beat simply training on *more* of it.\n\nThis is the core WeightsLab loop: **read the per-sample signals → curate the dataset → fine-tune → measure**, all without leaving the UI or restarting the run.", + "metadata": {} + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZnCx2Mh2FETl" + }, "source": [ "---\n", "\n", @@ -484,11 +991,11 @@ } ], "metadata": { - "accelerator": "GPU", "colab": { "name": "ws-classification.ipynb", "provenance": [], - "toc_visible": true + "gpuType": "T4", + "include_colab_link": true }, "kernelspec": { "display_name": "Python 3", @@ -496,7 +1003,634 @@ }, "language_info": { "name": "python" - } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "version_major": 2, + "version_minor": 0, + "state": { + "8d93f084a8ae4ff0a442b8385a0febc7": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_8658fa7fc5674b7bb2d6803336825cf2", + "IPY_MODEL_3a99ecc652df435d8d16c3d1ea4b6236", + "IPY_MODEL_532b8013bc9549e692dbe8b20c99fe30" + ], + "layout": "IPY_MODEL_c68d2e0fbc504ba8bf3d057b72b52293" + } + }, + "8658fa7fc5674b7bb2d6803336825cf2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_48d77b568b6d47579f27055722205bb7", + "placeholder": "​", + "style": "IPY_MODEL_f72491d19b5c4fb8824bb4962e1fa812", + "value": "Training: 100%" + } + }, + "3a99ecc652df435d8d16c3d1ea4b6236": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_6d29e91107f246a69f7ba1c9d3541747", + "max": 6000, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_ec736e6e5cf74dd7a644222821cb5899", + "value": 6000 + } + }, + "532b8013bc9549e692dbe8b20c99fe30": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_5477fc60ab154377b690c8624b021c94", + "placeholder": "​", + "style": "IPY_MODEL_98af3369f706433f8dbb9be79d3988e2", + "value": " 6000/6000 [07:11<00:00, 45.13it/s, loss=1.774, test_acc=81.1%]" + } + }, + "c68d2e0fbc504ba8bf3d057b72b52293": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "48d77b568b6d47579f27055722205bb7": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f72491d19b5c4fb8824bb4962e1fa812": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "6d29e91107f246a69f7ba1c9d3541747": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ec736e6e5cf74dd7a644222821cb5899": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "5477fc60ab154377b690c8624b021c94": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "98af3369f706433f8dbb9be79d3988e2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "a67abfb4e9a441bdb92f5ee92593aa5c": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_257741e960664020981c3e0dc2a71a0b", + "max": 100, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_5f801ece792c416f90a86bde02d24514", + "value": 100 + } + }, + "257741e960664020981c3e0dc2a71a0b": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "auto" + } + }, + "5f801ece792c416f90a86bde02d24514": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": "black", + "description_width": "" + } + }, + "d0e534930f564af3b6fc47ff63286bd3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_4c013e26a480400bafdfa45c36830b3c", + "max": 100, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_92834b1a78d1439181f9026917f4882d", + "value": 100 + } + }, + "4c013e26a480400bafdfa45c36830b3c": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "auto" + } + }, + "92834b1a78d1439181f9026917f4882d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": "black", + "description_width": "" + } + }, + "39cf7f4f60554bfd81859b9edeb7d41f": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f35b21ef87c44a24ac9de9f3f7f58fa1", + "max": 100, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_1e96676b72ca44f399ae30822eb54501", + "value": 100 + } + }, + "f35b21ef87c44a24ac9de9f3f7f58fa1": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "auto" + } + }, + "1e96676b72ca44f399ae30822eb54501": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": "black", + "description_width": "" + } + } + } + } + }, + "accelerator": "GPU" }, "nbformat": 4, "nbformat_minor": 0 diff --git a/weightslab/examples/Notebooks/PyTorch/ws-clustering.ipynb b/weightslab/examples/Notebooks/PyTorch/ws-clustering.ipynb index 4dfcd835..a68a77b8 100644 --- a/weightslab/examples/Notebooks/PyTorch/ws-clustering.ipynb +++ b/weightslab/examples/Notebooks/PyTorch/ws-clustering.ipynb @@ -1,241 +1,5272 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - " \n", - " \"WeightsLab\n", - "\n", - " \"License\"\n", - " \"Stars\"\n", - " \"Version\"\n", - "
\n", - " \"Open\n", - "\n", - " Welcome to the WeightsLab Face Recognition (triplet loss) notebook! WeightsLab traces training signals back to the exact samples producing them. Browse the Docs for details.
" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "WvuZDK7iF391" + }, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Welcome to the WeightsLab Face Recognition (triplet loss) notebook! WeightsLab traces training signals back to the exact samples producing them. Browse the Docs for details.
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8SrJYFpJF392" + }, + "source": [ + "# Face Recognition (triplet loss) with WeightsLab\n", + "\n", + "Trains a ResNet-18 embedding head with online batch-hard **triplet loss** and tracks verification / retrieval / similarity signals per sample.\n", + "\n", + "> Data: Defaults to the **Olivetti Faces** dataset (via scikit-learn) which works **offline** - no download needed. Switch to LFW or a folder dataset in the config.\n", + "\n", + "This notebook is fully **self-contained**: the `face/` helper modules and the training loop\n", + "are inlined below, so there is no repo to clone and no `main.py` to shell out to. Run the\n", + "cells top to bottom." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NEWaY0IuF393" + }, + "source": [ + "## Setup\n", + "\n", + "On Colab, pick a **T4 GPU** runtime (`Runtime -> Change runtime type -> T4 GPU`)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v2Va8BtGF393" + }, + "source": [ + "\"PyPI\n", + "\"PyPI\n", + "\"PyPI" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "WaMERj7lF394", + "outputId": "19afa3e0-9dbc-4280-8ebf-03388009d2b4", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Looking in indexes: https://test.pypi.org/simple/, https://pypi.org/simple/\n", + "Collecting weightslab==1.3.3.dev7\n", + " Downloading https://test-files.pythonhosted.org/packages/fd/50/2281a781f96c471a8e8ec9f7e15cb6b63bcae66b362474f00f1f29e75428/weightslab-1.3.3.dev7-py3-none-any.whl.metadata (15 kB)\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (3.10.2)\n", + "Collecting torch<=2.9,>=2.1 (from weightslab==1.3.3.dev7)\n", + " Downloading torch-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (30 kB)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.26.0+cpu)\n", + "Collecting torchmetrics>=1.9 (from weightslab==1.3.3.dev7)\n", + " Downloading torchmetrics-1.9.0-py3-none-any.whl.metadata (23 kB)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.21)\n", + "Collecting onnx<=1.20,>=1.15 (from weightslab==1.3.3.dev7)\n", + " Downloading onnx-1.20.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (8.4 kB)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.4.8)\n", + "Collecting langchain-ollama<2,>=0.2 (from weightslab==1.3.3.dev7)\n", + " Downloading langchain_ollama-1.1.0-py3-none-any.whl.metadata (3.0 kB)\n", + "Collecting langchain-openai<2,>=0.2 (from weightslab==1.3.3.dev7)\n", + " Downloading langchain_openai-1.3.5-py3-none-any.whl.metadata (3.4 kB)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev7) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.16.2)\n", + "Collecting ollama<1.0.0,>=0.6.1 (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev7)\n", + " Downloading ollama-0.6.2-py3-none-any.whl.metadata (5.8 kB)\n", + "Collecting langchain-core<2,>=0.3 (from weightslab==1.3.3.dev7)\n", + " Downloading langchain_core-1.4.9-py3-none-any.whl.metadata (4.7 kB)\n", + "Collecting openai<3.0.0,>=2.45.0 (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev7)\n", + " Downloading openai-2.45.0-py3-none-any.whl.metadata (34 kB)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev7) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev7) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev7) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev7) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev7) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev7) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev7) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev7) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (2025.3.0)\n", + "Collecting nvidia-cuda-nvrtc-cu12==12.8.93 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl.metadata (1.7 kB)\n", + "Collecting nvidia-cuda-runtime-cu12==12.8.90 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (1.7 kB)\n", + "Collecting nvidia-cuda-cupti-cu12==12.8.90 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (1.7 kB)\n", + "Collecting nvidia-cudnn-cu12==9.10.2.21 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl.metadata (1.8 kB)\n", + "Collecting nvidia-cublas-cu12==12.8.4.1 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl.metadata (1.7 kB)\n", + "Collecting nvidia-cufft-cu12==11.3.3.83 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (1.7 kB)\n", + "Collecting nvidia-curand-cu12==10.3.9.90 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl.metadata (1.7 kB)\n", + "Collecting nvidia-cusolver-cu12==11.7.3.90 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl.metadata (1.8 kB)\n", + "Collecting nvidia-cusparse-cu12==12.5.8.93 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (1.8 kB)\n", + "Collecting nvidia-cusparselt-cu12==0.7.1 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl.metadata (7.0 kB)\n", + "Collecting nvidia-nccl-cu12==2.27.5 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (2.0 kB)\n", + "Collecting nvidia-nvshmem-cu12==3.3.20 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (2.1 kB)\n", + "Collecting nvidia-nvtx-cu12==12.8.90 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (1.8 kB)\n", + "Collecting nvidia-nvjitlink-cu12==12.8.93 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl.metadata (1.7 kB)\n", + "Collecting nvidia-cufile-cu12==1.13.1.3 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (1.7 kB)\n", + "Collecting triton==3.5.0 (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7)\n", + " Downloading triton-3.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (1.7 kB)\n", + "Collecting lightning-utilities>=0.15.3 (from torchmetrics>=1.9->weightslab==1.3.3.dev7)\n", + " Downloading lightning_utilities-0.15.3-py3-none-any.whl.metadata (5.5 kB)\n", + "INFO: pip is looking at multiple versions of torchvision to determine which version is compatible with other requirements. This could take a while.\n", + "Collecting torchvision<1,>=0.16 (from weightslab==1.3.3.dev7)\n", + " Downloading torchvision-0.28.0-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (5.6 kB)\n", + " Downloading torchvision-0.27.1-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (5.5 kB)\n", + " Downloading torchvision-0.27.0-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (5.5 kB)\n", + " Downloading torchvision-0.26.0-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (5.5 kB)\n", + " Downloading torchvision-0.25.0-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (5.4 kB)\n", + " Downloading torchvision-0.24.1-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (5.9 kB)\n", + " Downloading torchvision-0.24.0-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (5.9 kB)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev7) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev7) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.0.0)\n", + "Downloading https://test-files.pythonhosted.org/packages/fd/50/2281a781f96c471a8e8ec9f7e15cb6b63bcae66b362474f00f1f29e75428/weightslab-1.3.3.dev7-py3-none-any.whl (2.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.8/2.8 MB\u001b[0m \u001b[31m16.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading langchain_ollama-1.1.0-py3-none-any.whl (31 kB)\n", + "Downloading langchain_openai-1.3.5-py3-none-any.whl (121 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m121.6/121.6 kB\u001b[0m \u001b[31m3.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading langchain_core-1.4.9-py3-none-any.whl (558 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m558.3/558.3 kB\u001b[0m \u001b[31m11.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading onnx-1.20.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (18.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m18.1/18.1 MB\u001b[0m \u001b[31m67.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading torch-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl (899.7 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m899.7/899.7 MB\u001b[0m \u001b[31m1.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl (594.3 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m594.3/594.3 MB\u001b[0m \u001b[31m3.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (10.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m10.2/10.2 MB\u001b[0m \u001b[31m96.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl (88.0 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m88.0/88.0 MB\u001b[0m \u001b[31m9.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (954 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m954.8/954.8 kB\u001b[0m \u001b[31m64.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl (706.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m706.8/706.8 MB\u001b[0m \u001b[31m2.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (193.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m193.1/193.1 MB\u001b[0m \u001b[31m6.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (1.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.2/1.2 MB\u001b[0m \u001b[31m76.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl (63.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m63.6/63.6 MB\u001b[0m \u001b[31m12.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl (267.5 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m267.5/267.5 MB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (288.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m288.2/288.2 MB\u001b[0m \u001b[31m4.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl (287.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m287.2/287.2 MB\u001b[0m \u001b[31m4.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (322.3 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m322.3/322.3 MB\u001b[0m \u001b[31m1.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl (39.3 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m39.3/39.3 MB\u001b[0m \u001b[31m20.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (124.7 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m124.7/124.7 MB\u001b[0m \u001b[31m7.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (89 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m90.0/90.0 kB\u001b[0m \u001b[31m9.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading triton-3.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (170.5 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m170.5/170.5 MB\u001b[0m \u001b[31m6.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading torchmetrics-1.9.0-py3-none-any.whl (983 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m983.4/983.4 kB\u001b[0m \u001b[31m58.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading torchvision-0.24.0-cp312-cp312-manylinux_2_28_x86_64.whl (8.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m8.1/8.1 MB\u001b[0m \u001b[31m82.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading lightning_utilities-0.15.3-py3-none-any.whl (31 kB)\n", + "Downloading ollama-0.6.2-py3-none-any.whl (15 kB)\n", + "Downloading openai-2.45.0-py3-none-any.whl (1.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m64.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: nvidia-cusparselt-cu12, triton, nvidia-nvtx-cu12, nvidia-nvshmem-cu12, nvidia-nvjitlink-cu12, nvidia-nccl-cu12, nvidia-curand-cu12, nvidia-cufile-cu12, nvidia-cuda-runtime-cu12, nvidia-cuda-nvrtc-cu12, nvidia-cuda-cupti-cu12, nvidia-cublas-cu12, lightning-utilities, onnx, nvidia-cusparse-cu12, nvidia-cufft-cu12, nvidia-cudnn-cu12, openai, ollama, nvidia-cusolver-cu12, torch, langchain-core, torchvision, torchmetrics, langchain-openai, langchain-ollama, weightslab\n", + " Attempting uninstall: nvidia-nccl-cu12\n", + " Found existing installation: nvidia-nccl-cu12 2.30.7\n", + " Uninstalling nvidia-nccl-cu12-2.30.7:\n", + " Successfully uninstalled nvidia-nccl-cu12-2.30.7\n", + " Attempting uninstall: openai\n", + " Found existing installation: openai 2.43.0\n", + " Uninstalling openai-2.43.0:\n", + " Successfully uninstalled openai-2.43.0\n", + " Attempting uninstall: torch\n", + " Found existing installation: torch 2.11.0+cpu\n", + " Uninstalling torch-2.11.0+cpu:\n", + " Successfully uninstalled torch-2.11.0+cpu\n", + " Attempting uninstall: langchain-core\n", + " Found existing installation: langchain-core 1.4.8\n", + " Uninstalling langchain-core-1.4.8:\n", + " Successfully uninstalled langchain-core-1.4.8\n", + " Attempting uninstall: torchvision\n", + " Found existing installation: torchvision 0.26.0+cpu\n", + " Uninstalling torchvision-0.26.0+cpu:\n", + " Successfully uninstalled torchvision-0.26.0+cpu\n", + "Successfully installed langchain-core-1.4.9 langchain-ollama-1.1.0 langchain-openai-1.3.5 lightning-utilities-0.15.3 nvidia-cublas-cu12-12.8.4.1 nvidia-cuda-cupti-cu12-12.8.90 nvidia-cuda-nvrtc-cu12-12.8.93 nvidia-cuda-runtime-cu12-12.8.90 nvidia-cudnn-cu12-9.10.2.21 nvidia-cufft-cu12-11.3.3.83 nvidia-cufile-cu12-1.13.1.3 nvidia-curand-cu12-10.3.9.90 nvidia-cusolver-cu12-11.7.3.90 nvidia-cusparse-cu12-12.5.8.93 nvidia-cusparselt-cu12-0.7.1 nvidia-nccl-cu12-2.27.5 nvidia-nvjitlink-cu12-12.8.93 nvidia-nvshmem-cu12-3.3.20 nvidia-nvtx-cu12-12.8.90 ollama-0.6.2 onnx-1.20.0 openai-2.45.0 torch-2.9.0 torchmetrics-1.9.0 torchvision-0.24.0 triton-3.5.0 weightslab-1.3.3.dev7\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install -q weightslab\n", + "%pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev7\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wJTexBG4F395" + }, + "source": [ + "## 1. Imports\n", + "\n", + "`weightslab` is imported as `wl`. The two `guard_*_context` managers scope a block as\n", + "training vs. evaluation so signals are attributed to the right phase. We also detect the\n", + "compute `device`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "kYbxEdSMF395", + "outputId": "37ab580f-ae16-46ac-e394-9866f76469c7", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:47:42.131 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmpmfs9yakr/weightslab_logs/weightslab_20260716_084742.log\n", + "16/07/2026-08:47:42.132 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-08:47:42.134 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev7 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-08:47:42.137 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n", + "Using device: cpu\n" + ] + } + ], + "source": [ + "import logging\n", + "import os\n", + "import tempfile\n", + "\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "from torch.utils.data import Dataset\n", + "from torchvision import models\n", + "import torchvision.transforms as T\n", + "\n", + "from typing import Any, Dict, List, Optional, Tuple\n", + "\n", + "import weightslab as wl\n", + "from weightslab.components.global_monitoring import (\n", + " guard_training_context,\n", + " guard_testing_context,\n", + ")\n", + "\n", + "logging.basicConfig(level=logging.ERROR)\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "print(f\"Using device: {device}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9nIr37kPF396" + }, + "source": [ + "## 2. Face helper modules (inlined)\n", + "\n", + "These are the `face/` package modules from the original example, inlined here so the\n", + "notebook is self-contained. Only the intra-package `from face. ...` imports were removed -\n", + "every class and function is otherwise unchanged. Order follows the dependencies:\n", + "**utils -> data -> model -> signals**." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "94QCaUYjF396" + }, + "outputs": [], + "source": [ + "# ===== face/utils.py =====\n", + "\"\"\"\n", + "Utility functions for face recognition training.\n", + "- Pairwise distance computation\n", + "- Online batch-hard triplet mining\n", + "- Verification and Rank-1 evaluation helpers\n", + "- Similarity grouping signals for clustering-oriented analysis\n", + "\"\"\"\n", + "\n", + "import numpy as np\n", + "import torch\n", + "\n", + "from typing import Tuple, Dict, List, Any\n", + "\n", + "\n", + "# ============================================================\n", + "# Distance helpers\n", + "# ============================================================\n", + "\n", + "def pairwise_distances(embeddings: torch.Tensor, squared: bool = False) -> torch.Tensor:\n", + " \"\"\"Compute pairwise L2 distance matrix for a batch of embeddings.\n", + "\n", + " Args:\n", + " embeddings: (B, D) tensor\n", + " squared: return squared L2 distances when True\n", + "\n", + " Returns:\n", + " (B, B) distance matrix\n", + " \"\"\"\n", + " dot = torch.matmul(embeddings, embeddings.t())\n", + " sq_norms = torch.diagonal(dot) # (B,)\n", + " distances = sq_norms.unsqueeze(0) - 2.0 * dot + sq_norms.unsqueeze(1)\n", + " distances = distances.clamp(min=0.0)\n", + "\n", + " if not squared:\n", + " # Avoid NaN gradients at exactly 0\n", + " mask = (distances == 0.0).float()\n", + " distances = distances + mask * 1e-16\n", + " distances = torch.sqrt(distances)\n", + " distances = distances * (1.0 - mask)\n", + "\n", + " return distances\n", + "\n", + "\n", + "# ============================================================\n", + "# Triplet mining\n", + "# ============================================================\n", + "\n", + "def mine_batch_hard(\n", + " embeddings: torch.Tensor,\n", + " labels: torch.Tensor,\n", + " squared: bool = False,\n", + ") -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:\n", + " \"\"\"Online batch-hard triplet mining (Hermans et al., 2017).\n", + "\n", + " For each anchor selects:\n", + " - Hardest positive : same-class sample with the **largest** distance\n", + " - Hardest negative : different-class sample with the **smallest** distance\n", + "\n", + " Args:\n", + " embeddings: (B, D) detached from graph during mining\n", + " labels: (B,) integer class ids\n", + " squared: use squared L2 distances for mining\n", + "\n", + " Returns:\n", + " anc_idx, pos_idx, neg_idx 1-D LongTensors; only valid anchors\n", + " (those that have at least one positive peer) are included.\n", + " \"\"\"\n", + " dist_mat = pairwise_distances(embeddings.detach(), squared=squared)\n", + " B = labels.shape[0]\n", + " device = labels.device\n", + "\n", + " same = labels.unsqueeze(0) == labels.unsqueeze(1) # (B, B)\n", + " diff = ~same\n", + " eye = torch.eye(B, dtype=torch.bool, device=device)\n", + "\n", + " # Hardest positive (exclude self)\n", + " pos_mask = same & ~eye\n", + " pos_dist = dist_mat * pos_mask.float()\n", + " _, pos_idx = pos_dist.max(dim=1)\n", + "\n", + " # Hardest negative (mask invalid positions with large value)\n", + " neg_dist = dist_mat + (~diff).float() * 1e9\n", + " _, neg_idx = neg_dist.min(dim=1)\n", + "\n", + " # Keep only anchors that have at least one positive peer in the batch\n", + " valid = pos_mask.any(dim=1)\n", + " anc_idx = torch.where(valid)[0]\n", + "\n", + " return anc_idx, pos_idx[anc_idx], neg_idx[anc_idx]\n", + "\n", + "\n", + "# ============================================================\n", + "# Numpy-level evaluation helpers\n", + "# ============================================================\n", + "\n", + "def compute_verification_metrics(\n", + " embeddings: np.ndarray,\n", + " labels: np.ndarray,\n", + " thresholds: np.ndarray | None = None,\n", + ") -> dict:\n", + " \"\"\"Pairwise verification accuracy over a threshold grid.\"\"\"\n", + " if thresholds is None:\n", + " thresholds = np.linspace(0.0, 2.0, 100)\n", + "\n", + " n = len(embeddings)\n", + "\n", + " # Pairwise L2 distances\n", + " dot = embeddings @ embeddings.T # (N, N)\n", + " sq = np.sum(embeddings ** 2, axis=1)\n", + " dist_mat = (sq[:, None] - 2.0 * dot + sq[None, :]).clip(min=0.0)\n", + " dist_mat = np.sqrt(dist_mat.clip(min=1e-16)) * (dist_mat != 0.0)\n", + "\n", + " same_pair = labels[:, None] == labels[None, :] # (N, N)\n", + "\n", + " # Upper triangle only (avoid double-counting)\n", + " iu = np.triu_indices(n, k=1)\n", + " dist_pairs = dist_mat[iu]\n", + " is_same_pair = same_pair[iu]\n", + "\n", + " n_same = int(is_same_pair.sum())\n", + " n_diff = int((~is_same_pair).sum())\n", + "\n", + " best_acc = 0.0\n", + " best_threshold = float(thresholds[len(thresholds) // 2])\n", + " best_far = 0.0\n", + " best_frr = 0.0\n", + "\n", + " for thr in thresholds:\n", + " pred_same = dist_pairs <= thr\n", + " tp = int((pred_same & is_same_pair).sum())\n", + " fp = int((pred_same & ~is_same_pair).sum())\n", + " fn = int((~pred_same & is_same_pair).sum())\n", + " tn = int((~pred_same & ~is_same_pair).sum())\n", + "\n", + " total = n_same + n_diff\n", + " acc = (tp + tn) / total if total > 0 else 0.0\n", + " far = fp / n_diff if n_diff > 0 else 0.0\n", + " frr = fn / n_same if n_same > 0 else 0.0\n", + "\n", + " if acc > best_acc:\n", + " best_acc = acc\n", + " best_threshold = float(thr)\n", + " best_far = far\n", + " best_frr = frr\n", + "\n", + " return {\n", + " \"verification_accuracy\": float(best_acc),\n", + " \"best_threshold\": float(best_threshold),\n", + " \"far\": float(best_far),\n", + " \"frr\": float(best_frr),\n", + " }\n", + "\n", + "\n", + "def compute_rank1_accuracy(embeddings: np.ndarray, labels: np.ndarray) -> float:\n", + " \"\"\"1-NN Rank-1 retrieval accuracy (leave-one-out).\"\"\"\n", + " n = len(embeddings)\n", + " dot = embeddings @ embeddings.T\n", + " sq = np.sum(embeddings ** 2, axis=1)\n", + " dist_mat = (sq[:, None] - 2.0 * dot + sq[None, :]).clip(min=0.0)\n", + "\n", + " # Exclude self\n", + " np.fill_diagonal(dist_mat, 1e9)\n", + " nn_idx = dist_mat.argmin(axis=1)\n", + "\n", + " correct = int(np.sum(labels[nn_idx] == labels))\n", + " return correct / n\n", + "\n", + "\n", + "def compute_similarity_grouping(\n", + " embeddings: np.ndarray,\n", + " uids: List[str],\n", + " cluster_eps: float = 0.6,\n", + " cluster_min_samples: int = 2,\n", + ") -> Dict[str, Any]:\n", + " \"\"\"Build per-sample similarity grouping signals for test-set clustering.\n", + "\n", + " Returns per-sample arrays that can be written as WeightsLAB signals:\n", + " - cluster_id: DBSCAN cluster index (-1 for noise)\n", + " - cluster_size: size of assigned cluster (1 for noise)\n", + " - nn1_uid: nearest-neighbor UID in the evaluated set\n", + " - nn1_distance: nearest-neighbor L2 distance\n", + " \"\"\"\n", + " n = len(embeddings)\n", + " if n == 0:\n", + " return {\n", + " \"cluster_id\": np.array([], dtype=np.int32),\n", + " \"cluster_size\": np.array([], dtype=np.int32),\n", + " \"nn1_uid\": [],\n", + " \"nn1_distance\": np.array([], dtype=np.float32),\n", + " \"num_clusters\": 0.0,\n", + " \"noise_ratio\": 0.0,\n", + " \"mean_nn1_distance\": float(\"nan\"),\n", + " }\n", + "\n", + " # Pairwise distances for NN lookup\n", + " dot = embeddings @ embeddings.T\n", + " sq = np.sum(embeddings ** 2, axis=1)\n", + " dist_mat = (sq[:, None] - 2.0 * dot + sq[None, :]).clip(min=0.0)\n", + " dist_mat = np.sqrt(dist_mat.clip(min=1e-16))\n", + "\n", + " if n == 1:\n", + " nn_idx = np.array([0], dtype=np.int64)\n", + " nn_dist = np.array([0.0], dtype=np.float32)\n", + " else:\n", + " np.fill_diagonal(dist_mat, 1e9)\n", + " nn_idx = dist_mat.argmin(axis=1)\n", + " nn_dist = dist_mat[np.arange(n), nn_idx].astype(np.float32)\n", + "\n", + " nn_uid = [uids[int(i)] for i in nn_idx.tolist()]\n", + "\n", + " # Unsupervised clustering on embeddings\n", + " try:\n", + " from sklearn.cluster import DBSCAN\n", + "\n", + " labels = DBSCAN(eps=cluster_eps, min_samples=cluster_min_samples, metric=\"euclidean\").fit_predict(embeddings)\n", + " except Exception:\n", + " labels = np.full((n,), -1, dtype=np.int32)\n", + "\n", + " labels = labels.astype(np.int32)\n", + " valid_labels = labels[labels >= 0]\n", + " num_clusters = int(len(np.unique(valid_labels))) if valid_labels.size > 0 else 0\n", + "\n", + " counts = {}\n", + " if valid_labels.size > 0:\n", + " uniq, cnt = np.unique(valid_labels, return_counts=True)\n", + " counts = {int(k): int(v) for k, v in zip(uniq.tolist(), cnt.tolist())}\n", + "\n", + " cluster_size = np.array([counts.get(int(c), 1) if int(c) >= 0 else 1 for c in labels], dtype=np.int32)\n", + " noise_ratio = float(np.mean(labels < 0))\n", + "\n", + " return {\n", + " \"cluster_id\": labels,\n", + " \"cluster_size\": cluster_size,\n", + " \"nn1_uid\": nn_uid,\n", + " \"nn1_distance\": nn_dist,\n", + " \"num_clusters\": float(num_clusters),\n", + " \"noise_ratio\": noise_ratio,\n", + " \"mean_nn1_distance\": float(nn_dist.mean()) if len(nn_dist) > 0 else float(\"nan\"),\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "P6rEQuj6F397" + }, + "outputs": [], + "source": [ + "# ===== face/data.py =====\n", + "\"\"\"\n", + "FaceDataset — toy face recognition dataset.\n", + "\n", + "Supported back-ends\n", + "-------------------\n", + "\"olivetti\" Olivetti Faces from sklearn (40 identities, 400 images, 64×64 grey).\n", + " Self-contained; requires only scikit-learn. Good default toy set.\n", + "\"lfw\" Labeled Faces in the Wild (LFW) via torchvision. Downloaded on\n", + " first use to *root*. Much larger but realistic.\n", + "\"folder\" Generic ImageFolder layout: root/{split}/{class_name}/*.jpg\n", + "\n", + "Every sample is returned as:\n", + " (image_tensor: Tensor[C,H,W],\n", + " uid: str,\n", + " label: int,\n", + " metadata: dict)\n", + "\n", + "These map directly onto the (data, uid, target, metadata) convention used\n", + "throughout the WeightsLAB kitchen examples so the same training loop works\n", + "without modification.\n", + "\"\"\"\n", + "\n", + "import os\n", + "import logging\n", + "\n", + "import numpy as np\n", + "import torch\n", + "from torch.utils.data import Dataset\n", + "from typing import Dict, Optional, Tuple\n", + "\n", + "import torchvision.transforms as T\n", + "\n", + "logger = logging.getLogger(__name__)\n", + "\n", + "\n", + "# ============================================================\n", + "# Dataset\n", + "# ============================================================\n", + "\n", + "class FaceDataset(Dataset):\n", + " \"\"\"Unified face recognition dataset wrapper.\n", + "\n", + " Args:\n", + " root: Download / data root (only used for lfw / folder).\n", + " dataset_type: One of \"olivetti\", \"lfw\", \"folder\".\n", + " split: \"train\" or \"test\" (ignored for pre-split sources).\n", + " image_size: Spatial size; images are resized to (image_size, image_size).\n", + " train_ratio: Fraction of per-class samples used for training\n", + " (Olivetti only).\n", + " min_images_per_class: Classes with fewer samples are discarded.\n", + " transform: Optional torchvision transform; defaults to\n", + " Resize → ToTensor → Normalize([0.5], [0.5]).\n", + " seed: RNG seed for reproducible train/test splits.\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " root: str = \".\",\n", + " dataset_type: str = \"olivetti\",\n", + " split: str = \"train\",\n", + " image_size: int = 64,\n", + " train_ratio: float = 0.8,\n", + " min_images_per_class: int = 2,\n", + " transform=None,\n", + " seed: int = 42,\n", + " ):\n", + " self.dataset_type = dataset_type\n", + " self.split = split\n", + " self.image_size = image_size\n", + " self.transform = transform or self._default_transform(image_size)\n", + "\n", + " # These are populated by each loader\n", + " self.images: Optional[np.ndarray] = None # (N, H, W) float [0,1] — Olivetti only\n", + " self.img_paths: Optional[np.ndarray] = None\n", + " self.labels: np.ndarray = np.array([], dtype=np.int64)\n", + " self.num_classes: int = 0\n", + "\n", + " if dataset_type == \"olivetti\":\n", + " self._load_olivetti(train_ratio, min_images_per_class, seed)\n", + " elif dataset_type == \"lfw\":\n", + " self._load_lfw(root, min_images_per_class, split)\n", + " elif dataset_type == \"folder\":\n", + " self._load_folder(root, split)\n", + " else:\n", + " raise ValueError(\n", + " f\"Unknown dataset_type {dataset_type!r}. \"\n", + " \"Choose from 'olivetti', 'lfw', 'folder'.\"\n", + " )\n", + "\n", + " logger.info(\n", + " f\"FaceDataset [{dataset_type} / {split}] \"\n", + " f\"| samples={len(self)} | classes={self.num_classes}\"\n", + " )\n", + "\n", + " # ----------------------------------------------------------\n", + " # Loaders\n", + " # ----------------------------------------------------------\n", + "\n", + " @staticmethod\n", + " def _default_transform(image_size: int):\n", + " return T.Compose([\n", + " T.Resize((image_size, image_size)),\n", + " T.ToTensor(),\n", + " T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),\n", + " ])\n", + "\n", + " def _load_olivetti(self, train_ratio: float, min_images: int, seed: int):\n", + " \"\"\"Load and split the Olivetti Faces dataset (sklearn).\"\"\"\n", + " from sklearn.datasets import fetch_olivetti_faces\n", + "\n", + " data = fetch_olivetti_faces(shuffle=True, random_state=seed)\n", + " images = data.images # (400, 64, 64) float [0,1]\n", + " labels = data.target.astype(np.int64)\n", + "\n", + " # Drop classes with insufficient samples\n", + " unique, counts = np.unique(labels, return_counts=True)\n", + " valid_classes = unique[counts >= min_images]\n", + " mask = np.isin(labels, valid_classes)\n", + " images, labels = images[mask], labels[mask]\n", + "\n", + " # Remap labels to a contiguous 0…N-1 range\n", + " mapping = {int(c): i for i, c in enumerate(sorted(valid_classes.tolist()))}\n", + " labels = np.array([mapping[int(l)] for l in labels], dtype=np.int64)\n", + "\n", + " # Per-class stratified train/test split\n", + " rng = np.random.RandomState(seed)\n", + " train_idx, test_idx = [], []\n", + " for cls in np.unique(labels):\n", + " idx = np.where(labels == cls)[0]\n", + " n_train = max(1, int(len(idx) * train_ratio))\n", + " perm = rng.permutation(len(idx))\n", + " train_idx.extend(idx[perm[:n_train]].tolist())\n", + " test_idx.extend(idx[perm[n_train:]].tolist())\n", + "\n", + " indices = train_idx if self.split == \"train\" else test_idx\n", + " self.images = images[indices]\n", + " self.labels = labels[indices]\n", + " self.num_classes = len(mapping)\n", + "\n", + " def _load_lfw(self, root: str, min_images: int, split: str):\n", + " \"\"\"Load LFW People via torchvision (downloads on first call).\"\"\"\n", + " from torchvision.datasets import LFWPeople\n", + "\n", + " split_map = {\"train\": \"train\", \"test\": \"test\", \"val\": \"10fold\"}\n", + " lfw_split = split_map.get(split, \"train\")\n", + " ds = LFWPeople(root=root, split=lfw_split, download=True, transform=None)\n", + "\n", + " paths, lbls = zip(*ds.imgs)\n", + " lbls = np.array(lbls, dtype=np.int64)\n", + "\n", + " # Filter low-shot identities\n", + " unique, counts = np.unique(lbls, return_counts=True)\n", + " valid = set(unique[counts >= min_images].tolist())\n", + " mask = np.array([int(l) in valid for l in lbls])\n", + "\n", + " self.img_paths = np.array(paths)[mask]\n", + " lbls = lbls[mask]\n", + "\n", + " mapping = {int(c): i for i, c in enumerate(sorted(valid))}\n", + " self.labels = np.array([mapping[int(l)] for l in lbls], dtype=np.int64)\n", + " self.num_classes = len(mapping)\n", + "\n", + " def _load_folder(self, root: str, split: str):\n", + " \"\"\"Load from a torchvision ImageFolder directory.\"\"\"\n", + " from torchvision.datasets import ImageFolder\n", + "\n", + " split_dir = os.path.join(root, split)\n", + " ds = ImageFolder(split_dir)\n", + " paths, lbls = zip(*ds.imgs)\n", + " self.img_paths = list(paths)\n", + " self.labels = np.array(lbls, dtype=np.int64)\n", + " self.num_classes = len(ds.classes)\n", + "\n", + " # ----------------------------------------------------------\n", + " # Dataset interface\n", + " # ----------------------------------------------------------\n", + "\n", + " def __len__(self) -> int:\n", + " return len(self.labels)\n", + "\n", + " def __getitem__(self, idx: int) -> Tuple[torch.Tensor, str, int, Dict]:\n", + " \"\"\"Return (image_tensor, uid, label_id, metadata).\n", + "\n", + " The (data, uid, target, metadata) signature mirrors the WeightsLAB\n", + " kitchen convention used in the VLA training example.\n", + " \"\"\"\n", + " label = int(self.labels[idx])\n", + "\n", + " if self.dataset_type == \"olivetti\":\n", + " from PIL import Image as PILImage\n", + " img_np = self.images[idx] # (H, W) float [0,1]\n", + " img_pil = PILImage.fromarray(\n", + " (img_np * 255).astype(np.uint8), mode=\"L\"\n", + " ).convert(\"RGB\")\n", + " else:\n", + " from PIL import Image as PILImage\n", + " img_pil = PILImage.open(self.img_paths[idx]).convert(\"RGB\")\n", + "\n", + " image_tensor = self.transform(img_pil)\n", + "\n", + " uid = f\"{self.split}_cls{label:04d}_idx{idx:06d}\"\n", + " metadata = {\n", + " \"split\": self.split,\n", + " \"label_id\": label,\n", + " \"idx\": idx,\n", + " \"dataset_type\": self.dataset_type,\n", + " }\n", + " return image_tensor, uid, label, metadata" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "0mFrPy3QF397" + }, + "outputs": [], + "source": [ + "# ===== face/model.py =====\n", + "\"\"\"\n", + "Face embedding model.\n", + "\n", + "Architecture\n", + "------------\n", + "Pretrained backbone (ResNet-18 / ResNet-50 / MobileNet-V3-Small)\n", + " ?\n", + "EmbeddingHead Linear ? BN ? ReLU ? Linear\n", + " ?\n", + "L2-normalised D-dimensional embedding\n", + "\n", + "The backbone is optionally frozen so that only the lightweight head is trained\n", + "(recommended toy-example setup). The combined graph is registered with\n", + "WeightsLAB for model tracking.\n", + "\n", + "Public interface\n", + "----------------\n", + "FaceEmbeddingModel.get_embeddings(images) ? normalised embeddings (B, D)\n", + "FaceEmbeddingModel.train_step(images, labels, ...) ? scalar loss float\n", + "\"\"\"\n", + "\n", + "import logging\n", + "\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "import weightslab as wl\n", + "\n", + "from torchvision import models\n", + "from typing import List\n", + "\n", + "\n", + "logger = logging.getLogger(__name__)\n", + "\n", + "\n", + "# ============================================================\n", + "# Network modules\n", + "# ============================================================\n", + "\n", + "class EmbeddingHead(nn.Module):\n", + " \"\"\"Projection head: backbone_feature_dim ? embedding_dim (L2-normalised).\"\"\"\n", + "\n", + " def __init__(self, in_dim: int, hidden_dim: int, embedding_dim: int):\n", + " super().__init__()\n", + " self.net = nn.Sequential(\n", + " nn.Linear(in_dim, hidden_dim),\n", + " nn.BatchNorm1d(hidden_dim),\n", + " nn.ReLU(inplace=True),\n", + " nn.Linear(hidden_dim, embedding_dim),\n", + " )\n", + "\n", + " def forward(self, x: torch.Tensor) -> torch.Tensor:\n", + " return F.normalize(self.net(x), p=2, dim=-1)\n", + "\n", + "\n", + "class FaceEmbeddingNet(nn.Module):\n", + " \"\"\"Single nn.Module combining the frozen backbone and trainable head.\"\"\"\n", + "\n", + " def __init__(self, backbone: nn.Module, head: EmbeddingHead):\n", + " super().__init__()\n", + " self.backbone = backbone\n", + " self.head = head\n", + "\n", + " def forward(self, x: torch.Tensor) -> torch.Tensor:\n", + " features = self.backbone(x) # (B, feature_dim)\n", + " embeddings = self.head(features) # (B, embedding_dim), L2-normalised\n", + " return embeddings\n", + "\n", + "\n", + "class TripletLossWithMetadata(nn.Module):\n", + " \"\"\"Triplet loss wrapper that logs anchor-level triplet metadata in WeightsLAB.\n", + "\n", + " The anchor UID is used as batch ID. For each anchor we persist the selected\n", + " positive and negative UIDs into per-sample signal columns so they remain\n", + " attached to image/sample metadata in the ledger.\n", + " \"\"\"\n", + "\n", + " def __init__(self, margin: float = 0.3):\n", + " super().__init__()\n", + " self.margin = margin\n", + " self.criterion = nn.TripletMarginLoss(margin=margin, p=2, reduce=False)\n", + "\n", + " def forward(\n", + " self,\n", + " anchor_emb: torch.Tensor,\n", + " pos_emb: torch.Tensor,\n", + " neg_emb: torch.Tensor,\n", + " anchor_ids: List[str],\n", + " pos_ids: List[str],\n", + " neg_ids: List[str],\n", + " name: str = \"train\",\n", + " ) -> torch.Tensor:\n", + " loss = self.criterion(anchor_emb, pos_emb, neg_emb)\n", + "\n", + " wl.save_signals(\n", + " batch_ids=anchor_ids,\n", + " signals={f\"{name}/loss/triplet\": loss.detach().cpu().numpy()},\n", + " preds_raw=anchor_emb,\n", + " targets=neg_emb,\n", + " preds=None,\n", + " log=True,\n", + " )\n", + "\n", + " # Store pivot triplet composition as per-sample metadata-like signals.\n", + " wl.save_signals(\n", + " batch_ids=anchor_ids,\n", + " signals={\n", + " f\"{name}/meta/triplet_pos_uid\": pos_ids,\n", + " f\"{name}/meta/triplet_neg_uid\": neg_ids,\n", + " },\n", + " preds_raw=None,\n", + " targets=None,\n", + " preds=None,\n", + " log=False,\n", + " )\n", + " return loss\n", + "\n", + "\n", + "# ============================================================\n", + "# High-level model wrapper\n", + "# ============================================================\n", + "\n", + "class FaceEmbeddingModel:\n", + " \"\"\"Wrapper that manages the backbone + head, optimiser, and WeightsLAB tracking.\n", + "\n", + " Args:\n", + " backbone_name: \"resnet18\" | \"resnet50\" | \"mobilenet_v3_small\"\n", + " embedding_dim: Output embedding dimensionality (default 128).\n", + " head_hidden_dim: Hidden size of the projection MLP (default 256).\n", + " lr: Learning rate for AdamW (default 1e-3).\n", + " weight_decay: AdamW weight decay (default 1e-4).\n", + " freeze_backbone: When True, only the head's parameters receive\n", + " gradients ? recommended for quick toy runs.\n", + " device: \"cpu\", \"cuda\", or \"cuda:N\".\n", + " pretrained: Load ImageNet-pretrained weights for the backbone.\n", + " margin: Triplet margin (default 0.3).\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " backbone_name: str = \"resnet18\",\n", + " embedding_dim: int = 128,\n", + " head_hidden_dim: int = 256,\n", + " lr: float = 1e-3,\n", + " weight_decay: float = 1e-4,\n", + " freeze_backbone: bool = True,\n", + " device: str = \"cpu\",\n", + " pretrained: bool = True,\n", + " margin: float = 0.3,\n", + " ):\n", + " self.device = torch.device(device)\n", + " self.margin = margin\n", + " self.embedding_dim = embedding_dim\n", + "\n", + " # ---- Build backbone ----\n", + " weights = \"DEFAULT\" if pretrained else None\n", + " backbone_feature_dim = self._build_backbone(backbone_name, weights, freeze_backbone)\n", + "\n", + " # ---- Build head ----\n", + " head = EmbeddingHead(\n", + " in_dim=backbone_feature_dim,\n", + " hidden_dim=head_hidden_dim,\n", + " embedding_dim=embedding_dim,\n", + " )\n", + "\n", + " # ---- Compose & move to device ----\n", + " self.net = FaceEmbeddingNet(backbone=self._backbone, head=head).to(self.device)\n", + "\n", + " # ---- WeightsLAB graph tracking ----\n", + " self.net = wl.watch_or_edit(\n", + " self.net,\n", + " flag=\"model\",\n", + " compute_dependencies=False,\n", + " device=str(self.device),\n", + " )\n", + "\n", + " # ---- WeightsLAB tracked loss ----\n", + " self.triplet_loss_fn = TripletLossWithMetadata(margin=self.margin)\n", + " self.triplet_loss_fn.__name__ = \"tripletcriterion\"\n", + " watched_loss = wl.watch_or_edit(\n", + " self.triplet_loss_fn,\n", + " flag=\"loss\",\n", + " signal_name=\"triplet_with_metadata\",\n", + " log=False,\n", + " use_batch_ids_as_x=True,\n", + " use_batch_value_as_y=False,\n", + " )\n", + " if watched_loss is not None and hasattr(watched_loss, \"forward\"):\n", + " self.triplet_loss_fn = watched_loss\n", + "\n", + " # ---- Optimiser (head-only when backbone is frozen) ----\n", + " trainable = [p for p in self.net.parameters() if p.requires_grad]\n", + " self.optimizer = torch.optim.AdamW(\n", + " trainable, lr=lr, weight_decay=weight_decay\n", + " )\n", + "\n", + " n_trainable = sum(p.numel() for p in trainable)\n", + " logger.info(\n", + " f\"FaceEmbeddingModel | backbone={backbone_name} pretrained={pretrained} \"\n", + " f\"frozen={freeze_backbone} | emb_dim={embedding_dim} | \"\n", + " f\"trainable_params={n_trainable:,}\"\n", + " )\n", + " print(\n", + " f\" Backbone : {backbone_name} (pretrained={pretrained}, frozen={freeze_backbone})\\n\"\n", + " f\" Emb dim : {embedding_dim}\\n\"\n", + " f\" Head dim : {head_hidden_dim}\\n\"\n", + " f\" Trainable : {n_trainable:,} params\\n\"\n", + " f\" Device : {self.device}\"\n", + " )\n", + "\n", + " def _build_backbone(\n", + " self,\n", + " backbone_name: str,\n", + " weights,\n", + " freeze: bool,\n", + " ) -> int:\n", + " \"\"\"Instantiate backbone, strip its classifier, optionally freeze.\n", + "\n", + " Sets self._backbone and returns the feature dimensionality.\n", + " \"\"\"\n", + " if backbone_name == \"resnet18\":\n", + " base = models.resnet18(weights=weights)\n", + " feature_dim = 512\n", + " base.fc = nn.Identity()\n", + " elif backbone_name == \"resnet50\":\n", + " base = models.resnet50(weights=weights)\n", + " feature_dim = 2048\n", + " base.fc = nn.Identity()\n", + " elif backbone_name == \"mobilenet_v3_small\":\n", + " base = models.mobilenet_v3_small(weights=weights)\n", + " feature_dim = 576\n", + " base.classifier = nn.Identity()\n", + " else:\n", + " raise ValueError(\n", + " f\"Unsupported backbone {backbone_name!r}. \"\n", + " \"Choose from 'resnet18', 'resnet50', 'mobilenet_v3_small'.\"\n", + " )\n", + "\n", + " if freeze:\n", + " for param in base.parameters():\n", + " param.requires_grad_(False)\n", + " logger.info(\"Backbone frozen ? training head only.\")\n", + "\n", + " self._backbone = base\n", + " return feature_dim\n", + "\n", + " # ----------------------------------------------------------\n", + " # Inference\n", + " # ----------------------------------------------------------\n", + "\n", + " def get_embeddings(self, images: torch.Tensor) -> torch.Tensor:\n", + " \"\"\"Forward pass in eval mode; returns L2-normalised embeddings (B, D).\n", + "\n", + " Args:\n", + " images: (B, C, H, W) float tensor (GPU/CPU ? moved internally)\n", + "\n", + " Returns:\n", + " (B, D) embedding tensor on CPU\n", + " \"\"\"\n", + " self.net.eval()\n", + " with torch.no_grad():\n", + " emb = self.net(images.to(self.device))\n", + " return emb.cpu()\n", + "\n", + " # ----------------------------------------------------------\n", + " # Training step\n", + " # ----------------------------------------------------------\n", + "\n", + " def train_step(\n", + " self,\n", + " images: torch.Tensor,\n", + " labels: torch.Tensor,\n", + " batch_ids: List[str],\n", + " loss_name: str = \"triplet\",\n", + " ) -> float:\n", + " \"\"\"One gradient update using online batch-hard triplet mining.\n", + "\n", + " Args:\n", + " images: (B, C, H, W) float tensor\n", + " labels: (B,) long tensor of identity ids\n", + " batch_ids: list of sample UIDs for WeightsLAB signal logging\n", + " loss_name: \"triplet\" (contrastive support planned)\n", + "\n", + " Returns:\n", + " Scalar loss value (Python float)\n", + " \"\"\"\n", + " self.net.train()\n", + " self.optimizer.zero_grad()\n", + "\n", + " images = images.to(self.device)\n", + " labels = labels.to(self.device)\n", + "\n", + " embeddings = self.net(images) # (B, D)\n", + "\n", + " # Mine hardest triplets in the batch\n", + " anc_idx, pos_idx, neg_idx = mine_batch_hard(embeddings, labels)\n", + "\n", + " if anc_idx.numel() == 0:\n", + " logger.warning(\n", + " \"No valid triplets found in batch (all samples same class?); \"\n", + " \"skipping gradient step.\"\n", + " )\n", + " return 0.0\n", + "\n", + " anc_emb = embeddings[anc_idx]\n", + " pos_emb = embeddings[pos_idx]\n", + " neg_emb = embeddings[neg_idx]\n", + "\n", + " triplet_ids = [batch_ids[i] for i in anc_idx.tolist()]\n", + " pos_triplet_ids = [batch_ids[i] for i in pos_idx.tolist()]\n", + " neg_triplet_ids = [batch_ids[i] for i in neg_idx.tolist()]\n", + "\n", + " if loss_name == \"triplet\":\n", + " loss_by_sample = self.triplet_loss_fn(\n", + " anchor_emb=anc_emb,\n", + " pos_emb=pos_emb,\n", + " neg_emb=neg_emb,\n", + " anchor_ids=triplet_ids,\n", + " pos_ids=pos_triplet_ids,\n", + " neg_ids=neg_triplet_ids,\n", + " name=\"train\",\n", + " )\n", + " else:\n", + " raise ValueError(\n", + " f\"Unsupported loss {loss_name!r}. Use 'triplet'.\"\n", + " )\n", + " # Aggregate per-sample losses into a single scalar and backprop\n", + " loss = loss_by_sample.mean()\n", + " loss.backward()\n", + " torch.nn.utils.clip_grad_norm_(\n", + " [p for p in self.net.parameters() if p.requires_grad],\n", + " max_norm=1.0,\n", + " )\n", + " self.optimizer.step()\n", + "\n", + " return float(loss.item())" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "zH13fDb8F398" + }, + "outputs": [], + "source": [ + "# ===== face/signals.py =====\n", + "\"\"\"\n", + "Signals (losses and metrics) for face recognition training.\n", + "\n", + "Follows the WeightsLAB pattern: every helper calls wl.save_signals\n", + "immediately after computing the value so the platform captures it.\n", + "\n", + "Classes\n", + "-------\n", + "TripletLosses differentiable loss functions (return torch.Tensor)\n", + "FaceMetrics evaluation metrics and clustering-oriented test signals\n", + "\"\"\"\n", + "\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn as nn\n", + "\n", + "import weightslab as wl\n", + "\n", + "from typing import Dict\n", + "\n", + "\n", + "\n", + "# ============================================================\n", + "# Loss functions\n", + "# ============================================================\n", + "\n", + "class TripletLosses:\n", + " \"\"\"Differentiable loss functions for face embedding training.\"\"\"\n", + "\n", + " @staticmethod\n", + " def triplet_loss(\n", + " ids,\n", + " anchor_emb: torch.Tensor,\n", + " pos_emb: torch.Tensor,\n", + " neg_emb: torch.Tensor,\n", + " margin: float = 0.3,\n", + " name: str = \"train\",\n", + " ) -> torch.Tensor:\n", + " \"\"\"Standard triplet margin loss on pre-mined (a, p, n) embeddings.\"\"\"\n", + " loss = nn.TripletMarginLoss(margin=margin, p=2)(anchor_emb, pos_emb, neg_emb)\n", + " wl.save_signals(\n", + " batch_ids=ids,\n", + " signals={f\"{name}/loss/triplet\": loss.detach().cpu().item()},\n", + " preds_raw=anchor_emb,\n", + " targets=neg_emb,\n", + " preds=None,\n", + " log=True,\n", + " )\n", + " return loss\n", + "\n", + " @staticmethod\n", + " def contrastive_loss(\n", + " ids,\n", + " emb_a: torch.Tensor,\n", + " emb_b: torch.Tensor,\n", + " same_label: torch.Tensor,\n", + " margin: float = 1.0,\n", + " name: str = \"train\",\n", + " ) -> torch.Tensor:\n", + " \"\"\"Contrastive loss (Hadsell et al., 2006).\"\"\"\n", + " dist = nn.functional.pairwise_distance(emb_a, emb_b)\n", + " loss = (\n", + " same_label * dist.pow(2)\n", + " + (1.0 - same_label) * (margin - dist).clamp(min=0.0).pow(2)\n", + " ).mean()\n", + " wl.save_signals(\n", + " batch_ids=ids,\n", + " signals={f\"{name}/loss/contrastive\": loss.detach().cpu().item()},\n", + " preds_raw=emb_a,\n", + " targets=emb_b,\n", + " preds=None,\n", + " log=True,\n", + " )\n", + " return loss\n", + "\n", + "\n", + "# ============================================================\n", + "# Evaluation metrics\n", + "# ============================================================\n", + "\n", + "class FaceMetrics:\n", + " \"\"\"Evaluation metrics for face verification, retrieval, and grouping.\"\"\"\n", + "\n", + " @staticmethod\n", + " def verification_accuracy(\n", + " ids,\n", + " embeddings: np.ndarray,\n", + " labels: np.ndarray,\n", + " name: str = \"test\",\n", + " ) -> Dict[str, float]:\n", + " \"\"\"Pairwise verification accuracy with optimal threshold search.\"\"\"\n", + " result = compute_verification_metrics(embeddings, labels)\n", + " wl.save_signals(\n", + " batch_ids=ids,\n", + " signals={\n", + " f\"{name}/metric/verification_accuracy\": result[\"verification_accuracy\"],\n", + " f\"{name}/metric/far\": result[\"far\"],\n", + " f\"{name}/metric/frr\": result[\"frr\"],\n", + " f\"{name}/metric/best_threshold\": result[\"best_threshold\"],\n", + " },\n", + " preds_raw=None,\n", + " targets=None,\n", + " preds=None,\n", + " log=True,\n", + " )\n", + " return result\n", + "\n", + " @staticmethod\n", + " def rank1_accuracy(\n", + " ids,\n", + " embeddings: np.ndarray,\n", + " labels: np.ndarray,\n", + " name: str = \"test\",\n", + " ) -> float:\n", + " \"\"\"1-NN Rank-1 retrieval accuracy (leave-one-out).\"\"\"\n", + " acc = compute_rank1_accuracy(embeddings, labels)\n", + " wl.save_signals(\n", + " batch_ids=ids,\n", + " signals={f\"{name}/metric/rank1_accuracy\": acc},\n", + " preds_raw=None,\n", + " targets=None,\n", + " preds=None,\n", + " log=True,\n", + " )\n", + " return acc\n", + "\n", + " @staticmethod\n", + " def similarity_grouping_signals(\n", + " ids,\n", + " embeddings: np.ndarray,\n", + " name: str = \"test\",\n", + " cluster_eps: float = 0.6,\n", + " cluster_min_samples: int = 2,\n", + " ) -> Dict[str, float]:\n", + " \"\"\"Save per-sample grouping signals for later clustering/sorting in Studio.\"\"\"\n", + " grouping = compute_similarity_grouping(\n", + " embeddings=embeddings,\n", + " uids=list(ids),\n", + " cluster_eps=cluster_eps,\n", + " cluster_min_samples=cluster_min_samples,\n", + " )\n", + "\n", + " wl.save_signals(\n", + " batch_ids=ids,\n", + " signals={\n", + " f\"{name}/cluster/id\": grouping[\"cluster_id\"],\n", + " f\"{name}/cluster/size\": grouping[\"cluster_size\"],\n", + " f\"{name}/cluster/nn1_distance\": grouping[\"nn1_distance\"],\n", + " f\"{name}/cluster/nn1_uid\": grouping[\"nn1_uid\"],\n", + " },\n", + " preds_raw=None,\n", + " targets=None,\n", + " preds=None,\n", + " log=False,\n", + " )\n", + "\n", + " summary = {\n", + " \"num_clusters\": grouping[\"num_clusters\"],\n", + " \"noise_ratio\": grouping[\"noise_ratio\"],\n", + " \"mean_nn1_distance\": grouping[\"mean_nn1_distance\"],\n", + " }\n", + " wl.save_signals(\n", + " batch_ids=ids,\n", + " signals={\n", + " f\"{name}/cluster/num_clusters\": summary[\"num_clusters\"],\n", + " f\"{name}/cluster/noise_ratio\": summary[\"noise_ratio\"],\n", + " f\"{name}/cluster/mean_nn1_distance\": summary[\"mean_nn1_distance\"],\n", + " },\n", + " preds_raw=None,\n", + " targets=None,\n", + " preds=None,\n", + " log=True,\n", + " )\n", + " return summary\n", + "\n", + " @staticmethod\n", + " def compute_all_metrics(\n", + " ids,\n", + " embeddings: np.ndarray,\n", + " labels: np.ndarray,\n", + " name: str = \"test\",\n", + " ) -> Dict[str, float]:\n", + " \"\"\"Run all metrics and return a merged dict.\"\"\"\n", + " verif = FaceMetrics.verification_accuracy(ids, embeddings, labels, name=name)\n", + " rank1 = FaceMetrics.rank1_accuracy(ids, embeddings, labels, name=name)\n", + " grouping = FaceMetrics.similarity_grouping_signals(ids, embeddings, name=name)\n", + " return {**verif, \"rank1_accuracy\": rank1, **grouping}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IiE3R5OrF398" + }, + "source": [ + "## 3. Configuration\n", + "\n", + "Every tunable lives in one `config` dict (the inlined `config.yaml`). Wrapping it with\n", + "`flag=\"hyperparameters\"` lets the Studio UI read and live-edit these values while training." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "7f3X3gveF398", + "outputId": "ccbe3f77-aefc-4d7d-d4be-4eea01f86dc9", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:47:42.439 INFO:weightslab.src:watch_or_edit: LoggerQueue for experiment history has been initialized and registered.\n", + "16/07/2026-08:47:42.442 INFO:weightslab.components.checkpoint_manager:__init__: CheckpointManager initialized at /tmp/tmpu_lry8gs\n", + "16/07/2026-08:47:42.443 INFO:weightslab.src:watch_or_edit: Registered new checkpoint manager in ledger\n", + "16/07/2026-08:47:42.444 INFO:root:set_log_directory: Log file moved from /tmp/tmpmfs9yakr/weightslab_logs/weightslab_20260716_084742.log to /tmp/tmpu_lry8gs/weightslab_20260716_084742.log\n", + "16/07/2026-08:47:42.445 INFO:root:set_log_directory: Log directory updated to: /tmp/tmpu_lry8gs\n", + "16/07/2026-08:47:42.446 INFO:root:set_log_directory: Log file: /tmp/tmpu_lry8gs/weightslab_20260716_084742.log\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'experiment_name': 'face_triplet_training',\n", + " 'device': 'auto',\n", + " 'eval_full_to_train_steps_ratio': ValueProxy(key='eval_full_to_train_steps_ratio', value=50),\n", + " 'experiment_dump_to_train_steps_ratio': ValueProxy(key='experiment_dump_to_train_steps_ratio', value=10),\n", + " 'is_training': ValueProxy(key='is_training', value=True),\n", + " 'log_every': ValueProxy(key='log_every', value=50),\n", + " 'max_steps': ValueProxy(key='max_steps', value=1500),\n", + " 'enable_h5_persistence': ValueProxy(key='enable_h5_persistence', value=True),\n", + " 'serving_grpc': ValueProxy(key='serving_grpc', value=True),\n", + " 'serving_cli': ValueProxy(key='serving_cli', value=True),\n", + " 'data': {'dataset_type': 'olivetti',\n", + " 'root_data_dir': '',\n", + " 'image_size': 64,\n", + " 'train_ratio': 0.8,\n", + " 'min_images_per_class': 2,\n", + " 'train_loader': {'batch_size': 32, 'shuffle': True, 'n_workers': 0},\n", + " 'test_loader': {'batch_size': 64, 'shuffle': False, 'n_workers': 0}},\n", + " 'model': {'backbone': 'resnet18',\n", + " 'pretrained': True,\n", + " 'freeze_backbone': True,\n", + " 'embedding_dim': 128,\n", + " 'head_hidden_dim': 256,\n", + " 'lr': 0.001,\n", + " 'weight_decay': 0.0001,\n", + " 'loss': 'triplet',\n", + " 'margin': 0.3},\n", + " 'root_log_dir': '/tmp/tmpu_lry8gs'}" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ], + "source": [ + "# ============================================================\n", + "# Face Recognition - Training Config (inlined from config.yaml)\n", + "# ============================================================\n", + "config = {\n", + " \"experiment_name\": \"face_triplet_training\",\n", + " \"device\": \"auto\", # \"auto\" -> cuda if available, else cpu\n", + "\n", + " # Training schedule\n", + " \"eval_full_to_train_steps_ratio\": 50, # evaluation frequency (steps); -1 disables\n", + " \"experiment_dump_to_train_steps_ratio\": 10, # print running loss every N steps\n", + " \"is_training\": True, # start training immediately or not\n", + " \"log_every\": 50, # running-loss print cadence\n", + " \"max_steps\": 1500, # Colab cap so the training cell terminates;\n", + " # set to None to train open-ended (interrupt to stop)\n", + "\n", + " # WeightsLAB persistence\n", + " # \"root_log_dir\": leave unset -> auto temp dir\n", + " \"enable_h5_persistence\": True,\n", + "\n", + " # WeightsLAB serving\n", + " \"serving_grpc\": True,\n", + " \"serving_cli\": True,\n", + "\n", + " # --------------------------------------------------------\n", + " # Data\n", + " # --------------------------------------------------------\n", + " \"data\": {\n", + " # dataset_type options:\n", + " # \"olivetti\" - sklearn Olivetti Faces (40 ids, 400 imgs, 64x64 grey).\n", + " # Works offline, no download needed. Ideal for quick tests.\n", + " # \"lfw\" - LFW People via torchvision (download on first run).\n", + " # \"folder\" - Generic ImageFolder layout: root/{train,test}/{class}/*.jpg\n", + " \"dataset_type\": \"olivetti\",\n", + " \"root_data_dir\": \"\", # only required for lfw / folder\n", + " \"image_size\": 64, # images are resized to image_size x image_size\n", + " \"train_ratio\": 0.8, # olivetti only: fraction used for training\n", + " \"min_images_per_class\": 2, # identities with fewer samples are discarded\n", + "\n", + " \"train_loader\": {\n", + " \"batch_size\": 32,\n", + " \"shuffle\": True,\n", + " \"n_workers\": 0, # >0 for multi-process loading (not needed for Olivetti)\n", + " },\n", + " \"test_loader\": {\n", + " \"batch_size\": 64,\n", + " \"shuffle\": False,\n", + " \"n_workers\": 0,\n", + " },\n", + " },\n", + "\n", + " # --------------------------------------------------------\n", + " # Model\n", + " # --------------------------------------------------------\n", + " \"model\": {\n", + " # backbone: \"resnet18\" | \"resnet50\" | \"mobilenet_v3_small\"\n", + " \"backbone\": \"resnet18\",\n", + " \"pretrained\": True, # load ImageNet-pretrained weights\n", + " \"freeze_backbone\": True, # train the embedding head only (fast & effective)\n", + "\n", + " # Embedding head\n", + " \"embedding_dim\": 128, # output dim of the L2-normalised vector\n", + " \"head_hidden_dim\": 256, # hidden dim of the projection MLP\n", + "\n", + " # Optimiser (AdamW, trainable params only)\n", + " \"lr\": 0.001,\n", + " \"weight_decay\": 0.0001,\n", + "\n", + " # Triplet loss\n", + " \"loss\": \"triplet\",\n", + " \"margin\": 0.3, # triplet margin alpha\n", + " },\n", + "}\n", + "\n", + "wl.watch_or_edit(config, flag=\"hyperparameters\", defaults=config, poll_interval=1.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rHGGFVtgF399" + }, + "source": [ + "## 4. Datasets and tracked loaders\n", + "\n", + "Build the **Olivetti Faces** train/test splits and wrap the loaders with\n", + "`wl.watch_or_edit(..., flag=\"data\")` so every sample keeps its identity in WeightsLab.\n", + "Olivetti downloads automatically through scikit-learn (cached under `~/scikit_learn_data`)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "91s0NMtEF399", + "outputId": "07e8d642-4be4-48bb-86cc-aebfd17171f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "downloading Olivetti faces from https://ndownloader.figshare.com/files/5976027 to /root/scikit_learn_data\n", + "16/07/2026-08:47:46.760 INFO:__main__:__init__: FaceDataset [olivetti / train] | samples=320 | classes=40\n", + "16/07/2026-08:47:46.867 INFO:__main__:__init__: FaceDataset [olivetti / test] | samples=80 | classes=40\n", + "\n", + "Dataset : olivetti | train=320 | test=80 | classes=40\n", + "16/07/2026-08:47:46.880 INFO:weightslab.data.data_samples_with_ops:__init__: [DataSampleTrackingWrapper] H5 persistence enabled at /tmp/tmpu_lry8gs/checkpoints/data/data.h5\n", + "16/07/2026-08:47:46.893 INFO:weightslab.data.data_samples_with_ops:__init__: Preloading sample statistics for PHYSICAL indices in split 'train_loader' with: preload_labels=True, preload_metadata=True...\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Initializing ledger for split 'train_loader': 0%| | 0/320 [00:00 the runtime device detected above).\n", + "if config.get(\"device\", \"auto\") == \"auto\":\n", + " config[\"device\"] = str(device)\n", + "\n", + "# Log dir (auto temp dir if unset).\n", + "if not config.get(\"root_log_dir\"):\n", + " config[\"root_log_dir\"] = tempfile.mkdtemp()\n", + " print(f\"No root_log_dir specified - using temp dir: {config['root_log_dir']}\")\n", + "os.makedirs(config[\"root_log_dir\"], exist_ok=True)\n", + "\n", + "eval_full_to_train_steps_ratio = int(config.get(\"eval_full_to_train_steps_ratio\", 50))\n", + "log_every = int(config.get(\"log_every\", 10))\n", + "enable_h5 = config.get(\"enable_h5_persistence\", True)\n", + "\n", + "# ---- Datasets ----\n", + "data_cfg = config.get(\"data\", {})\n", + "dataset_type = data_cfg.get(\"dataset_type\", \"olivetti\")\n", + "root_data = data_cfg.get(\"root_data_dir\", config.get(\"root_log_dir\", \".\"))\n", + "image_size = int(data_cfg.get(\"image_size\", 64))\n", + "min_imgs = int(data_cfg.get(\"min_images_per_class\", 2))\n", + "train_ratio = float(data_cfg.get(\"train_ratio\", 0.8))\n", + "\n", + "train_dataset = FaceDataset(\n", + " root=root_data,\n", + " dataset_type=dataset_type,\n", + " split=\"train\",\n", + " image_size=image_size,\n", + " train_ratio=train_ratio,\n", + " min_images_per_class=min_imgs,\n", + ")\n", + "test_dataset = FaceDataset(\n", + " root=root_data,\n", + " dataset_type=dataset_type,\n", + " split=\"test\",\n", + " image_size=image_size,\n", + " train_ratio=train_ratio,\n", + " min_images_per_class=min_imgs,\n", + ")\n", + "\n", + "print(\n", + " f\"\\nDataset : {dataset_type}\"\n", + " f\" | train={len(train_dataset)}\"\n", + " f\" | test={len(test_dataset)}\"\n", + " f\" | classes={train_dataset.num_classes}\"\n", + ")\n", + "\n", + "train_cfg = data_cfg.get(\"train_loader\", {})\n", + "test_cfg = data_cfg.get(\"test_loader\", {})\n", + "\n", + "# ---- WeightsLab-tracked data loaders ----\n", + "train_loader = wl.watch_or_edit(\n", + " train_dataset,\n", + " flag=\"data\",\n", + " loader_name=\"train_loader\",\n", + " batch_size=int(train_cfg.get(\"batch_size\", 32)),\n", + " shuffle=train_cfg.get(\"shuffle\", True),\n", + " is_training=True,\n", + " compute_hash=False,\n", + " num_workers=int(train_cfg.get(\"n_workers\", 0)),\n", + " enable_h5_persistence=enable_h5,\n", + ")\n", + "test_loader = wl.watch_or_edit(\n", + " test_dataset,\n", + " flag=\"data\",\n", + " loader_name=\"test_loader\",\n", + " batch_size=int(test_cfg.get(\"batch_size\", 64)),\n", + " shuffle=test_cfg.get(\"shuffle\", False),\n", + " is_training=False,\n", + " compute_hash=False,\n", + " num_workers=int(test_cfg.get(\"n_workers\", 0)),\n", + " enable_h5_persistence=enable_h5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5JWo3BxFF399" + }, + "source": [ + "## 5. Model\n", + "\n", + "A pretrained ResNet-18 backbone (frozen) plus a small L2-normalised embedding head, trained\n", + "with online batch-hard **triplet loss**. Constructing it registers the graph and the loss\n", + "with WeightsLab." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "2J48fm6sF399", + "outputId": "2970e5b8-1f7c-4dc4-ffd1-f8c7f50c5444", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading: \"https://download.pytorch.org/models/resnet18-f37072fd.pth\" to /root/.cache/torch/hub/checkpoints/resnet18-f37072fd.pth\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 44.7M/44.7M [00:00<00:00, 77.5MB/s]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:47:49.494 INFO:__main__:_build_backbone: Backbone frozen ? training head only.\n", + "16/07/2026-08:47:49.505 INFO:weightslab.backend.model_interface:__init__: Using checkpoint manager from ledger\n", + "16/07/2026-08:47:49.509 INFO:__main__:__init__: FaceEmbeddingModel | backbone=resnet18 pretrained=True frozen=True | emb_dim=128 | trainable_params=164,736\n", + " Backbone : resnet18 (pretrained=True, frozen=True)\n", + " Emb dim : 128\n", + " Head dim : 256\n", + " Trainable : 164,736 params\n", + " Device : cpu\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.12/dist-packages/torch/nn/_reduction.py:51: UserWarning: size_average and reduce args will be deprecated, please use reduction='none' instead.\n", + " warnings.warn(warning.format(ret))\n" + ] + } + ], + "source": [ + "# ---- Model ----\n", + "model_cfg = config.get(\"model\", {})\n", + "model = FaceEmbeddingModel(\n", + " backbone_name=model_cfg.get(\"backbone\", \"resnet18\"),\n", + " embedding_dim=int(model_cfg.get(\"embedding_dim\", 128)),\n", + " head_hidden_dim=int(model_cfg.get(\"head_hidden_dim\", 256)),\n", + " lr=float(model_cfg.get(\"lr\", 1e-3)),\n", + " weight_decay=float(model_cfg.get(\"weight_decay\", 1e-4)),\n", + " freeze_backbone=model_cfg.get(\"freeze_backbone\", True),\n", + " pretrained=model_cfg.get(\"pretrained\", True),\n", + " margin=float(model_cfg.get(\"margin\", 0.3)),\n", + " device=config[\"device\"],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EmKUHOgJF399" + }, + "source": [ + "## 6. Train and evaluate steps\n", + "\n", + "`evaluate(...)` collects embeddings over a loader and computes verification / retrieval /\n", + "clustering signals; `train(...)` runs the batch-hard triplet loop. The\n", + "`guard_training_context` / `guard_testing_context` blocks tag each phase. The loop is capped\n", + "by `max_steps` so it terminates on Colab (pass `max_steps=None` to run open-ended)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "1Cu9mwxEF399" + }, + "outputs": [], + "source": [ + "def evaluate(\n", + " model: FaceEmbeddingModel,\n", + " loader: torch.utils.data.DataLoader,\n", + " name: str = \"test\",\n", + ") -> Dict[str, float]:\n", + " \"\"\"Collect embeddings from loader and compute face recognition metrics.\"\"\"\n", + " print(f\"\\n{'=' * 55}\")\n", + " print(f\"Evaluation [{name}]\")\n", + " print(f\"{'=' * 55}\")\n", + "\n", + " all_embeddings: List[np.ndarray] = []\n", + " all_labels: List[np.ndarray] = []\n", + " all_uids: List[str] = []\n", + "\n", + " for images, uids, labels, _metadata in loader:\n", + " emb = model.get_embeddings(images) # (B, D)\n", + " all_embeddings.append(emb.numpy())\n", + " if isinstance(labels, torch.Tensor):\n", + " all_labels.append(labels.numpy())\n", + " else:\n", + " all_labels.append(np.array(labels))\n", + " all_uids.extend(uids if isinstance(uids, list) else list(uids))\n", + "\n", + " all_embeddings = np.concatenate(all_embeddings, axis=0)\n", + " all_labels = np.concatenate(all_labels, axis=0)\n", + "\n", + " metrics = FaceMetrics.compute_all_metrics(\n", + " ids=all_uids,\n", + " embeddings=all_embeddings,\n", + " labels=all_labels,\n", + " name=name,\n", + " )\n", + "\n", + " print(f\" verification_accuracy : {metrics.get('verification_accuracy', float('nan')):.4f}\")\n", + " print(f\" rank1_accuracy : {metrics.get('rank1_accuracy', float('nan')):.4f}\")\n", + " print(f\" FAR : {metrics.get('far', float('nan')):.4f}\")\n", + " print(f\" FRR : {metrics.get('frr', float('nan')):.4f}\")\n", + " print(f\" best_threshold : {metrics.get('best_threshold', float('nan')):.4f}\")\n", + " if \"num_clusters\" in metrics:\n", + " print(f\" num_clusters : {metrics['num_clusters']:.0f}\")\n", + " print(f\" noise_ratio : {metrics['noise_ratio']:.4f}\")\n", + " print(f\" mean_nn1_distance : {metrics['mean_nn1_distance']:.4f}\")\n", + "\n", + " return metrics\n", + "\n", + "\n", + "def train(\n", + " model: FaceEmbeddingModel,\n", + " train_loader: torch.utils.data.DataLoader,\n", + " test_loader: torch.utils.data.DataLoader,\n", + " eval_full_to_train_steps_ratio: int = 50,\n", + " log_every: int = 10,\n", + " loss_name: str = \"triplet\",\n", + " max_steps: int = None,\n", + ") -> Dict[str, Any]:\n", + " \"\"\"Training loop.\n", + "\n", + " Runs until `max_steps` optimizer steps have been taken (Colab-friendly), or\n", + " indefinitely when `max_steps` is None (interrupt the cell to stop). Periodic test\n", + " evaluation runs every eval_full_to_train_steps_ratio steps when test_loader is provided.\n", + " \"\"\"\n", + " print(\"\\n\" + \"=\" * 60)\n", + " print(\"Face Recognition Training\")\n", + " print(f\" Loss : {loss_name}\")\n", + " print(f\" Eval every : {eval_full_to_train_steps_ratio} steps\")\n", + " print(f\" Max steps : {'infinite (stop with Ctrl+C)' if max_steps is None else max_steps}\")\n", + " print(\"=\" * 60)\n", + "\n", + " data_iter = iter(train_loader)\n", + " losses: List[float] = []\n", + " eval_history: List[Dict[str, Any]] = []\n", + " step = 0\n", + "\n", + " try:\n", + " while max_steps is None or step < max_steps:\n", + " step += 1\n", + "\n", + " with guard_training_context:\n", + " # ---- Fetch next batch (cycle loader) ----\n", + " try:\n", + " images, batch_ids, labels, _metadata = next(data_iter)\n", + " except StopIteration:\n", + " data_iter = iter(train_loader)\n", + " images, batch_ids, labels, _metadata = next(data_iter)\n", + "\n", + " if not isinstance(batch_ids, list):\n", + " batch_ids = list(batch_ids)\n", + "\n", + " if isinstance(labels, torch.Tensor):\n", + " labels_tensor = labels.long()\n", + " else:\n", + " labels_tensor = torch.tensor(labels, dtype=torch.long)\n", + "\n", + " # ---- Gradient step ----\n", + " loss_val = model.train_step(\n", + " images=images,\n", + " labels=labels_tensor,\n", + " batch_ids=batch_ids,\n", + " loss_name=loss_name,\n", + " )\n", + " losses.append(loss_val)\n", + "\n", + " if step == 1 or step % max(1, log_every) == 0:\n", + " window = losses[-log_every:] if len(losses) >= log_every else losses\n", + " print(\n", + " f\"[train] step {step:>7d} \"\n", + " f\"| loss={loss_val:.6f} \"\n", + " f\"| running_mean={np.mean(window):.6f}\"\n", + " )\n", + "\n", + " should_eval = (\n", + " test_loader is not None\n", + " and (step % eval_full_to_train_steps_ratio == 0)\n", + " )\n", + "\n", + " if should_eval:\n", + " with guard_testing_context:\n", + " print(f\"\\n[eval@test] step {step}\")\n", + " metrics = evaluate(model=model, loader=test_loader, name=\"test\")\n", + " eval_history.append({\"step\": step, \"metrics\": metrics})\n", + "\n", + " except KeyboardInterrupt:\n", + " print(\"\\nInterrupted by user. Stopping training loop.\")\n", + "\n", + " summary = {\n", + " \"train_steps\": step,\n", + " \"train_loss_mean\": float(np.mean(losses)) if losses else float(\"nan\"),\n", + " \"train_loss_last\": float(losses[-1]) if losses else float(\"nan\"),\n", + " \"num_evals\": len(eval_history),\n", + " }\n", + "\n", + " print(\"\\nTraining summary:\")\n", + " for k, v in summary.items():\n", + " print(f\" {k}: {v}\")\n", + "\n", + " return summary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wBOmcQBgF39-" + }, + "source": [ + "## 7. Serve and train\n", + "\n", + "`wl.serve(serving_grpc=True, serving_bore=True)` starts the background gRPC server and a\n", + "public `bore.pub` tunnel (the endpoint is printed below). `wl.start_training(...)` flips the\n", + "experiment into the training state, then the finite loop runs while streaming per-sample\n", + "signals. Leave this cell **running** while you watch it in the UI." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "N_88-lktF39-", + "outputId": "e736988d-2a95-4041-e9e5-414ec2861619", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:47:49.574 INFO:weightslab.trainer.trainer_services:_run_security_preflight: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "[gRPC] Security preflight checks:\n", + "\tTLS: DISABLED (unencrypted traffic)\n", + "\tAuth tokens: NONE configured\n", + "\t! WARNING: GRPC_TLS_ENABLED=0. Traffic will be unencrypted. Use only for development.\n", + "\t! WARNING: No GRPC_AUTH_TOKEN/GRPC_AUTH_TOKENS configured. Only transport-level trust (TLS/mTLS) will protect RPC access.\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "16/07/2026-08:47:49.576 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Watchdogs disabled via WEIGHTSLAB_DISABLE_WATCHDOGS.\n", + "16/07/2026-08:47:49.577 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Thread callback started\n", + "16/07/2026-08:47:49.580 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Creating ThreadPoolExecutor with 6 worker threads (n_workers_grpc=None, max_concurrent_rpcs=None)\n", + "16/07/2026-08:47:49.578 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Server started with watchdogs disabled (host=0.0.0.0 port=50051 workers=None)\n", + "16/07/2026-08:47:49.585 INFO:weightslab.tunnel:_ensure_bore: Downloading bore v0.6.0 (bore-v0.6.0-x86_64-unknown-linux-musl.tar.gz)...\n", + "16/07/2026-08:47:49.729 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Server object created\n", + "16/07/2026-08:47:49.788 INFO:weightslab.trainer.services.agent.agent:__init__: Initializing DataManipulationAgent\n", + "16/07/2026-08:47:49.841 WARNING:weightslab.trainer.services.agent.agent:_load_config: Error loading config from /usr/local/lib/python3.12/dist-packages: [Errno 21] Is a directory: '/usr/local/lib/python3.12/dist-packages'\n", + "16/07/2026-08:47:49.853 INFO:weightslab.trainer.services.agent.agent:_load_config: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "Agent initialized from configuration /content/agent_config.yaml: \n", + "\tFinal Agent Configuration: Preferred Provider=openrouter, \n", + "\tFallback to Local=True, \n", + "\tOpenRouter Model=~google/gemini-flash-latest with:\n", + "\t\tAPI Key=None\n", + "\t\tBase URL=https://openrouter.ai/api/v1, \n", + "\tOllama Model=llama3.2:3b\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "16/07/2026-08:47:49.860 INFO:weightslab.trainer.services.agent.agent:_setup_providers: Setting up Ollama with model llama3.2:3b\n", + "16/07/2026-08:47:50.171 INFO:weightslab.trainer.services.agent.agent:_setup_providers: [Agent] Ollama enabled: llama3.2:3b\n", + "16/07/2026-08:47:50.173 INFO:weightslab.trainer.services.data_service:_build_preview_cache: [PreviewCache] Building 64×64 or less or less preview cache for 400 samples …\n", + "16/07/2026-08:47:50.173 INFO:weightslab.trainer.services.data_service:__init__: DataService initialized.\n", + "16/07/2026-08:47:50.175 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Servicer added\n", + "16/07/2026-08:47:50.176 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Attempting to bind to 0.0.0.0:50051\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\r[PreviewCache]: 0%| | 0/400 [00:00` printed in the Expose section:\n", + "\n", + "```bash\n", + "pip install weightslab\n", + "weightslab ui launch # Terminal 1 - opens http://localhost:5173\n", + "weightslab tunnel bore.pub:12345 # Terminal 2 - the host:port from the Expose section\n", + "```\n", + "\n", + "Then open **http://localhost:5173**. Prefer local-only? Run this example directly on your machine\n", + "(`weightslab start example` selects a bundled example) and launch the UI next to it - no tunnel." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EW46PivEF39-" + }, + "source": [ + "---\n", + "\n", + "
\n", + "Crafted by GrayboxTech - if WeightsLab helps you catch a bad label, drop us a star on GitHub.\n", + "
" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "ws-clustering.ipynb", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Face Recognition (triplet loss) with WeightsLab\n", - "\n", - "Trains a ResNet-18 embedding head with online batch-hard **triplet loss** and tracks verification / retrieval / similarity signals per sample.\n", - "\n", - "> Data: Defaults to the **Olivetti Faces** dataset (via scikit-learn) which works **offline** - no download needed. Switch to LFW or a folder dataset in the config.\n", - "\n", - "Unlike the self-contained classification demo, this example uses local helper modules\n", - "(`utils/`) and bundled/downloaded data, so the notebook **clones the repo** and runs the\n", - "example in place." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "On Colab, pick a **T4 GPU** runtime (`Runtime -> Change runtime type -> T4 GPU`)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\"PyPI\n", - "\"PyPI\n", - "\"PyPI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Clone the repo (for the example's utils/ + data) and install WeightsLab.\n", - "import os\n", - "if not os.path.isdir(\"weightslab\"):\n", - " !git clone --branch torch_collab_examples https://github.com/GrayboxTech/weightslab.git\n", - "%pip install -q ./weightslab\n", - "!pip install --upgrade \"protobuf>=6.31.1\"\n", - "%pip install \"torchvision>=0.16\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Move into the example directory so its `utils/` and config.yaml resolve.\n", - "%cd weightslab/weightslab/examples/PyTorch/ws-clustering\n", - "# Install the example's own extra requirements, if any.\n", - "import os\n", - "if os.path.exists(\"requirements.txt\"):\n", - " !pip install -q -r requirements.txt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration\n", - "\n", - "Every tunable lives in **`config.yaml`** (already commented). We load it into a `config` dict\n", - "so you can tweak values here; the changes are written back before training." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml\n", - "\n", - "with open(\"config.yaml\") as f:\n", - " config = yaml.safe_load(f)\n", - "\n", - "# --- tweak any parameter here, e.g. ---\n", - "# config[\"eval_full_to_train_steps_ratio\"] = 5\n", - "config[\"serving_grpc\"] = True # expose the gRPC backend for Weights Studio\n", - "\n", - "with open(\"config.yaml\", \"w\") as f:\n", - " yaml.safe_dump(config, f, sort_keys=False)\n", - "\n", - "print(yaml.safe_dump(config, sort_keys=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## (Optional) Expose the backend for the live UI\n", - "\n", - "To watch training **live in Weights Studio on your own machine**, run this cell first (it opens a\n", - "raw-TCP [`bore`](https://github.com/ekzhang/bore) tunnel over the free public relay `bore.pub` -\n", - "**no signup, no card**), then start training below.\n", - "\n", - "> `bore.pub` is a shared public relay; the random port is the only thing protecting your endpoint.\n", - "> Fine for a demo, not for sensitive data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os, re, tarfile, threading, urllib.request, subprocess\n", - "\n", - "EXPOSE_UI = True # set False for a headless run (no tunnel)\n", - "BACKEND_PORT = 50051 # gRPC port to forward\n", - "\n", - "endpoint = None\n", - "if EXPOSE_UI:\n", - " bore = os.path.join(os.getcwd(), \"bore\")\n", - " if not os.path.exists(bore):\n", - " BORE = \"v0.6.0\"\n", - " urllib.request.urlretrieve(\n", - " f\"https://github.com/ekzhang/bore/releases/download/{BORE}/bore-{BORE}-x86_64-unknown-linux-musl.tar.gz\",\n", - " \"bore.tar.gz\")\n", - " with tarfile.open(\"bore.tar.gz\") as t:\n", - " t.extractall()\n", - " os.chmod(bore, 0o755)\n", - "\n", - " proc = subprocess.Popen(\n", - " [bore, \"local\", str(BACKEND_PORT), \"--to\", \"bore.pub\"],\n", - " stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)\n", - " for line in proc.stdout:\n", - " m = re.search(r\"bore\\.pub:(\\d+)\", line)\n", - " if m:\n", - " endpoint = f\"bore.pub:{m.group(1)}\"\n", - " break\n", - " threading.Thread(target=lambda: [None for _ in proc.stdout], daemon=True).start()\n", - "\n", - " print(\"=\" * 60)\n", - " print(\"Backend exposed at:\", endpoint)\n", - " print(\"On your OWN machine (Docker running), run:\")\n", - " print(\" weightslab ui launch\")\n", - " print(f\" weightslab tunnel {endpoint}\")\n", - " print(\"=\" * 60)\n", - "else:\n", - " print(\"EXPOSE_UI is False - no tunnel.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Train\n", - "\n", - "This runs the example's `main.py`, which wraps the model / optimizer / loaders / losses with\n", - "`wl.watch_or_edit(...)`, starts the gRPC backend, and trains while streaming per-sample signals.\n", - "\n", - "> This example trains **open-ended** (until you interrupt the cell). Interrupt the cell (stop button) to stop training; the exported history and\n", - "> dataframe are written to a temp `root_log_dir` (printed at startup)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python main.py" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## See it live in Weights Studio\n", - "\n", - "**Colab has no Docker daemon**, so run Studio on your own machine and point it at this notebook's\n", - "backend using the `bore.pub:` printed in the Expose section:\n", - "\n", - "```bash\n", - "pip install weightslab\n", - "weightslab ui launch # Terminal 1 - opens http://localhost:5173\n", - "weightslab tunnel bore.pub:12345 # Terminal 2 - the host:port from the Expose section\n", - "```\n", - "\n", - "Then open **http://localhost:5173**. Prefer local-only? Run this example directly on your machine\n", - "(`weightslab start example` selects a bundled example) and launch the UI next to it - no tunnel." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "
\n", - "Crafted by GrayboxTech - if WeightsLab helps you catch a bad label, drop us a star on GitHub.\n", - "
" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "ws-clustering.ipynb", - "provenance": [], - "toc_visible": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 0 + "nbformat": 4, + "nbformat_minor": 0 } \ No newline at end of file diff --git a/weightslab/examples/Notebooks/PyTorch/ws-detection.ipynb b/weightslab/examples/Notebooks/PyTorch/ws-detection.ipynb index 69c72d74..d4f26db2 100644 --- a/weightslab/examples/Notebooks/PyTorch/ws-detection.ipynb +++ b/weightslab/examples/Notebooks/PyTorch/ws-detection.ipynb @@ -1,241 +1,4691 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - " \n", - " \"WeightsLab\n", - "\n", - " \"License\"\n", - " \"Stars\"\n", - " \"Version\"\n", - "
\n", - " \"Open\n", - "\n", - " Welcome to the WeightsLab Object Detection (Penn-Fudan) notebook! WeightsLab traces training signals back to the exact samples producing them. Browse the Docs for details.
" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Z2kgyn-s_xBa" + }, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Welcome to the WeightsLab Object Detection (Penn-Fudan) notebook! WeightsLab traces training signals back to the exact samples producing them. Browse the Docs for details.
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "C_WnDPZv_xBc" + }, + "source": [ + "# Object Detection (Penn-Fudan) with WeightsLab\n", + "\n", + "This notebook trains a small detector (MobileNetV3-Small backbone + a lightweight detection head) on the **Penn-Fudan** pedestrian dataset and instruments it with WeightsLab, so every training signal is traced **back to the exact samples** producing it.\n", + "\n", + "WeightsLab records **per-sample** detection loss and **per-instance** IoU (one value per ground-truth box) live, so each predicted box is traceable in Studio - you can rank the worst samples, spot bad boxes, and curate the dataset **without restarting training**.\n", + "\n", + "> Data: the Penn-Fudan dataset (~170 images, one class: *person*) is **downloaded automatically on first run** - nothing to upload.\n", + "\n", + "### What you'll do\n", + "1. Install WeightsLab.\n", + "2. Inline the dataset, detector, and loss/IoU criterions (previously the example's `utils/` package).\n", + "3. Set every knob in one **config** dict (like a `config.yaml`).\n", + "4. Wrap the model, optimizer, dataloaders, loss, and IoU metrics with the SDK.\n", + "5. Train while per-sample and per-instance signals are captured, then open the live **Weights Studio** UI." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dpQhH7Jd_xBc" + }, + "source": [ + "## Setup\n", + "\n", + "On Colab, pick a **T4 GPU** runtime (`Runtime -> Change runtime type -> T4 GPU`)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3nlJ2-Oj_xBc" + }, + "source": [ + "\"PyPI\n", + "\"PyPI\n", + "\"PyPI" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "WptCnGqv_xBd", + "outputId": "617e6869-1797-49dc-96b9-525dfe8e7b35", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Looking in indexes: https://test.pypi.org/simple/, https://pypi.org/simple/\n", + "Requirement already satisfied: weightslab==1.3.3.dev7 in /usr/local/lib/python3.12/dist-packages (1.3.3.dev7)\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev7) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev7) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev7) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev7) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev7) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev7) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev7) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev7) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev7) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev7) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev7) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev7) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev7) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev7) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev7) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev7) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev7) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev7) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev7) (2.0.0)\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install -q weightslab\n", + "%pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev7\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KtEZwMxI_xBd" + }, + "source": [ + "## 1. Imports\n", + "\n", + "`weightslab` is imported as `wl`. The two `guard_*_context` managers scope a block as training vs. evaluation so signals are attributed to the right phase. Every external dependency used by the inlined modules is imported here." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "NHwcL67i_xBd", + "outputId": "336670cc-0cab-4abf-b13f-91f6babe5c14", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:32:40.383 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp_zk4n7mc/weightslab_logs/weightslab_20260716_083240.log\n", + "16/07/2026-08:32:40.387 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-08:32:40.388 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev7 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-08:32:40.394 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n", + "Using device: cpu\n" + ] + } + ], + "source": [ + "import os\n", + "import ssl\n", + "import zipfile\n", + "import logging\n", + "import tempfile\n", + "import urllib.request\n", + "\n", + "import numpy as np\n", + "import tqdm\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "from torch import optim\n", + "from torch.utils.data import Dataset\n", + "from torchvision import transforms\n", + "from torchvision.models import mobilenet_v3_small, MobileNet_V3_Small_Weights\n", + "from PIL import Image\n", + "\n", + "import weightslab as wl\n", + "from weightslab.components.global_monitoring import (\n", + " guard_training_context,\n", + " guard_testing_context,\n", + ")\n", + "\n", + "logging.basicConfig(level=logging.ERROR)\n", + "logging.getLogger(\"PIL\").setLevel(logging.INFO)\n", + "\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "print(f\"Using device: {device}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OOYtb2D2_xBe" + }, + "source": [ + "## 2. Inlined helper modules (from the example's `utils/`)\n", + "\n", + "The example ships a small `utils/` package; its three modules are inlined below so the notebook is fully self-contained. Only the intra-package imports are dropped - those names (`decode_grid`, `det_collate`, the dataset and the criterions) are now notebook-global.\n", + "\n", + "- **`utils/data.py`** - the Penn-Fudan detection dataset (auto-downloads on first use) + the `det_collate` collate fn.\n", + "- **`utils/model.py`** - a MobileNetV3-Small backbone + a small grid detection head.\n", + "- **`utils/criterions.py`** - per-sample YOLO-style detection loss and per-sample / per-instance IoU." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "csaORA30_xBe" + }, + "outputs": [], + "source": [ + "# ===== utils/data.py =====\n", + "import os\n", + "import ssl\n", + "import zipfile\n", + "import urllib.request\n", + "\n", + "import numpy as np\n", + "import torch\n", + "\n", + "from torchvision import transforms\n", + "\n", + "from torch.utils.data import Dataset\n", + "\n", + "from PIL import Image\n", + "\n", + "\n", + "# =============================================================================\n", + "# Penn-Fudan Pedestrian detection dataset\n", + "# =============================================================================\n", + "# A small, real object-detection dataset (~170 photos, one class: \"person\").\n", + "# It ships per-instance segmentation masks; we derive an axis-aligned bounding\n", + "# box per pedestrian from each mask. Downloaded + extracted on first use.\n", + "#\n", + "# On-disk layout after extraction:\n", + "# /PennFudanPed/\n", + "# PNGImages/FudanPed00001.png ...\n", + "# PedMasks/FudanPed00001_mask.png ... # pixel value k = k-th pedestrian, 0 = bg\n", + "#\n", + "# WL renders detection targets/predictions from a per-sample [N, 6] array\n", + "# ``[x1, y1, x2, y2, class_id, confidence]`` normalized to [0, 1] (GT conf = 1.0)\n", + "# — see ``get_items`` below and ``data_service.py`` (task_type == \"detection\").\n", + "\n", + "CLASS_NAMES = [\"person\"]\n", + "\n", + "_URL = \"https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip\"\n", + "\n", + "# ImageNet statistics — the MobileNetV3 backbone is pretrained with these, so we\n", + "# normalize model inputs the same way. (The UI still shows the original PNG.)\n", + "IMAGENET_MEAN = [0.485, 0.456, 0.406]\n", + "IMAGENET_STD = [0.229, 0.224, 0.225]\n", + "\n", + "\n", + "def download_penn_fudan(root):\n", + " \"\"\"Download + extract Penn-Fudan into /PennFudanPed (idempotent).\"\"\"\n", + " base = os.path.join(root, \"PennFudanPed\")\n", + " if os.path.isdir(os.path.join(base, \"PNGImages\")):\n", + " return base\n", + "\n", + " os.makedirs(root, exist_ok=True)\n", + " zip_path = os.path.join(root, \"PennFudanPed.zip\")\n", + "\n", + " if not os.path.exists(zip_path):\n", + " print(f\"[data] Downloading Penn-Fudan dataset to {zip_path} ...\", flush=True)\n", + " try:\n", + " urllib.request.urlretrieve(_URL, zip_path)\n", + " except Exception as e:\n", + " # Some corporate environments break TLS verification - retry unverified.\n", + " print(f\"[data] TLS verification failed ({e}); retrying without verification.\", flush=True)\n", + " ctx = ssl._create_unverified_context()\n", + " with urllib.request.urlopen(_URL, context=ctx) as resp, open(zip_path, \"wb\") as fh:\n", + " fh.write(resp.read())\n", + "\n", + " print(\"[data] Extracting Penn-Fudan ...\", flush=True)\n", + " with zipfile.ZipFile(zip_path) as zf:\n", + " zf.extractall(root)\n", + " return base\n", + "\n", + "\n", + "def _boxes_from_mask(mask_path):\n", + " \"\"\"Derive one bbox per pedestrian from a Penn-Fudan instance mask.\n", + "\n", + " Returns (boxes_px [N, 4] int xyxy, height, width). Background (0) skipped.\n", + " \"\"\"\n", + " mask = np.array(Image.open(mask_path))\n", + " h, w = mask.shape[:2]\n", + " obj_ids = np.unique(mask)\n", + " obj_ids = obj_ids[obj_ids != 0] # drop background\n", + "\n", + " boxes = []\n", + " for oid in obj_ids:\n", + " ys, xs = np.where(mask == oid)\n", + " if xs.size == 0:\n", + " continue\n", + " x1, x2 = int(xs.min()), int(xs.max())\n", + " y1, y2 = int(ys.min()), int(ys.max())\n", + " if x2 > x1 and y2 > y1:\n", + " boxes.append([x1, y1, x2, y2])\n", + " return np.asarray(boxes, dtype=np.float32).reshape(-1, 4), h, w\n", + "\n", + "\n", + "class PennFudanDetectionDataset(Dataset):\n", + " \"\"\"Pedestrian bounding-box detection over the Penn-Fudan images.\n", + "\n", + " Args:\n", + " root: directory to download/extract the dataset into.\n", + " split: \"train\" or \"val\" (deterministic split of the 170 images).\n", + " image_size: square resize fed to the model.\n", + " val_fraction: fraction of images held out for validation.\n", + " max_samples: optional cap on the split size (for quick runs).\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " root,\n", + " split=\"train\",\n", + " num_classes=1,\n", + " image_size=256,\n", + " val_fraction=0.2,\n", + " max_samples=None,\n", + " ):\n", + " super().__init__()\n", + " self.root = root\n", + " self.split = split\n", + " self.num_classes = num_classes\n", + " self.image_size = image_size\n", + " # Explicit task type; bypasses WL's label-shape heuristic so bboxes are\n", + " # rendered as detection overlays (not mistaken for classification).\n", + " self.task_type = \"detection\"\n", + " self.class_names = CLASS_NAMES[:num_classes]\n", + "\n", + " base = download_penn_fudan(root)\n", + " img_dir = os.path.join(base, \"PNGImages\")\n", + " mask_dir = os.path.join(base, \"PedMasks\")\n", + "\n", + " all_imgs = sorted(f for f in os.listdir(img_dir) if f.lower().endswith(\".png\"))\n", + "\n", + " # Deterministic train/val split: every k-th image goes to val.\n", + " k = max(2, int(round(1.0 / max(val_fraction, 1e-6))))\n", + " if split == \"val\":\n", + " selected = all_imgs[::k]\n", + " else:\n", + " val_set = set(all_imgs[::k])\n", + " selected = [f for f in all_imgs if f not in val_set]\n", + "\n", + " selected = selected[:max_samples] if max_samples != None else selected\n", + "\n", + " self.images = []\n", + " self.masks = []\n", + " for fname in selected:\n", + " base_name, _ = os.path.splitext(fname)\n", + " mask_path = os.path.join(mask_dir, base_name + \"_mask.png\")\n", + " if os.path.exists(mask_path):\n", + " self.images.append(os.path.join(img_dir, fname))\n", + " self.masks.append(mask_path)\n", + "\n", + " if len(self.images) == 0:\n", + " raise RuntimeError(f\"No image/mask pairs found under {base}\")\n", + "\n", + " self.image_transform = transforms.Compose(\n", + " [\n", + " transforms.Resize((image_size, image_size), interpolation=Image.BILINEAR),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),\n", + " ]\n", + " )\n", + "\n", + " def __len__(self):\n", + " return len(self.images)\n", + "\n", + " def _load_boxes(self, mask_path):\n", + " \"\"\"Read mask → [N, 6] float32 = [x1, y1, x2, y2, cls=0, conf=1.0] (normalized).\"\"\"\n", + " boxes_px, h, w = _boxes_from_mask(mask_path)\n", + " if boxes_px.shape[0] == 0:\n", + " return np.zeros((0, 6), dtype=np.float32)\n", + " norm = boxes_px.copy()\n", + " norm[:, [0, 2]] /= float(w)\n", + " norm[:, [1, 3]] /= float(h)\n", + " n = norm.shape[0]\n", + " cls = np.zeros((n, 1), dtype=np.float32) # single class: person\n", + " conf = np.ones((n, 1), dtype=np.float32)\n", + " return np.concatenate([norm, cls, conf], axis=1).astype(np.float32)\n", + "\n", + " def __getitem__(self, idx):\n", + " \"\"\"Returns (item, uid, target, metadata).\n", + "\n", + " - item: normalized image tensor [C, H, W]\n", + " - uid: unique sample id (string)\n", + " - target: [N, 6] float32 = [x1, y1, x2, y2, class_id, confidence]\n", + " - metadata: dict with source paths\n", + " \"\"\"\n", + " return self.get_items(idx, include_metadata=True, include_labels=True, include_images=True)\n", + "\n", + " def get_items(self, idx, include_metadata=False, include_labels=False, include_images=False):\n", + " img_path = self.images[idx]\n", + " mask_path = self.masks[idx]\n", + " uid = os.path.splitext(os.path.basename(img_path))[0]\n", + "\n", + " metadata = {\n", + " \"img_path\": img_path,\n", + " \"mask_path\": mask_path,\n", + " } if include_metadata else None\n", + "\n", + " img_t = None\n", + " if include_images:\n", + " img = Image.open(img_path).convert(\"RGB\")\n", + " img_t = self.image_transform(img)\n", + "\n", + " target = None\n", + " if include_labels:\n", + " target = self._load_boxes(mask_path)\n", + "\n", + " return img_t, uid, target, metadata\n", + "\n", + "\n", + "def det_collate(batch):\n", + " \"\"\"Collate WL per-sample tuples for object detection.\n", + "\n", + " A sample owns a variable number of boxes, so targets cannot be stacked. We\n", + " keep them as a Python list (one [N_i, 6] tensor per sample), exactly the\n", + " layout WL's per-instance helpers expect (``targets[s]`` iterates that\n", + " sample's boxes in annotation order).\n", + "\n", + " Returns:\n", + " images: FloatTensor [B, C, H, W]\n", + " ids: list[str] of length B\n", + " targets: list[B] of [N_i, 6] float tensors ([x1, y1, x2, y2, cls, conf])\n", + " metas: list[B] of metadata dicts\n", + " \"\"\"\n", + " images = torch.stack([b[0] for b in batch], dim=0)\n", + " ids = [b[1] for b in batch]\n", + " targets = [\n", + " torch.as_tensor(b[2], dtype=torch.float32)\n", + " if not isinstance(b[2], torch.Tensor) else b[2].float()\n", + " for b in batch\n", + " ]\n", + " metas = [b[3] if len(b) > 3 else None for b in batch]\n", + " return images, ids, targets, metas" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "GFZWMzNi_xBf" + }, + "outputs": [], + "source": [ + "# ===== utils/model.py =====\n", + "# =============================================================================\n", + "# Small single-shot grid detector on a pretrained MobileNetV3-Small backbone\n", + "# =============================================================================\n", + "# A frozen/fine-tuned ImageNet-pretrained backbone extracts features; a tiny\n", + "# detection head turns them into an S x S grid where each cell predicts ONE box:\n", + "# (objectness, tx, ty, tw, th, class_logits...).\n", + "#\n", + "# Encoding (all coordinates normalized to the [0, 1] image frame):\n", + "# * objectness = sigmoid(t_obj) -> P(box present in this cell)\n", + "# * cx = (col + sigmoid(tx)) / S -> box center, x\n", + "# * cy = (row + sigmoid(ty)) / S -> box center, y\n", + "# * w = sigmoid(tw) -> box width (fraction of image)\n", + "# * h = sigmoid(th) -> box height (fraction of image)\n", + "# * class = softmax(class_logits)\n", + "#\n", + "# Raw forward output keeps logits (loss applies the activations); `decode`\n", + "# turns logits into xyxy boxes for metrics and UI rendering.\n", + "import torch\n", + "import torch.nn as nn\n", + "\n", + "from torchvision.models import mobilenet_v3_small, MobileNet_V3_Small_Weights\n", + "\n", + "\n", + "def decode_grid(outputs, grid_size):\n", + " \"\"\"Decode raw grid logits -> per-cell xyxy boxes, objectness and class probs.\n", + "\n", + " Shared by the model (``SmallDetector.decode``) and the criterions so the\n", + " encoding lives in exactly one place.\n", + "\n", + " Args:\n", + " outputs: [B, S, S, 5 + num_classes] raw logits.\n", + " grid_size: S.\n", + "\n", + " Returns:\n", + " boxes: [B, S, S, 4] xyxy in [0, 1]\n", + " obj: [B, S, S] objectness probability\n", + " cls_probs: [B, S, S, num_classes] class probabilities\n", + " \"\"\"\n", + " B, S, _, _ = outputs.shape\n", + " device = outputs.device\n", + "\n", + " obj = torch.sigmoid(outputs[..., 0])\n", + " tx = torch.sigmoid(outputs[..., 1])\n", + " ty = torch.sigmoid(outputs[..., 2])\n", + " w = torch.sigmoid(outputs[..., 3])\n", + " h = torch.sigmoid(outputs[..., 4])\n", + " cls_probs = torch.softmax(outputs[..., 5:], dim=-1)\n", + "\n", + " # Cell-origin grid (col=x, row=y).\n", + " cols = torch.arange(S, device=device).view(1, 1, S).expand(B, S, S)\n", + " rows = torch.arange(S, device=device).view(1, S, 1).expand(B, S, S)\n", + "\n", + " cx = (cols + tx) / S\n", + " cy = (rows + ty) / S\n", + " x1 = (cx - w / 2).clamp(0, 1)\n", + " y1 = (cy - h / 2).clamp(0, 1)\n", + " x2 = (cx + w / 2).clamp(0, 1)\n", + " y2 = (cy + h / 2).clamp(0, 1)\n", + "\n", + " boxes = torch.stack([x1, y1, x2, y2], dim=-1)\n", + " return boxes, obj, cls_probs\n", + "\n", + "\n", + "class SmallDetector(nn.Module):\n", + " def __init__(\n", + " self,\n", + " in_channels=3,\n", + " num_classes=1,\n", + " image_size=256,\n", + " grid_size=8,\n", + " pretrained=True,\n", + " freeze_backbone=True,\n", + " ):\n", + " super().__init__()\n", + " # For WeightsLab\n", + " self.task_type = \"detection\"\n", + " self.num_classes = num_classes\n", + " self.class_names = [\"person\"][:num_classes]\n", + " self.grid_size = grid_size\n", + " self.image_size = image_size\n", + " self.input_shape = (1, in_channels, image_size, image_size)\n", + "\n", + " # Channels per cell: objectness(1) + box(4) + class logits(num_classes)\n", + " self.preds_per_cell = 5 + num_classes\n", + "\n", + " # --- Pretrained backbone (ImageNet) ---\n", + " weights = MobileNet_V3_Small_Weights.IMAGENET1K_V1 if pretrained else None\n", + " backbone = mobilenet_v3_small(weights=weights)\n", + " self.backbone = backbone.features # [B, 576, H/32, W/32]\n", + " backbone_out_ch = 576\n", + "\n", + " self.freeze_backbone = freeze_backbone\n", + " if freeze_backbone:\n", + " for p in self.backbone.parameters():\n", + " p.requires_grad = False\n", + "\n", + " # --- Detection head (lightweight, trained from scratch) ---\n", + " self.neck = nn.Sequential(\n", + " nn.Conv2d(backbone_out_ch, 256, 3, padding=1, bias=False),\n", + " nn.BatchNorm2d(256),\n", + " nn.ReLU(inplace=True),\n", + " nn.Conv2d(256, 128, 3, padding=1, bias=False),\n", + " nn.BatchNorm2d(128),\n", + " nn.ReLU(inplace=True),\n", + " )\n", + " self.head = nn.Conv2d(128, self.preds_per_cell, 1)\n", + "\n", + " def train(self, mode=True):\n", + " \"\"\"Keep a frozen backbone in eval mode (so its BatchNorm stats stay fixed).\"\"\"\n", + " super().train(mode)\n", + " if self.freeze_backbone:\n", + " self.backbone.eval()\n", + " return self\n", + "\n", + " def forward(self, x):\n", + " \"\"\"Returns raw logits [B, S, S, 5 + num_classes].\"\"\"\n", + " if self.freeze_backbone:\n", + " with torch.no_grad():\n", + " feat = self.backbone(x)\n", + " else:\n", + " feat = self.backbone(x)\n", + "\n", + " feat = self.neck(feat)\n", + " out = self.head(feat) # [B, preds_per_cell, S', S']\n", + "\n", + " # Resize feature grid to the configured grid_size.\n", + " if out.shape[-1] != self.grid_size or out.shape[-2] != self.grid_size:\n", + " out = nn.functional.adaptive_avg_pool2d(out, self.grid_size)\n", + " # [B, C, S, S] -> [B, S, S, C]\n", + " return out.permute(0, 2, 3, 1).contiguous()\n", + "\n", + " def decode(self, outputs):\n", + " \"\"\"Decode raw logits -> per-cell xyxy boxes, objectness, class probs.\"\"\"\n", + " return decode_grid(outputs, self.grid_size)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "GQpthcwy_xBf" + }, + "outputs": [], + "source": [ + "# ===== utils/criterions.py =====\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "\n", + "# =============================================================================\n", + "# Per-instance / per-sample detection criterions (YOLO-v1 style loss + IoU)\n", + "# =============================================================================\n", + "# The detection dataset yields, per sample, a [N, 6] target tensor\n", + "# ``[x1, y1, x2, y2, class_id, confidence]`` (normalized to [0, 1]). Each GT box\n", + "# is assigned to the grid cell containing its center; that cell is \"responsible\"\n", + "# for predicting the box.\n", + "#\n", + "# * PerSampleDetectionLoss -> one differentiable loss scalar per sample ([B]),\n", + "# wrapped with ``per_sample=True`` (the value WL backprops + dashboards).\n", + "# * PerSampleIoU -> mean IoU over a sample's boxes ([B]), a metric.\n", + "# * PerInstanceIoU -> flat tensor of one IoU per GT box (sample-major\n", + "# order), wrapped with ``per_instance=True`` so WL auto-saves it at\n", + "# (sample_id, annotation_id). The ordering matches the per-sample target\n", + "# iteration, so the wrapper's auto ``batch_idx`` maps each value correctly.\n", + "\n", + "_EPS = 1e-6\n", + "_LAMBDA_COORD = 5.0\n", + "_LAMBDA_NOOBJ = 0.5\n", + "\n", + "\n", + "def box_iou_xyxy(a, b):\n", + " \"\"\"IoU between two aligned sets of xyxy boxes. a, b: [..., 4] -> [...].\"\"\"\n", + " x1 = torch.maximum(a[..., 0], b[..., 0])\n", + " y1 = torch.maximum(a[..., 1], b[..., 1])\n", + " x2 = torch.minimum(a[..., 2], b[..., 2])\n", + " y2 = torch.minimum(a[..., 3], b[..., 3])\n", + "\n", + " inter = (x2 - x1).clamp(min=0) * (y2 - y1).clamp(min=0)\n", + " area_a = (a[..., 2] - a[..., 0]).clamp(min=0) * (a[..., 3] - a[..., 1]).clamp(min=0)\n", + " area_b = (b[..., 2] - b[..., 0]).clamp(min=0) * (b[..., 3] - b[..., 1]).clamp(min=0)\n", + " union = area_a + area_b - inter + _EPS\n", + " return inter / union\n", + "\n", + "\n", + "def _responsible_cells(boxes, S):\n", + " \"\"\"Map GT boxes -> their responsible (row, col) cell and center offsets.\n", + "\n", + " Args:\n", + " boxes: [N, 4] xyxy in [0, 1].\n", + " S: grid size.\n", + "\n", + " Returns:\n", + " rows, cols: [N] long, the responsible cell indices.\n", + " off_x, off_y: [N] center offset within the cell, in [0, 1).\n", + " w, h: [N] box size as a fraction of the image.\n", + " \"\"\"\n", + " cx = (boxes[:, 0] + boxes[:, 2]) / 2\n", + " cy = (boxes[:, 1] + boxes[:, 3]) / 2\n", + " w = (boxes[:, 2] - boxes[:, 0]).clamp(_EPS, 1.0)\n", + " h = (boxes[:, 3] - boxes[:, 1]).clamp(_EPS, 1.0)\n", + "\n", + " cols = (cx * S).long().clamp(0, S - 1)\n", + " rows = (cy * S).long().clamp(0, S - 1)\n", + " off_x = (cx * S - cols).clamp(0, 1)\n", + " off_y = (cy * S - rows).clamp(0, 1)\n", + " return rows, cols, off_x, off_y, w, h\n", + "\n", + "\n", + "def _per_sample_loss(outputs, targets, num_classes, weights=None):\n", + " \"\"\"YOLO-v1 style loss, returned as one scalar per sample ([B], with grad).\"\"\"\n", + " B, S = outputs.shape[0], outputs.shape[1]\n", + " device = outputs.device\n", + "\n", + " obj_logit = outputs[..., 0] # [B, S, S]\n", + " tx = torch.sigmoid(outputs[..., 1])\n", + " ty = torch.sigmoid(outputs[..., 2])\n", + " w_pred = torch.sigmoid(outputs[..., 3])\n", + " h_pred = torch.sigmoid(outputs[..., 4])\n", + " cls_logits = outputs[..., 5:] # [B, S, S, C]\n", + "\n", + " if weights is not None:\n", + " weights = torch.as_tensor(weights, device=device, dtype=outputs.dtype)\n", + "\n", + " losses = []\n", + " for s in range(B):\n", + " tgt = targets[s]\n", + " tgt = torch.as_tensor(tgt, device=device, dtype=outputs.dtype)\n", + " if tgt.ndim == 1:\n", + " tgt = tgt.view(-1, 6) if tgt.numel() else tgt.view(0, 6)\n", + "\n", + " obj_target = torch.zeros((S, S), device=device, dtype=outputs.dtype)\n", + "\n", + " coord_loss = torch.zeros((), device=device)\n", + " class_loss = torch.zeros((), device=device)\n", + "\n", + " if tgt.numel() > 0:\n", + " boxes = tgt[:, :4]\n", + " cls_ids = tgt[:, 4].long().clamp(0, num_classes - 1)\n", + " rows, cols, off_x, off_y, gw, gh = _responsible_cells(boxes, S)\n", + "\n", + " obj_target[rows, cols] = 1.0\n", + "\n", + " # Localization: center offset (linear) + size in sqrt space (YOLO trick\n", + " # so small-box errors weigh as much as large-box ones).\n", + " coord = (\n", + " (tx[s, rows, cols] - off_x) ** 2\n", + " + (ty[s, rows, cols] - off_y) ** 2\n", + " + (torch.sqrt(w_pred[s, rows, cols] + _EPS) - torch.sqrt(gw + _EPS)) ** 2\n", + " + (torch.sqrt(h_pred[s, rows, cols] + _EPS) - torch.sqrt(gh + _EPS)) ** 2\n", + " )\n", + " coord_loss = _LAMBDA_COORD * coord.sum()\n", + "\n", + " ce = F.cross_entropy(\n", + " cls_logits[s, rows, cols], cls_ids, reduction=\"none\"\n", + " )\n", + " if weights is not None:\n", + " ce = ce * weights[cls_ids]\n", + " class_loss = ce.sum()\n", + "\n", + " # Objectness BCE over the whole grid; down-weight the (many) empty cells.\n", + " obj_weight = torch.where(\n", + " obj_target > 0,\n", + " torch.ones_like(obj_target),\n", + " torch.full_like(obj_target, _LAMBDA_NOOBJ),\n", + " )\n", + " obj_loss = (\n", + " F.binary_cross_entropy_with_logits(\n", + " obj_logit[s], obj_target, reduction=\"none\"\n", + " ) * obj_weight\n", + " ).sum()\n", + "\n", + " losses.append(coord_loss + class_loss + obj_loss)\n", + "\n", + " return torch.stack(losses)\n", + "\n", + "\n", + "def _per_box_iou(outputs, targets, grid_size):\n", + " \"\"\"IoU of each GT box against its responsible cell's decoded prediction.\n", + "\n", + " Returns a list[B] of 1-D tensors (one IoU per box for that sample, in\n", + " annotation order). Detached — this is a metric, not a loss.\n", + " \"\"\"\n", + " boxes_grid, _, _ = decode_grid(outputs, grid_size) # [B, S, S, 4]\n", + " B = outputs.shape[0]\n", + " S = grid_size\n", + " device = outputs.device\n", + "\n", + " per_sample = []\n", + " for s in range(B):\n", + " tgt = torch.as_tensor(targets[s], device=device, dtype=outputs.dtype)\n", + " if tgt.numel() == 0:\n", + " per_sample.append(torch.zeros(0, device=device))\n", + " continue\n", + " if tgt.ndim == 1:\n", + " tgt = tgt.view(-1, 6)\n", + "\n", + " gt_boxes = tgt[:, :4]\n", + " rows, cols, _, _, _, _ = _responsible_cells(gt_boxes, S)\n", + " pred_boxes = boxes_grid[s, rows, cols] # [N, 4]\n", + " ious = box_iou_xyxy(pred_boxes, gt_boxes) # [N]\n", + " per_sample.append(ious.detach())\n", + "\n", + " return per_sample\n", + "\n", + "\n", + "class PerSampleDetectionLoss(nn.Module):\n", + " \"\"\"Total detection loss aggregated to one value per sample ([B]).\"\"\"\n", + "\n", + " def __init__(self, num_classes, grid_size, weights=None):\n", + " super().__init__()\n", + " self.num_classes = num_classes\n", + " self.grid_size = grid_size\n", + " self.register_buffer(\n", + " \"weights\", torch.tensor(weights) if weights is not None else None\n", + " )\n", + "\n", + " def forward(self, outputs, targets):\n", + " return _per_sample_loss(outputs, targets, self.num_classes, weights=self.weights)\n", + "\n", + "\n", + "class PerSampleIoU(nn.Module):\n", + " \"\"\"Mean IoU over a sample's boxes -> one value per sample ([B]).\"\"\"\n", + "\n", + " def __init__(self, num_classes, grid_size):\n", + " super().__init__()\n", + " self.num_classes = num_classes\n", + " self.grid_size = grid_size\n", + "\n", + " def forward(self, outputs, targets):\n", + " per_sample = _per_box_iou(outputs, targets, self.grid_size)\n", + " out = [\n", + " v.mean() if v.numel() > 0 else torch.zeros((), device=outputs.device)\n", + " for v in per_sample\n", + " ]\n", + " return torch.stack(out).detach()\n", + "\n", + "\n", + "class PerInstanceIoU(nn.Module):\n", + " \"\"\"IoU per GT box -> flat tensor [total_boxes] (sample-major order).\"\"\"\n", + "\n", + " def __init__(self, num_classes, grid_size):\n", + " super().__init__()\n", + " self.num_classes = num_classes\n", + " self.grid_size = grid_size\n", + "\n", + " def forward(self, outputs, targets):\n", + " per_sample = _per_box_iou(outputs, targets, self.grid_size)\n", + " flat = [v for s in per_sample for v in s]\n", + " if not flat:\n", + " return torch.zeros(0, device=outputs.device)\n", + " return torch.stack(flat).detach()\n", + "\n", + "\n", + "# =============================================================================\n", + "# Inference-time decoding (for UI prediction overlays)\n", + "# =============================================================================\n", + "def decode_predictions(outputs, grid_size, conf_thresh=0.3, max_det=10):\n", + " \"\"\"Turn raw grid logits into a per-sample list of detected boxes.\n", + "\n", + " Returns list[B] of [M, 6] numpy-friendly tensors\n", + " ``[x1, y1, x2, y2, class_id, confidence]`` (kept on CPU, detached) — the\n", + " exact 6-column schema WL renders for detection predictions.\n", + " \"\"\"\n", + " boxes_grid, obj, cls_probs = decode_grid(outputs, grid_size)\n", + " B, S = outputs.shape[0], grid_size\n", + "\n", + " cls_conf, cls_id = cls_probs.max(dim=-1) # [B, S, S]\n", + " score = obj * cls_conf # combined confidence\n", + "\n", + " flat_boxes = boxes_grid.view(B, S * S, 4)\n", + " flat_score = score.view(B, S * S)\n", + " flat_cls = cls_id.view(B, S * S)\n", + "\n", + " results = []\n", + " for s in range(B):\n", + " keep = flat_score[s] >= conf_thresh\n", + " if keep.sum() == 0:\n", + " results.append(torch.zeros((0, 6)))\n", + " continue\n", + " sc = flat_score[s][keep]\n", + " bx = flat_boxes[s][keep]\n", + " cl = flat_cls[s][keep].to(bx.dtype)\n", + "\n", + " # Keep the most confident detections (cheap top-k in place of full NMS).\n", + " order = torch.argsort(sc, descending=True)[:max_det]\n", + " det = torch.cat([bx[order], cl[order, None], sc[order, None]], dim=1)\n", + " results.append(det.detach().cpu())\n", + "\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9hjJ9fHD_xBf" + }, + "source": [ + "## 3. Configuration\n", + "\n", + "Every tunable lives here in one dict (the example's `config.yaml`, inlined with its comments). Wrapping it with `flag=\"hyperparameters\"` lets the Studio UI read - and live-edit - these values while training." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "YZCPlGjN_xBf", + "outputId": "97c94163-c64d-4f7e-8ce7-d812b5983885", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:32:57.025 INFO:weightslab.components.checkpoint_manager:__init__: CheckpointManager initialized at /tmp/tmpf35sd4_z\n", + "16/07/2026-08:32:57.030 INFO:weightslab.src:watch_or_edit: Registered new checkpoint manager in ledger\n", + "16/07/2026-08:32:57.040 INFO:root:set_log_directory: Log file moved from /tmp/tmpmt3c2rur/weightslab_20260716_083240.log to /tmp/tmpf35sd4_z/weightslab_20260716_083240.log\n", + "16/07/2026-08:32:57.041 INFO:root:set_log_directory: Log directory updated to: /tmp/tmpf35sd4_z\n", + "16/07/2026-08:32:57.044 INFO:root:set_log_directory: Log file: /tmp/tmpf35sd4_z/weightslab_20260716_083240.log\n", + "Experiment logs -> /tmp/tmpf35sd4_z\n" + ] + } + ], + "source": [ + "# Inlined from the example's config.yaml (comments preserved).\n", + "config = {\n", + " # -- Global -----------------------------------------------------------\n", + " \"device\": \"auto\", # auto -> cuda if available else cpu (resolved in the imports cell)\n", + " \"experiment_name\": \"detection_pennfudan_usecase\", # name shown in Weights Studio\n", + " \"training_steps_to_do\": None, # None = open-ended in the example; capped below for Colab\n", + " \"root_log_dir\": None, # None -> a temp dir is created below\n", + "\n", + " \"checkpoint_manager\": {\n", + " \"load_config\": False, # ignore a previous run's config so edits here take effect\n", + " },\n", + "\n", + " # Initially compute natural-sort signals, e.g. average intensity.\n", + " \"compute_natural_sort\": True,\n", + "\n", + " # -- Experiment schedule ---------------------------------------------\n", + " \"eval_full_to_train_steps_ratio\": 5, # run a full eval every N steps\n", + " \"experiment_dump_to_train_steps_ratio\": 5, # export history + dataframe every N steps\n", + " \"tqdm_display\": True,\n", + " \"is_training\": False, # start training immediately or not\n", + "\n", + " # -- Serving ----------------------------------------------------------\n", + " \"serving_grpc\": True, # expose the gRPC backend for Weights Studio\n", + " \"serving_cli\": True,\n", + "\n", + " # -- Global dataframe storage ----------------------------------------\n", + " \"ledger_enable_h5_persistence\": True,\n", + " \"ledger_enable_flushing_threads\": True,\n", + " \"ledger_flush_max_rows\": 100,\n", + " \"ledger_flush_interval\": 60.0,\n", + "\n", + " # -- Model / task -----------------------------------------------------\n", + " \"num_classes\": 1, # Penn-Fudan: single class (person)\n", + " \"image_size\": 128,\n", + " \"grid_size\": 8, # detector predicts on an 8x8 cell grid\n", + " \"conf_thresh\": 0.3, # objectness*class confidence threshold for displayed predictions\n", + " \"pretrained_backbone\": True, # ImageNet-pretrained MobileNetV3-Small feature extractor\n", + " \"freeze_backbone\": True, # train only the detection head (faster, less data hungry)\n", + "\n", + " \"optimizer\": {\n", + " \"lr\": 0.001,\n", + " },\n", + "\n", + " # -- Data (Penn-Fudan pedestrians; ~170 images downloaded on first run under data_root) --\n", + " \"data_root\": \"./data\", # portable relative path; the dataset downloads here on first run\n", + " \"data\": {\n", + " \"train_loader\": {\n", + " \"batch_size\": 8,\n", + " \"shuffle\": True,\n", + " \"max_samples\": None, # None = use the full training split\n", + " },\n", + " \"test_loader\": {\n", + " \"batch_size\": 8,\n", + " \"shuffle\": False,\n", + " \"max_samples\": None,\n", + " },\n", + " },\n", + "}\n", + "\n", + "wl.watch_or_edit(config, flag=\"hyperparameters\", defaults=config, poll_interval=1.0)\n", + "\n", + "# Resolve the log directory (a temp dir when unset), mirroring the example's main.py.\n", + "log_dir = config.get(\"root_log_dir\") or tempfile.mkdtemp(prefix=\"weightslab_detection_\")\n", + "config[\"root_log_dir\"] = log_dir\n", + "os.makedirs(log_dir, exist_ok=True)\n", + "print(\"Experiment logs ->\", log_dir)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vWWllcOA_xBg" + }, + "source": [ + "## 4. Data\n", + "\n", + "Build the Penn-Fudan train/val datasets (the ~170 images **download automatically** on first construction) and wrap them as tracked dataloaders. Detection targets have a variable number of boxes per image, so the custom `det_collate` keeps them as a list." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "Xqkj3Lo__xBg", + "outputId": "ae81a09b-c057-45d3-e3b3-254d99b31e78", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[data] Downloading Penn-Fudan dataset to ./data/PennFudanPed.zip ...\n", + "[data] Extracting Penn-Fudan ...\n", + "16/07/2026-08:33:02.465 INFO:weightslab.data.data_samples_with_ops:__init__: [DataSampleTrackingWrapper] H5 persistence enabled at /tmp/tmpf35sd4_z/checkpoints/data/data.h5\n", + "16/07/2026-08:33:02.469 INFO:weightslab.data.data_samples_with_ops:__init__: Preloading sample statistics for PHYSICAL indices in split 'train_loader' with: preload_metadata=True...\n", + "16/07/2026-08:33:02.471 INFO:weightslab.data.data_samples_with_ops:__init__: Labels will be loaded on demand from the wrapped dataset for split 'train_loader' when accessed, which may increase latency on first access but reduces initialization time.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Initializing ledger for split 'train_loader': 100%|██████████| 136/136 [00:00<00:00, 94114.06it/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:33:02.481 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] Registering split 'train_loader' with 136 samples.\n", + "16/07/2026-08:33:02.527 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] After annotation expansion: 136 samples → 136 annotation rows.\n", + "16/07/2026-08:33:02.528 INFO:weightslab.data.h5_array_store:__init__: [H5ArrayStore] Initialized with cache limit: 2048MB\n", + "16/07/2026-08:33:02.562 INFO:weightslab.data.data_samples_with_ops:__init__: [DataSampleTrackingWrapper] H5 persistence enabled at /tmp/tmpf35sd4_z/checkpoints/data/data.h5\n", + "16/07/2026-08:33:02.564 INFO:weightslab.data.data_samples_with_ops:__init__: Preloading sample statistics for PHYSICAL indices in split 'test_loader' with: preload_labels=True, preload_metadata=True...\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Initializing ledger for split 'test_loader': 100%|██████████| 34/34 [00:00<00:00, 102.86it/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:33:02.902 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] Registering split 'test_loader' with 34 samples.\n", + "16/07/2026-08:33:02.907 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] After annotation expansion: 34 samples → 34 annotation rows.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n" + ] + } + ], + "source": [ + "# Penn-Fudan auto-downloads (~170 images) on first dataset construction - nothing to upload.\n", + "data_root = config.get(\"data_root\") or \"./data\"\n", + "os.makedirs(data_root, exist_ok=True)\n", + "\n", + "num_classes = int(config[\"num_classes\"])\n", + "image_size = int(config[\"image_size\"])\n", + "grid_size = int(config[\"grid_size\"])\n", + "conf_thresh = float(config[\"conf_thresh\"])\n", + "\n", + "train_cfg = config.get(\"data\", {}).get(\"train_loader\", {})\n", + "test_cfg = config.get(\"data\", {}).get(\"test_loader\", {})\n", + "\n", + "_train_dataset = PennFudanDetectionDataset(\n", + " root=data_root,\n", + " split=\"train\",\n", + " num_classes=num_classes,\n", + " image_size=image_size,\n", + " max_samples=train_cfg.get(\"max_samples\", None),\n", + ")\n", + "_val_dataset = PennFudanDetectionDataset(\n", + " root=data_root,\n", + " split=\"val\",\n", + " num_classes=num_classes,\n", + " image_size=image_size,\n", + " max_samples=test_cfg.get(\"max_samples\", None),\n", + ")\n", + "\n", + "# Tracked dataloaders (detection uses the custom det_collate defined above).\n", + "train_loader = wl.watch_or_edit(\n", + " _train_dataset,\n", + " flag=\"data\",\n", + " loader_name=\"train_loader\",\n", + " batch_size=train_cfg.get(\"batch_size\", 8),\n", + " shuffle=train_cfg.get(\"shuffle\", True),\n", + " compute_hash=False,\n", + " is_training=True,\n", + " array_autoload_arrays=False,\n", + " array_return_proxies=True,\n", + " array_use_cache=True,\n", + " preload_labels=False,\n", + " collate_fn=det_collate,\n", + ")\n", + "test_loader = wl.watch_or_edit(\n", + " _val_dataset,\n", + " flag=\"data\",\n", + " loader_name=\"test_loader\",\n", + " batch_size=test_cfg.get(\"batch_size\", 8),\n", + " shuffle=test_cfg.get(\"shuffle\", False),\n", + " compute_hash=False,\n", + " is_training=False,\n", + " array_autoload_arrays=False,\n", + " array_return_proxies=True,\n", + " array_use_cache=True,\n", + " preload_labels=True,\n", + " collate_fn=det_collate,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pvj_ywPr_xBg" + }, + "source": [ + "## 5. Model, optimizer and tracked signals\n", + "\n", + "Wrap the detector, optimizer, loss and IoU metrics with `wl.watch_or_edit(...)`. The loss is tracked **per sample**; per-instance IoU stores one value per ground-truth box at `(sample_id, annotation_id)`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "21SKj-oK_xBg", + "outputId": "698a493f-d5a5-4e42-c399-563b6addc2bf", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading: \"https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth\" to /root/.cache/torch/hub/checkpoints/mobilenet_v3_small-047dcff4.pth\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 9.83M/9.83M [00:00<00:00, 133MB/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:33:23.329 INFO:weightslab.backend.model_interface:__init__: Using checkpoint manager from ledger\n", + "\n", + "============================================================\n", + "Computing class weights for 1 classes (max 200 samples)...\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + " Analyzing Distribution: 100%|██████████| 136/136 [00:00<00:00, 147.35it/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Class distribution and weights:\n", + "Class 0 (person): 100.00% -> weight: 1.000\n", + "============================================================\n", + "\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "/tmp/ipykernel_5133/4133204388.py:171: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.detach().clone() or sourceTensor.detach().clone().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " \"weights\", torch.tensor(weights) if weights is not None else None\n" + ] + } + ], + "source": [ + "# --- Model ---\n", + "_model = SmallDetector(\n", + " in_channels=3, num_classes=num_classes,\n", + " image_size=image_size, grid_size=grid_size,\n", + " pretrained=bool(config[\"pretrained_backbone\"]),\n", + " freeze_backbone=bool(config[\"freeze_backbone\"]),\n", + ").to(device)\n", + "model = wl.watch_or_edit(_model, flag=\"model\", device=device)\n", + "\n", + "# --- Optimizer (only trainable params; the backbone may be frozen) ---\n", + "lr = config.get(\"optimizer\", {}).get(\"lr\", 1e-3)\n", + "trainable = [p for p in model.parameters() if p.requires_grad]\n", + "_optimizer = optim.Adam(trainable, lr=lr)\n", + "optimizer = wl.watch_or_edit(_optimizer, flag=\"optimizer\")\n", + "\n", + "\n", + "# --- Detection loss (per sample) + IoU (per sample & per instance) signals ---\n", + "# per_sample=True auto-saves one value per sample; per_instance=True auto-saves\n", + "# one IoU per (sample_id, annotation_id), i.e. one per ground-truth box.\n", + "def _make_det_signals(split: str, weights=None) -> dict:\n", + " return {\n", + " \"loss\": wl.watch_or_edit(\n", + " PerSampleDetectionLoss(num_classes, grid_size, weights=weights),\n", + " flag=\"loss\",\n", + " name=f\"{split}_loss/sample\", per_sample=True, log=True,\n", + " ),\n", + " \"iou_sample\": wl.watch_or_edit(\n", + " PerSampleIoU(num_classes, grid_size), flag=\"metric\",\n", + " name=f\"{split}_iou/sample\", per_sample=True, log=True,\n", + " ),\n", + " \"iou_instance\": wl.watch_or_edit(\n", + " PerInstanceIoU(num_classes, grid_size), flag=\"metric\",\n", + " name=f\"{split}_iou/instance\", per_instance=True, log=True,\n", + " ),\n", + " }\n", + "\n", + "\n", + "# Class weights from ground-truth box counts (optional; balances rare shapes).\n", + "def compute_class_weights(dataset, num_classes, max_samples=200):\n", + " print(\"\\n\" + \"=\" * 60, flush=True)\n", + " print(f\"Computing class weights for {num_classes} classes (max {max_samples} samples)...\", flush=True)\n", + " class_counts = np.zeros(num_classes, dtype=np.float64)\n", + " num_samples = min(len(dataset), max_samples)\n", + "\n", + " for idx in tqdm.tqdm(range(num_samples), desc=\" Analyzing Distribution\"):\n", + " _, _, target, _ = dataset.get_items(idx, include_labels=True)\n", + " if target is None or len(target) == 0:\n", + " continue\n", + " for c in target[:, 4].astype(np.int64):\n", + " if 0 <= c < num_classes:\n", + " class_counts[c] += 1\n", + "\n", + " class_counts = np.maximum(class_counts, 1) # Avoid div by zero\n", + " total = class_counts.sum()\n", + " class_weights = total / (num_classes * class_counts)\n", + " class_weights = class_weights / class_weights.mean() # Normalize\n", + "\n", + " print(\"\\nClass distribution and weights:\", flush=True)\n", + " for c in range(num_classes):\n", + " pct = (class_counts[c] / total) * 100\n", + " print(f\"Class {c} ({dataset.class_names[c]}): {pct:6.2f}% -> weight: {class_weights[c]:.3f}\", flush=True)\n", + " print(\"=\" * 60 + \"\\n\", flush=True)\n", + " return torch.FloatTensor(class_weights).to(device)\n", + "\n", + "\n", + "weights = compute_class_weights(_train_dataset, num_classes)\n", + "\n", + "train_sig = _make_det_signals(\"train\", weights=weights)\n", + "test_sig = _make_det_signals(\"test\", weights=weights)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xjCJ2AYV_xBg" + }, + "source": [ + "## 6. Train and evaluate steps\n", + "\n", + "The `guard_training_context` / `guard_testing_context` blocks tell WeightsLab which phase it's in. The watched loss rides along with `batch_ids=ids` (so it is stored per sample) and `preds=` (so the decoded boxes are saved for the UI overlay)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "g_m26pWr_xBg" + }, + "outputs": [], + "source": [ + "def train(loader, model, optimizer, sig, device, grid_size, conf_thresh):\n", + " \"\"\"Single training step using the tracked dataloader + watched loss.\n", + "\n", + " loader yields (inputs, ids, targets, metadata) because of the\n", + " DataSampleTrackingWrapper. `targets` is per sample a [N, 6] tensor of boxes\n", + " ([x1, y1, x2, y2, class_id, confidence]); see utils/data.det_collate.\n", + " \"\"\"\n", + " with guard_training_context:\n", + " (inputs, ids, targets, _) = next(loader)\n", + " inputs = inputs.to(device)\n", + " targets = [t.to(device) for t in targets]\n", + "\n", + " optimizer.zero_grad()\n", + " outputs = model(inputs) # [B, S, S, 5 + num_classes]\n", + "\n", + " # Decoded boxes for the UI overlay (detached - display only).\n", + " preds = decode_predictions(outputs.detach(), grid_size, conf_thresh=conf_thresh)\n", + "\n", + " # Per-sample detection loss (tracked, saved, and the value we backprop).\n", + " # `preds=` rides along so WL stores the predicted boxes for this batch.\n", + " loss_per_sample = sig[\"loss\"](outputs, targets, batch_ids=ids, preds=preds)\n", + "\n", + " # Metrics: per-sample mean IoU + per-instance IoU (one value per box).\n", + " sig[\"iou_sample\"](outputs, targets, batch_ids=ids)\n", + " sig[\"iou_instance\"](outputs, targets, batch_ids=ids)\n", + "\n", + " loss = loss_per_sample.mean()\n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " return float(loss.detach().cpu().item())\n", + "\n", + "\n", + "def test(loader, model, sig, device, grid_size, conf_thresh, test_loader_len):\n", + " \"\"\"Full evaluation pass over the val loader.\"\"\"\n", + " losses = 0.0\n", + " ious = 0.0\n", + " with guard_testing_context, torch.no_grad():\n", + " for inputs, ids, targets, _ in loader:\n", + " inputs = inputs.to(device)\n", + " targets = [t.to(device) for t in targets]\n", + "\n", + " outputs = model(inputs)\n", + " preds = decode_predictions(outputs, grid_size, conf_thresh=conf_thresh)\n", + "\n", + " loss_per_sample = sig[\"loss\"](outputs, targets, batch_ids=ids, preds=preds)\n", + " iou_per_sample = sig[\"iou_sample\"](outputs, targets, batch_ids=ids)\n", + " sig[\"iou_instance\"](outputs, targets, batch_ids=ids)\n", + "\n", + " losses += torch.mean(loss_per_sample)\n", + " ious += torch.mean(iou_per_sample)\n", + "\n", + " loss = float((losses / test_loader_len).detach().cpu().item())\n", + " iou = float((ious / test_loader_len).detach().cpu().item())\n", + " return loss, iou * 100.0 # Return mean IoU as percentage" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QsFSIK3j_xBg" + }, + "source": [ + "## 7. Serve and train\n", + "\n", + "`wl.serve(...)` starts the background gRPC server (and a public `bore` tunnel) that Weights Studio connects to. `wl.start_training(...)` flips the experiment into the *training* state, then we run the loop, periodically evaluating and exporting signals.\n", + "\n", + "Leave this cell **running** while you watch it stream in the UI." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5yQOd8iv_xBg", + "outputId": "19c6f02b-80b3-4eea-a207-aeef7c83d6f5", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:33:32.039 INFO:weightslab.trainer.trainer_services:_run_security_preflight: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "[gRPC] Security preflight checks:\n", + "\tTLS: DISABLED (unencrypted traffic)\n", + "\tAuth tokens: NONE configured\n", + "\t! WARNING: GRPC_TLS_ENABLED=0. Traffic will be unencrypted. Use only for development.\n", + "\t! WARNING: No GRPC_AUTH_TOKEN/GRPC_AUTH_TOKENS configured. Only transport-level trust (TLS/mTLS) will protect RPC access.\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "16/07/2026-08:33:32.041 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Watchdogs disabled via WEIGHTSLAB_DISABLE_WATCHDOGS.\n", + "16/07/2026-08:33:32.045 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Thread callback started\n", + "16/07/2026-08:33:32.045 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Server started with watchdogs disabled (host=0.0.0.0 port=50051 workers=None)\n", + "16/07/2026-08:33:32.047 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Creating ThreadPoolExecutor with 6 worker threads (n_workers_grpc=None, max_concurrent_rpcs=None)\n", + "16/07/2026-08:33:32.051 INFO:weightslab.tunnel:_ensure_bore: Downloading bore v0.6.0 (bore-v0.6.0-x86_64-unknown-linux-musl.tar.gz)...\n", + "16/07/2026-08:33:32.129 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Server object created\n", + "16/07/2026-08:33:32.142 INFO:weightslab.trainer.services.agent.agent:__init__: Initializing DataManipulationAgent\n", + "16/07/2026-08:33:32.156 WARNING:weightslab.trainer.services.agent.agent:_load_config: Error loading config from /usr/local/lib/python3.12/dist-packages: [Errno 21] Is a directory: '/usr/local/lib/python3.12/dist-packages'\n", + "16/07/2026-08:33:32.157 INFO:weightslab.trainer.services.agent.agent:_load_config: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "Agent initialized from configuration /content/agent_config.yaml: \n", + "\tFinal Agent Configuration: Preferred Provider=openrouter, \n", + "\tFallback to Local=True, \n", + "\tOpenRouter Model=~google/gemini-flash-latest with:\n", + "\t\tAPI Key=None\n", + "\t\tBase URL=https://openrouter.ai/api/v1, \n", + "\tOllama Model=llama3.2:3b\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "16/07/2026-08:33:32.158 INFO:weightslab.trainer.services.agent.agent:_setup_providers: Setting up Ollama with model llama3.2:3b\n", + "16/07/2026-08:33:32.260 INFO:weightslab.trainer.services.agent.agent:_setup_providers: [Agent] Ollama enabled: llama3.2:3b\n", + "16/07/2026-08:33:32.261 INFO:weightslab.trainer.services.data_service:_compute_natural_sort_stats: [DataService] Starting natural sort stats computation with weights: {'brightness': 0.7, 'entropy': 0.3, 'hue': 0.0}\n", + "16/07/2026-08:33:32.273 INFO:weightslab.trainer.services.data_service:_compute_natural_sort_stats: [DataService] Computing sort stats for 170 samples...\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Computing Natural Sort Stats: 26%|██▌ | 44/170 [00:00<00:02, 44.78samples/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "============================================================\n", + " Backend exposed via bore at: bore.pub:41162\n", + " On your local machine (Docker running), run:\n", + " weightslab ui launch\n", + " weightslab tunnel bore.pub:41162\n", + "============================================================\n", + "16/07/2026-08:33:33.256 INFO:weightslab.backend.cli:cli_serve: cli_thread_started\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\rComputing Natural Sort Stats: 31%|███ | 53/170 [00:00<00:02, 54.32samples/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:33:33.346 INFO:weightslab.src:start_training: Starting WeightsLab training mode with a timeout of 3 seconds.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Computing Natural Sort Stats: 100%|██████████| 170/170 [00:02<00:00, 62.04samples/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:33:35.145 INFO:weightslab.trainer.services.data_service:_compute_natural_sort_stats: [DataService] Completed stats computation for 170 samples\n", + "\n", + "\n", + "Natural sort computation finished for 170 samples\n", + "\n", + "\n", + "16/07/2026-08:33:35.147 INFO:weightslab.trainer.services.data_service:_build_preview_cache: [PreviewCache] Building 64×64 or less or less preview cache for 170 samples …\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "/usr/local/lib/python3.12/dist-packages/weightslab/data/dataframe_manager.py:703: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '[7.64467454 7.63759617 7.82511253 7.60454149 7.68511861 7.70962498\n", + " 7.66256563 7.67420456 7.49258183 7.81929857 6.45717949 7.54281418\n", + " 6.79016624 7.48513405 7.32040805 7.82787458 7.77316711 7.5562755\n", + " 7.61231887 7.63537192 7.82628297 7.76547166 7.13113334 7.52482093\n", + " 7.63815989 7.7459368 7.58974044 7.75762173 7.18705444 7.48240921\n", + " 7.76600207 7.76543333 7.71791639 7.79005573]' has dtype incompatible with float32, please explicitly cast to a compatible dtype first.\n", + " self._df.loc[existing_idx, all_cols] = df_norm.loc[existing_idx, all_cols]\n", + "/usr/local/lib/python3.12/dist-packages/weightslab/data/dataframe_manager.py:703: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '[ 53.51681774 93.30201817 61.98435004 65.24323962 43.56923658\n", + " 93.96657441 67.03601995 73.99117037 80.06768645 84.95401979\n", + " 88.10120384 55.8291746 82.3123762 58.22935624 100.60901833\n", + " 94.05543837 79.249868 53.99237577 65.56806674 73.41094873\n", + " 99.16436384 101.13093832 93.03205657 99.69871153 101.89878035\n", + " 85.80537355 86.84879743 86.214422 99.49009009 81.44740104\n", + " 56.29921017 61.5624775 67.23737161 66.83788963]' has dtype incompatible with float32, please explicitly cast to a compatible dtype first.\n", + " self._df.loc[existing_idx, all_cols] = df_norm.loc[existing_idx, all_cols]\n", + "/usr/local/lib/python3.12/dist-packages/weightslab/data/dataframe_manager.py:703: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '[ 64.49066497 65.37024572 104.59268596 52.93642734 72.02098046\n", + " 48.14319779 113.24922957 46.29628522 43.88088143 25.53077158\n", + " 60.70043395 49.99961312 32.48544889 87.93221913 36.88762984\n", + " 47.55134406 57.05124766 64.13736411 65.30893805 96.26249138\n", + " 38.45269062 42.4448374 35.35399698 49.62630278 46.2604104\n", + " 42.64606061 32.72595598 68.50939554 46.54847017 59.36240918\n", + " 55.71851252 58.31763177 50.1109307 61.99646905]' has dtype incompatible with float32, please explicitly cast to a compatible dtype first.\n", + " self._df.loc[existing_idx, all_cols] = df_norm.loc[existing_idx, all_cols]\n", + "/usr/local/lib/python3.12/dist-packages/weightslab/data/dataframe_manager.py:703: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '[0.65953828 0.61511859 0.63585766 0.62043339 0.63605741 0.58398832\n", + " 0.53613578 0.62181845 0.62159035 0.66207224 0.38428878 0.64687088\n", + " 0.52937324 0.5747199 0.54391107 0.61459091 0.61792182 0.55884669\n", + " 0.5585666 0.58468377 0.60382653 0.58175791 0.50977316 0.59231942\n", + " 0.5993173 0.63587023 0.63463003 0.58706704 0.4947014 0.59870562\n", + " 0.58584364 0.62449927 0.61637045 0.59844273]' has dtype incompatible with float32, please explicitly cast to a compatible dtype first.\n", + " self._df.loc[existing_idx, all_cols] = df_norm.loc[existing_idx, all_cols]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:33:35.148 INFO:weightslab.trainer.services.data_service:__init__: DataService initialized.\n", + "16/07/2026-08:33:35.150 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Servicer added\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\r[PreviewCache]: 0%| | 0/170 [00:00 16 (Loader: train_loader)\n", + "16/07/2026-08:40:17.818 INFO:weightslab.components.global_monitoring:resume: \n", + "Training resumed as modules hashes have been computed: ['bfb454d6', 'd39e7bd9', '43e44972'].\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 20%|█▉ | 296/1500 [06:41<33:32, 1.67s/it, iou=56.44%, test_loss=8.1024, train_loss=1.8979]\u001b[A\n", + "Training: 20%|█▉ | 297/1500 [06:41<10:23:18, 31.09s/it, iou=56.44%, test_loss=8.1024, train_loss=1.8979]\u001b[A\n", + "Training: 20%|█▉ | 297/1500 [06:41<10:23:18, 31.09s/it, iou=56.44%, test_loss=8.1024, train_loss=2.4030]\u001b[A\n", + "Training: 20%|█▉ | 298/1500 [06:41<7:18:34, 21.89s/it, iou=56.44%, test_loss=8.1024, train_loss=2.4030] \u001b[A\n", + "Training: 20%|█▉ | 298/1500 [06:42<7:18:34, 21.89s/it, iou=56.44%, test_loss=8.1024, train_loss=1.9995]\u001b[A\n", + "Training: 20%|█▉ | 299/1500 [06:42<5:11:45, 15.57s/it, iou=56.44%, test_loss=8.1024, train_loss=1.9995]\u001b[A\n", + "Training: 20%|█▉ | 299/1500 [06:42<5:11:45, 15.57s/it, iou=56.44%, test_loss=8.1024, train_loss=1.9483]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:20.402 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: bfb454d6d39e7bd943e44972_step_000300.pt\n", + "16/07/2026-08:40:20.588 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 20%|█▉ | 299/1500 [06:43<5:11:45, 15.57s/it, iou=56.44%, test_loss=8.1024, train_loss=2.3940]\u001b[A\n", + "Training: 20%|██ | 301/1500 [06:43<2:51:19, 8.57s/it, iou=56.44%, test_loss=8.1024, train_loss=2.3940]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:21.165 INFO:weightslab.backend.dataloader_interface:set_batch_size: Batch size updated: 8 -> 16 (Loader: test_loader)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:23.499 INFO:weightslab.src:write_dataframe: write_dataframe: wrote 593 row(s) × 24 column(s) as json to /tmp/tmpf35sd4_z/d8e01c87_dataframe.json\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 20%|██ | 301/1500 [06:46<2:51:19, 8.57s/it, iou=33.90%, test_loss=4.7464, train_loss=1.9816]\u001b[A\n", + "Training: 20%|██ | 302/1500 [06:46<2:22:34, 7.14s/it, iou=33.90%, test_loss=4.7464, train_loss=1.9816]\u001b[A\n", + "Training: 20%|██ | 302/1500 [06:46<2:22:34, 7.14s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8739]\u001b[A\n", + "Training: 20%|██ | 303/1500 [06:46<1:47:37, 5.39s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8739]\u001b[A\n", + "Training: 20%|██ | 303/1500 [06:46<1:47:37, 5.39s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8943]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 20%|██ | 303/1500 [06:47<1:47:37, 5.39s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8743]\u001b[A\n", + "Training: 20%|██ | 305/1500 [06:47<1:03:34, 3.19s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8743]\u001b[A\n", + "Training: 20%|██ | 305/1500 [06:47<1:03:34, 3.19s/it, iou=33.90%, test_loss=4.7464, train_loss=1.9137]\u001b[A\n", + "Training: 20%|██ | 306/1500 [06:47<50:33, 2.54s/it, iou=33.90%, test_loss=4.7464, train_loss=1.9137] \u001b[A\n", + "Training: 20%|██ | 306/1500 [06:48<50:33, 2.54s/it, iou=33.90%, test_loss=4.7464, train_loss=2.4774]\u001b[A\n", + "Training: 20%|██ | 307/1500 [06:48<42:26, 2.13s/it, iou=33.90%, test_loss=4.7464, train_loss=2.4774]\u001b[A\n", + "Training: 20%|██ | 307/1500 [06:48<42:26, 2.13s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8858]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 20%|██ | 307/1500 [06:49<42:26, 2.13s/it, iou=33.90%, test_loss=4.7464, train_loss=1.9598]\u001b[A\n", + "Training: 21%|██ | 309/1500 [06:49<26:52, 1.35s/it, iou=33.90%, test_loss=4.7464, train_loss=1.9598]\u001b[A\n", + "Training: 21%|██ | 309/1500 [06:49<26:52, 1.35s/it, iou=33.90%, test_loss=4.7464, train_loss=2.3849]\u001b[A\n", + "Training: 21%|██ | 310/1500 [06:49<22:41, 1.14s/it, iou=33.90%, test_loss=4.7464, train_loss=2.3849]\u001b[A\n", + "Training: 21%|██ | 310/1500 [06:50<22:41, 1.14s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8953]\u001b[A\n", + "Training: 21%|██ | 311/1500 [06:50<21:04, 1.06s/it, iou=33.90%, test_loss=4.7464, train_loss=1.8953]\u001b[A\n", + "Training: 21%|██ | 311/1500 [06:50<21:04, 1.06s/it, iou=33.90%, test_loss=4.7464, train_loss=1.9958]\u001b[A\n", + "Training: 21%|██ | 312/1500 [06:50<16:32, 1.20it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9958]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 21%|██ | 312/1500 [06:51<16:32, 1.20it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8536]\u001b[A\n", + "Training: 21%|██ | 313/1500 [06:51<14:17, 1.38it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8536]\u001b[A\n", + "Training: 21%|██ | 313/1500 [06:51<14:17, 1.38it/s, iou=33.90%, test_loss=4.7464, train_loss=2.3955]\u001b[A\n", + "Training: 21%|██ | 314/1500 [06:51<13:25, 1.47it/s, iou=33.90%, test_loss=4.7464, train_loss=2.3955]\u001b[A\n", + "Training: 21%|██ | 314/1500 [06:52<13:25, 1.47it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9691]\u001b[A\n", + "Training: 21%|██ | 315/1500 [06:52<16:04, 1.23it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9691]\u001b[A\n", + "Training: 21%|██ | 315/1500 [06:53<16:04, 1.23it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8806]\u001b[A\n", + "Training: 21%|██ | 316/1500 [06:53<13:43, 1.44it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8806]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 21%|██ | 316/1500 [06:54<13:43, 1.44it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8700]\u001b[A\n", + "Training: 21%|██ | 317/1500 [06:54<17:55, 1.10it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8700]\u001b[A\n", + "Training: 21%|██ | 317/1500 [06:55<17:55, 1.10it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9674]\u001b[A\n", + "Training: 21%|██ | 318/1500 [06:55<16:26, 1.20it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9674]\u001b[A\n", + "Training: 21%|██ | 318/1500 [06:56<16:26, 1.20it/s, iou=33.90%, test_loss=4.7464, train_loss=2.3859]\u001b[A\n", + "Training: 21%|██▏ | 319/1500 [06:56<18:06, 1.09it/s, iou=33.90%, test_loss=4.7464, train_loss=2.3859]\u001b[A\n", + "Training: 21%|██▏ | 319/1500 [06:56<18:06, 1.09it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8463]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 21%|██▏ | 319/1500 [06:56<18:06, 1.09it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9668]\u001b[A\n", + "Training: 21%|██▏ | 321/1500 [06:56<11:56, 1.64it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9668]\u001b[A\n", + "Training: 21%|██▏ | 321/1500 [06:57<11:56, 1.64it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8672]\u001b[A\n", + "Training: 21%|██▏ | 322/1500 [06:57<11:02, 1.78it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8672]\u001b[A\n", + "Training: 21%|██▏ | 322/1500 [06:58<11:02, 1.78it/s, iou=33.90%, test_loss=4.7464, train_loss=2.3978]\u001b[A\n", + "Training: 22%|██▏ | 323/1500 [06:58<12:20, 1.59it/s, iou=33.90%, test_loss=4.7464, train_loss=2.3978]\u001b[A\n", + "Training: 22%|██▏ | 323/1500 [06:58<12:20, 1.59it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9003]\u001b[A\n", + "Training: 22%|██▏ | 324/1500 [06:58<09:58, 1.96it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9003]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 22%|██▏ | 324/1500 [06:58<09:58, 1.96it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8884]\u001b[A\n", + "Training: 22%|██▏ | 325/1500 [06:58<09:27, 2.07it/s, iou=33.90%, test_loss=4.7464, train_loss=1.8884]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:36.225 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: bfb454d6d39e7bd943e44972_step_000325.pt\n", + "16/07/2026-08:40:36.404 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 22%|██▏ | 325/1500 [06:59<09:27, 2.07it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9650]\u001b[A\n", + "Training: 22%|██▏ | 326/1500 [06:59<10:33, 1.85it/s, iou=33.90%, test_loss=4.7464, train_loss=1.9650]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:38.855 INFO:weightslab.components.global_monitoring:pause: \n", + "Training paused.\n", + "16/07/2026-08:40:38.862 INFO:weightslab.trainer.services.experiment_service:ExperimentCommand: [WeightsLab] UI Command: HP changed, experiment paused!\n", + "16/07/2026-08:40:38.865 INFO:weightslab.trainer.services.experiment_service:ExperimentCommand: \n", + "[WeightsLab] UI Command: PAUSE\n", + "16/07/2026-08:40:38.870 INFO:weightslab.components.global_monitoring:pause: \n", + "Training paused.\n", + "16/07/2026-08:40:39.412 INFO:weightslab.src:write_dataframe: write_dataframe: wrote 593 row(s) × 24 column(s) as json to /tmp/tmpf35sd4_z/d8e01c87_dataframe.json\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 22%|██▏ | 326/1500 [07:02<10:33, 1.85it/s, iou=54.03%, test_loss=7.9861, train_loss=2.4054]\u001b[A\n", + "Training: 22%|██▏ | 327/1500 [07:02<23:56, 1.22s/it, iou=54.03%, test_loss=7.9861, train_loss=2.4054]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:41.095 INFO:weightslab.trainer.services.experiment_service:RestoreCheckpoint: Restoring checkpoint from hash: 802319f761e1ba3dc3472a78 (weights-only, target_step=24)\n", + "16/07/2026-08:40:41.096 INFO:weightslab.trainer.services.experiment_service:RestoreCheckpoint: Pausing training before restore...\n", + "16/07/2026-08:40:41.099 INFO:weightslab.components.global_monitoring:pause: \n", + "Training paused.\n", + "16/07/2026-08:40:41.102 INFO:weightslab.components.checkpoint_manager:load_state: \n", + "============================================================\n", + "16/07/2026-08:40:41.103 INFO:weightslab.components.checkpoint_manager:load_state: Loading and applying state: 802319f761e1ba3d...\n", + "16/07/2026-08:40:41.104 INFO:weightslab.components.checkpoint_manager:load_state: ============================================================\n", + "16/07/2026-08:40:41.110 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loading checkpoint 802319f761e1ba3d...\n", + "16/07/2026-08:40:41.112 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Target: HP=802319f7 MODEL=61e1ba3d DATA=c3472a78\n", + "16/07/2026-08:40:41.115 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Current: HP=bfb454d6 MODEL=d39e7bd9 DATA=43e44972\n", + "16/07/2026-08:40:41.118 WARNING:weightslab.components.checkpoint_manager:load_checkpoint: [WARNING] Model architecture file not found: /tmp/tmpf35sd4_z/checkpoints/models/61e1ba3d/61e1ba3d_architecture.pkl\n", + "16/07/2026-08:40:41.176 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [OK] Loaded weights from step 25 with RNG state\n", + "16/07/2026-08:40:41.184 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [OK] Loaded config (hash changed)\n", + "16/07/2026-08:40:41.198 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [OK] Loaded data snapshot (170 rows) with RNG state\n", + "16/07/2026-08:40:41.200 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loaded components: {'data', 'weights', 'config'}\n", + "16/07/2026-08:40:41.215 INFO:weightslab.components.checkpoint_manager:load_state: [OK] Applied weights to existing model (step 25)\n", + "16/07/2026-08:40:41.217 INFO:weightslab.components.checkpoint_manager:load_state: Loaded optimizer state\n", + "16/07/2026-08:40:41.219 INFO:weightslab.components.checkpoint_manager:load_state: [OK] Applied hyperparameters config\n", + "16/07/2026-08:40:41.254 INFO:weightslab.components.checkpoint_manager:load_state: [OK] Applied data snapshot (170 rows)\n", + "16/07/2026-08:40:41.266 INFO:weightslab.components.checkpoint_manager:load_state: \n", + "[OK] Successfully loaded and applied state: 802319f761e1ba3d\n", + "16/07/2026-08:40:41.267 INFO:weightslab.components.checkpoint_manager:load_state: ============================================================\n", + "\n", + "16/07/2026-08:40:41.272 INFO:weightslab.trainer.services.experiment_service:RestoreCheckpoint: Successfully restored checkpoint: 802319f761e1ba3dc3472a78\n", + "16/07/2026-08:40:44.091 INFO:weightslab.components.global_monitoring:pause: \n", + "Training paused.\n", + "16/07/2026-08:40:44.092 INFO:weightslab.trainer.services.experiment_service:ExperimentCommand: [WeightsLab] UI Command: HP changed, experiment paused!\n", + "16/07/2026-08:40:44.093 INFO:weightslab.trainer.services.experiment_service:ExperimentCommand: \n", + "[WeightsLab] UI Command: RESUME\n", + "16/07/2026-08:40:44.096 INFO:weightslab.components.global_monitoring:resume: \n", + "Attempting to resume training...\n", + "16/07/2026-08:40:44.104 INFO:weightslab.components.experiment_hash:has_changed: Experiment configuration changed: {'model'}\n", + "16/07/2026-08:40:44.107 INFO:weightslab.components.experiment_hash:generate_hash: Generated experiment hash: 802319f7f5ee131bc3472a78- (HP: 802319f7, Model: f5ee131b, Data: c3472a78)\n", + "16/07/2026-08:40:44.111 INFO:weightslab.components.checkpoint_manager:update_experiment_hash: New experiment hash: 802319f7-f5ee131b-c3472a78 (previous: 802319f7-61e1ba3d-c3472a78)\n", + "16/07/2026-08:40:44.113 INFO:weightslab.components.checkpoint_manager:update_experiment_hash: Changed components: {'model'}\n", + "16/07/2026-08:40:44.115 INFO:weightslab.components.checkpoint_manager:update_experiment_hash: Changes pending (not dumped yet): {'model'}\n", + "16/07/2026-08:40:44.118 INFO:weightslab.components.checkpoint_manager:save_pending_changes: Dumping pending changes: {'model'}\n", + "16/07/2026-08:40:44.427 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n", + "16/07/2026-08:40:44.434 INFO:weightslab.components.global_monitoring:resume: Hashes by module: ['802319f7', 'f5ee131b', 'c3472a78']\n", + "16/07/2026-08:40:44.437 INFO:weightslab.components.global_monitoring:resume: Resuming training now...\n", + "16/07/2026-08:40:44.438 INFO:weightslab.components.global_monitoring:resume: Hashes by module on resume: ['802319f7', 'f5ee131b', 'c3472a78']\n", + "16/07/2026-08:40:44.448 INFO:weightslab.backend.dataloader_interface:set_batch_size: Batch size updated: 16 -> 8 (Loader: train_loader)\n", + "16/07/2026-08:40:44.452 INFO:weightslab.components.global_monitoring:resume: \n", + "Training resumed as modules hashes have been computed: ['802319f7', 'f5ee131b', 'c3472a78'].\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 22%|██▏ | 327/1500 [07:07<23:56, 1.22s/it, iou=54.03%, test_loss=7.9861, train_loss=10.4120]\u001b[A\n", + "Training: 22%|██▏ | 328/1500 [07:07<48:08, 2.46s/it, iou=54.03%, test_loss=7.9861, train_loss=10.4120]\u001b[A\n", + "Training: 22%|██▏ | 328/1500 [07:08<48:08, 2.46s/it, iou=54.03%, test_loss=7.9861, train_loss=8.1298] \u001b[A\n", + "Training: 22%|██▏ | 329/1500 [07:08<36:27, 1.87s/it, iou=54.03%, test_loss=7.9861, train_loss=8.1298]\u001b[A\n", + "Training: 22%|██▏ | 329/1500 [07:08<36:27, 1.87s/it, iou=54.03%, test_loss=7.9861, train_loss=8.6498]\u001b[A\n", + "Training: 22%|██▏ | 330/1500 [07:08<27:35, 1.41s/it, iou=54.03%, test_loss=7.9861, train_loss=8.6498]\u001b[A\n", + "Training: 22%|██▏ | 330/1500 [07:09<27:35, 1.41s/it, iou=54.03%, test_loss=7.9861, train_loss=8.4801]\u001b[A\n", + "Training: 22%|██▏ | 331/1500 [07:09<22:37, 1.16s/it, iou=54.03%, test_loss=7.9861, train_loss=8.4801]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:48.449 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: 802319f7f5ee131bc3472a78_step_000030.pt\n", + "16/07/2026-08:40:48.825 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 22%|██▏ | 331/1500 [07:11<22:37, 1.16s/it, iou=54.03%, test_loss=7.9861, train_loss=9.8237]\u001b[A\n", + "Training: 22%|██▏ | 332/1500 [07:11<31:52, 1.64s/it, iou=54.03%, test_loss=7.9861, train_loss=9.8237]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:51.421 INFO:weightslab.backend.dataloader_interface:set_batch_size: Batch size updated: 16 -> 8 (Loader: test_loader)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:40:56.551 WARNING:weightslab.watchdog.grpc_watchdog:unary_unary_wrapper: [gRPC] /ExperimentService/GetDataSamples completed in 3558.8ms\n", + "16/07/2026-08:41:00.986 INFO:weightslab.src:write_dataframe: write_dataframe: wrote 593 row(s) × 24 column(s) as json to /tmp/tmpf35sd4_z/d8e01c87_dataframe.json\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 22%|██▏ | 332/1500 [07:23<31:52, 1.64s/it, iou=90.43%, test_loss=15.9695, train_loss=9.6350]\u001b[A\n", + "Training: 22%|██▏ | 333/1500 [07:23<1:32:06, 4.74s/it, iou=90.43%, test_loss=15.9695, train_loss=9.6350]\u001b[A\n", + "Training: 22%|██▏ | 333/1500 [07:24<1:32:06, 4.74s/it, iou=90.43%, test_loss=15.9695, train_loss=7.9581]\u001b[A\n", + "Training: 22%|██▏ | 334/1500 [07:24<1:06:21, 3.41s/it, iou=90.43%, test_loss=15.9695, train_loss=7.9581]\u001b[A\n", + "Training: 22%|██▏ | 334/1500 [07:24<1:06:21, 3.41s/it, iou=90.43%, test_loss=15.9695, train_loss=7.3944]\u001b[A\n", + "Training: 22%|██▏ | 335/1500 [07:24<47:55, 2.47s/it, iou=90.43%, test_loss=15.9695, train_loss=7.3944] \u001b[A\n", + "Training: 22%|██▏ | 335/1500 [07:24<47:55, 2.47s/it, iou=90.43%, test_loss=15.9695, train_loss=10.4550]\u001b[A\n", + "Training: 22%|██▏ | 336/1500 [07:24<35:17, 1.82s/it, iou=90.43%, test_loss=15.9695, train_loss=10.4550]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:02.160 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: 802319f7f5ee131bc3472a78_step_000035.pt\n", + "16/07/2026-08:41:02.346 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 22%|██▏ | 336/1500 [07:25<35:17, 1.82s/it, iou=90.43%, test_loss=15.9695, train_loss=7.6845] \u001b[A\n", + "Training: 22%|██▏ | 337/1500 [07:25<29:14, 1.51s/it, iou=90.43%, test_loss=15.9695, train_loss=7.6845]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:05.682 INFO:weightslab.src:write_dataframe: write_dataframe: wrote 593 row(s) × 24 column(s) as json to /tmp/tmpf35sd4_z/d8e01c87_dataframe.json\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 22%|██▏ | 337/1500 [07:28<29:14, 1.51s/it, iou=55.45%, test_loss=9.3424, train_loss=6.8015] \u001b[A\n", + "Training: 23%|██▎ | 338/1500 [07:28<38:06, 1.97s/it, iou=55.45%, test_loss=9.3424, train_loss=6.8015]\u001b[A\n", + "Training: 23%|██▎ | 338/1500 [07:28<38:06, 1.97s/it, iou=55.45%, test_loss=9.3424, train_loss=8.4716]\u001b[A\n", + "Training: 23%|██▎ | 339/1500 [07:28<28:11, 1.46s/it, iou=55.45%, test_loss=9.3424, train_loss=8.4716]\u001b[A\n", + "Training: 23%|██▎ | 339/1500 [07:29<28:11, 1.46s/it, iou=55.45%, test_loss=9.3424, train_loss=8.7423]\u001b[A\n", + "Training: 23%|██▎ | 340/1500 [07:29<21:16, 1.10s/it, iou=55.45%, test_loss=9.3424, train_loss=8.7423]\u001b[A\n", + "Training: 23%|██▎ | 340/1500 [07:29<21:16, 1.10s/it, iou=55.45%, test_loss=9.3424, train_loss=8.9636]\u001b[A\n", + "Training: 23%|██▎ | 341/1500 [07:29<16:33, 1.17it/s, iou=55.45%, test_loss=9.3424, train_loss=8.9636]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:06.887 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: 802319f7f5ee131bc3472a78_step_000040.pt\n", + "16/07/2026-08:41:07.490 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 23%|██▎ | 341/1500 [07:30<16:33, 1.17it/s, iou=55.45%, test_loss=9.3424, train_loss=8.2650]\u001b[A\n", + "Training: 23%|██▎ | 342/1500 [07:30<17:36, 1.10it/s, iou=55.45%, test_loss=9.3424, train_loss=8.2650]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:10.072 INFO:weightslab.src:write_dataframe: write_dataframe: wrote 593 row(s) × 24 column(s) as json to /tmp/tmpf35sd4_z/d8e01c87_dataframe.json\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 23%|██▎ | 342/1500 [07:32<17:36, 1.10it/s, iou=57.76%, test_loss=8.9165, train_loss=9.8330]\u001b[A\n", + "Training: 23%|██▎ | 343/1500 [07:32<26:56, 1.40s/it, iou=57.76%, test_loss=8.9165, train_loss=9.8330]\u001b[A\n", + "Training: 23%|██▎ | 343/1500 [07:33<26:56, 1.40s/it, iou=57.76%, test_loss=8.9165, train_loss=8.5029]\u001b[A\n", + "Training: 23%|██▎ | 344/1500 [07:33<20:40, 1.07s/it, iou=57.76%, test_loss=8.9165, train_loss=8.5029]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n", + "\n", + "Training: 23%|██▎ | 344/1500 [07:33<20:40, 1.07s/it, iou=57.76%, test_loss=8.9165, train_loss=6.6450]\u001b[A\n", + "Training: 23%|██▎ | 345/1500 [07:33<16:02, 1.20it/s, iou=57.76%, test_loss=8.9165, train_loss=6.6450]\u001b[A\n", + "Training: 23%|██▎ | 345/1500 [07:33<16:02, 1.20it/s, iou=57.76%, test_loss=8.9165, train_loss=7.5255]\u001b[A\n", + "Training: 23%|██▎ | 346/1500 [07:33<13:05, 1.47it/s, iou=57.76%, test_loss=8.9165, train_loss=7.5255]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:11.459 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: 802319f7f5ee131bc3472a78_step_000045.pt\n", + "16/07/2026-08:41:11.769 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 23%|██▎ | 346/1500 [07:34<13:05, 1.47it/s, iou=57.76%, test_loss=8.9165, train_loss=7.0646]\u001b[A\n", + "Training: 23%|██▎ | 347/1500 [07:34<15:19, 1.25it/s, iou=57.76%, test_loss=8.9165, train_loss=7.0646]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:16.032 INFO:weightslab.src:write_dataframe: write_dataframe: wrote 593 row(s) × 24 column(s) as json to /tmp/tmpf35sd4_z/d8e01c87_dataframe.json\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 23%|██▎ | 347/1500 [07:38<15:19, 1.25it/s, iou=58.16%, test_loss=8.7419, train_loss=8.9887]\u001b[A\n", + "Training: 23%|██▎ | 348/1500 [07:38<33:36, 1.75s/it, iou=58.16%, test_loss=8.7419, train_loss=8.9887]\u001b[A\n", + "Training: 23%|██▎ | 348/1500 [07:39<33:36, 1.75s/it, iou=58.16%, test_loss=8.7419, train_loss=7.1660]\u001b[A\n", + "Training: 23%|██▎ | 349/1500 [07:39<25:01, 1.30s/it, iou=58.16%, test_loss=8.7419, train_loss=7.1660]\u001b[A\n", + "Training: 23%|██▎ | 349/1500 [07:39<25:01, 1.30s/it, iou=58.16%, test_loss=8.7419, train_loss=7.0642]\u001b[A\n", + "Training: 23%|██▎ | 350/1500 [07:39<18:58, 1.01it/s, iou=58.16%, test_loss=8.7419, train_loss=7.0642]\u001b[A\n", + "Training: 23%|██▎ | 350/1500 [07:39<18:58, 1.01it/s, iou=58.16%, test_loss=8.7419, train_loss=5.4376]\u001b[A\n", + "Training: 23%|██▎ | 351/1500 [07:39<14:41, 1.30it/s, iou=58.16%, test_loss=8.7419, train_loss=5.4376]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:17.068 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: 802319f7f5ee131bc3472a78_step_000050.pt\n", + "16/07/2026-08:41:17.255 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 23%|██▎ | 351/1500 [07:40<14:41, 1.30it/s, iou=58.16%, test_loss=8.7419, train_loss=8.3300]\u001b[A\n", + "Training: 23%|██▎ | 352/1500 [07:40<14:17, 1.34it/s, iou=58.16%, test_loss=8.7419, train_loss=8.3300]\u001b[A/usr/local/lib/python3.12/dist-packages/torch/utils/data/dataloader.py:668: UserWarning: 'pin_memory' argument is set as true but no accelerator is found, then device pinned memory won't be used.\n", + " warnings.warn(warn_msg)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:20.421 INFO:weightslab.src:write_dataframe: write_dataframe: wrote 593 row(s) × 24 column(s) as json to /tmp/tmpf35sd4_z/d8e01c87_dataframe.json\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 23%|██▎ | 352/1500 [07:43<14:17, 1.34it/s, iou=58.39%, test_loss=8.7055, train_loss=6.9667]\u001b[A\n", + "Training: 24%|██▎ | 353/1500 [07:43<26:44, 1.40s/it, iou=58.39%, test_loss=8.7055, train_loss=6.9667]\u001b[A\n", + "Training: 24%|██▎ | 353/1500 [07:43<26:44, 1.40s/it, iou=58.39%, test_loss=8.7055, train_loss=5.6257]\u001b[A\n", + "Training: 24%|██▎ | 354/1500 [07:43<20:19, 1.06s/it, iou=58.39%, test_loss=8.7055, train_loss=5.6257]\u001b[A\n", + "Training: 24%|██▎ | 354/1500 [07:43<20:19, 1.06s/it, iou=58.39%, test_loss=8.7055, train_loss=6.9397]\u001b[A\n", + "Training: 24%|██▎ | 355/1500 [07:43<15:46, 1.21it/s, iou=58.39%, test_loss=8.7055, train_loss=6.9397]\u001b[A\n", + "Training: 24%|██▎ | 355/1500 [07:44<15:46, 1.21it/s, iou=58.39%, test_loss=8.7055, train_loss=7.1436]\u001b[A\n", + "Training: 24%|██▎ | 356/1500 [07:44<12:38, 1.51it/s, iou=58.39%, test_loss=8.7055, train_loss=7.1436]\u001b[A" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-08:41:21.337 INFO:weightslab.components.global_monitoring:pause: \n", + "Training paused.\n", + "16/07/2026-08:41:21.339 INFO:weightslab.trainer.services.experiment_service:ExperimentCommand: [WeightsLab] UI Command: HP changed, experiment paused!\n", + "16/07/2026-08:41:21.340 INFO:weightslab.trainer.services.experiment_service:ExperimentCommand: \n", + "[WeightsLab] UI Command: PAUSE\n", + "16/07/2026-08:41:21.342 INFO:weightslab.components.global_monitoring:pause: \n", + "Training paused.\n", + "16/07/2026-08:41:21.615 INFO:weightslab.components.checkpoint_manager:save_model_checkpoint: Saved model checkpoint: 802319f7f5ee131bc3472a78_step_000055.pt\n", + "16/07/2026-08:41:21.811 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmpf35sd4_z/checkpoints/loggers/loggers.manifest.json (1 chunks)\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Training: 24%|██▎ | 356/1500 [07:45<12:38, 1.51it/s, iou=58.39%, test_loss=8.7055, train_loss=6.4582]\u001b[A\n", + "Training: 24%|██▍ | 357/1500 [07:45<14:34, 1.31it/s, iou=58.39%, test_loss=8.7055, train_loss=6.4582]\u001b[A" + ] + } + ], + "source": [ + "# Start the background gRPC server + a public bore tunnel so a local Weights Studio can attach.\n", + "wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "# Flip the experiment into the training state, then run the loop.\n", + "wl.start_training(timeout=3)\n", + "\n", + "# training_steps_to_do is None (open-ended) in the example; cap the demo to a finite\n", + "# number of steps so the cell terminates on its own on Colab (raise for a longer run).\n", + "max_steps = config[\"training_steps_to_do\"] or 1500\n", + "eval_ratio = config[\"eval_full_to_train_steps_ratio\"]\n", + "export_ratio = config[\"experiment_dump_to_train_steps_ratio\"]\n", + "\n", + "test_loss, test_metric = None, None\n", + "pbar = tqdm.tqdm(range(max_steps), desc=\"Training\")\n", + "for train_step in pbar:\n", + " age = model.get_age() if hasattr(model, \"get_age\") else train_step\n", + "\n", + " # Train one step.\n", + " train_loss = train(train_loader, model, optimizer, train_sig, device, grid_size, conf_thresh)\n", + "\n", + " # Full eval pass every eval_ratio steps.\n", + " if age == 0 or age % eval_ratio == 0:\n", + " test_loader_len = len(test_loader)\n", + " test_loss, test_metric = test(test_loader, model, test_sig, device, grid_size, conf_thresh, test_loader_len)\n", + "\n", + " # Export per-sample history + dataframe periodically.\n", + " if age > 0 and age % export_ratio == 0:\n", + " wl.write_history()\n", + " wl.write_dataframe()\n", + "\n", + " pbar.set_postfix(\n", + " train_loss=f\"{train_loss:.4f}\",\n", + " test_loss=f\"{test_loss:.4f}\" if test_loss is not None else \"N/A\",\n", + " iou=f\"{test_metric:.2f}%\" if test_metric is not None else \"N/A\",\n", + " )\n", + "\n", + "wl.write_history()\n", + "wl.write_dataframe()\n", + "print(\"Training complete. Logs at:\", log_dir)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j9TYziOc_xBg" + }, + "source": [ + "## See it live in Weights Studio\n", + "\n", + "**Colab has no Docker daemon**, so run Studio on your own machine and point it at this notebook's\n", + "backend using the `bore.pub:` printed in the Expose section:\n", + "\n", + "```bash\n", + "pip install weightslab\n", + "weightslab ui launch # Terminal 1 - opens http://localhost:5173\n", + "weightslab tunnel bore.pub:12345 # Terminal 2 - the host:port from the Expose section\n", + "```\n", + "\n", + "Then open **http://localhost:5173**. Prefer local-only? Run this example directly on your machine\n", + "(`weightslab start example` selects a bundled example) and launch the UI next to it - no tunnel." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oQsjwjg7_xBg" + }, + "source": [ + "---\n", + "\n", + "
\n", + "Crafted by GrayboxTech - if WeightsLab helps you catch a bad label, drop us a star on GitHub.\n", + "
" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "ws-detection.ipynb", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Object Detection (Penn-Fudan) with WeightsLab\n", - "\n", - "Trains a small detector (MobileNetV3-Small backbone + detection head) on the **Penn-Fudan** pedestrian dataset, tracking per-sample detection loss and per-instance IoU so each predicted box is traceable in Studio.\n", - "\n", - "> Data: The Penn-Fudan dataset (~170 images) is **downloaded on first run** under the example's `data/`.\n", - "\n", - "Unlike the self-contained classification demo, this example uses local helper modules\n", - "(`utils/`) and bundled/downloaded data, so the notebook **clones the repo** and runs the\n", - "example in place." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "On Colab, pick a **T4 GPU** runtime (`Runtime -> Change runtime type -> T4 GPU`)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\"PyPI\n", - "\"PyPI\n", - "\"PyPI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Clone the repo (for the example's utils/ + data) and install WeightsLab.\n", - "import os\n", - "if not os.path.isdir(\"weightslab\"):\n", - " !git clone --branch torch_collab_examples https://github.com/GrayboxTech/weightslab.git\n", - "%pip install -q ./weightslab\n", - "!pip install --upgrade \"protobuf>=6.31.1\"\n", - "%pip install \"torchvision>=0.16\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Move into the example directory so its `utils/` and config.yaml resolve.\n", - "%cd weightslab/weightslab/examples/PyTorch/ws-detection\n", - "# Install the example's own extra requirements, if any.\n", - "import os\n", - "if os.path.exists(\"requirements.txt\"):\n", - " !pip install -q -r requirements.txt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration\n", - "\n", - "Every tunable lives in **`config.yaml`** (already commented). We load it into a `config` dict\n", - "so you can tweak values here; the changes are written back before training." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml\n", - "\n", - "with open(\"config.yaml\") as f:\n", - " config = yaml.safe_load(f)\n", - "\n", - "# --- tweak any parameter here, e.g. ---\n", - "# config[\"eval_full_to_train_steps_ratio\"] = 5\n", - "config[\"serving_grpc\"] = True # expose the gRPC backend for Weights Studio\n", - "\n", - "with open(\"config.yaml\", \"w\") as f:\n", - " yaml.safe_dump(config, f, sort_keys=False)\n", - "\n", - "print(yaml.safe_dump(config, sort_keys=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## (Optional) Expose the backend for the live UI\n", - "\n", - "To watch training **live in Weights Studio on your own machine**, run this cell first (it opens a\n", - "raw-TCP [`bore`](https://github.com/ekzhang/bore) tunnel over the free public relay `bore.pub` -\n", - "**no signup, no card**), then start training below.\n", - "\n", - "> `bore.pub` is a shared public relay; the random port is the only thing protecting your endpoint.\n", - "> Fine for a demo, not for sensitive data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os, re, tarfile, threading, urllib.request, subprocess\n", - "\n", - "EXPOSE_UI = True # set False for a headless run (no tunnel)\n", - "BACKEND_PORT = 50051 # gRPC port to forward\n", - "\n", - "endpoint = None\n", - "if EXPOSE_UI:\n", - " bore = os.path.join(os.getcwd(), \"bore\")\n", - " if not os.path.exists(bore):\n", - " BORE = \"v0.6.0\"\n", - " urllib.request.urlretrieve(\n", - " f\"https://github.com/ekzhang/bore/releases/download/{BORE}/bore-{BORE}-x86_64-unknown-linux-musl.tar.gz\",\n", - " \"bore.tar.gz\")\n", - " with tarfile.open(\"bore.tar.gz\") as t:\n", - " t.extractall()\n", - " os.chmod(bore, 0o755)\n", - "\n", - " proc = subprocess.Popen(\n", - " [bore, \"local\", str(BACKEND_PORT), \"--to\", \"bore.pub\"],\n", - " stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)\n", - " for line in proc.stdout:\n", - " m = re.search(r\"bore\\.pub:(\\d+)\", line)\n", - " if m:\n", - " endpoint = f\"bore.pub:{m.group(1)}\"\n", - " break\n", - " threading.Thread(target=lambda: [None for _ in proc.stdout], daemon=True).start()\n", - "\n", - " print(\"=\" * 60)\n", - " print(\"Backend exposed at:\", endpoint)\n", - " print(\"On your OWN machine (Docker running), run:\")\n", - " print(\" weightslab ui launch\")\n", - " print(f\" weightslab tunnel {endpoint}\")\n", - " print(\"=\" * 60)\n", - "else:\n", - " print(\"EXPOSE_UI is False - no tunnel.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Train\n", - "\n", - "This runs the example's `main.py`, which wraps the model / optimizer / loaders / losses with\n", - "`wl.watch_or_edit(...)`, starts the gRPC backend, and trains while streaming per-sample signals.\n", - "\n", - "> This example trains **open-ended** (until you interrupt the cell). Interrupt the cell (stop button) to stop training; the exported history and\n", - "> dataframe are written to a temp `root_log_dir` (printed at startup)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python main.py" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## See it live in Weights Studio\n", - "\n", - "**Colab has no Docker daemon**, so run Studio on your own machine and point it at this notebook's\n", - "backend using the `bore.pub:` printed in the Expose section:\n", - "\n", - "```bash\n", - "pip install weightslab\n", - "weightslab ui launch # Terminal 1 - opens http://localhost:5173\n", - "weightslab tunnel bore.pub:12345 # Terminal 2 - the host:port from the Expose section\n", - "```\n", - "\n", - "Then open **http://localhost:5173**. Prefer local-only? Run this example directly on your machine\n", - "(`weightslab start example` selects a bundled example) and launch the UI next to it - no tunnel." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "
\n", - "Crafted by GrayboxTech - if WeightsLab helps you catch a bad label, drop us a star on GitHub.\n", - "
" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "ws-detection.ipynb", - "provenance": [], - "toc_visible": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 0 + "nbformat": 4, + "nbformat_minor": 0 } \ No newline at end of file diff --git a/weightslab/examples/Notebooks/PyTorch/ws-generation.ipynb b/weightslab/examples/Notebooks/PyTorch/ws-generation.ipynb deleted file mode 100644 index 7efea18e..00000000 --- a/weightslab/examples/Notebooks/PyTorch/ws-generation.ipynb +++ /dev/null @@ -1,241 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - " \n", - " \"WeightsLab\n", - "\n", - " \"License\"\n", - " \"Stars\"\n", - " \"Version\"\n", - "
\n", - " \"Open\n", - "\n", - " Welcome to the WeightsLab Image Generation (VAD U-Net) notebook! WeightsLab traces training signals back to the exact samples producing them. Browse the Docs for details.
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Image Generation (VAD U-Net) with WeightsLab\n", - "\n", - "Trains a U-Net-style generator and tracks per-sample reconstruction signals.\n", - "\n", - "> Data: **This example needs your own image dataset** - set `data_root` in the config to a folder of images before running (there is no bundled/downloaded dataset for it).\n", - "\n", - "Unlike the self-contained classification demo, this example uses local helper modules\n", - "(`utils/`) and bundled/downloaded data, so the notebook **clones the repo** and runs the\n", - "example in place." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "On Colab, pick a **T4 GPU** runtime (`Runtime -> Change runtime type -> T4 GPU`)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\"PyPI\n", - "\"PyPI\n", - "\"PyPI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Clone the repo (for the example's utils/ + data) and install WeightsLab.\n", - "import os\n", - "if not os.path.isdir(\"weightslab\"):\n", - " !git clone --branch torch_collab_examples https://github.com/GrayboxTech/weightslab.git\n", - "%pip install -q ./weightslab\n", - "!pip install --upgrade \"protobuf>=6.31.1\"\n", - "%pip install \"torchvision>=0.16\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Move into the example directory so its `utils/` and config.yaml resolve.\n", - "%cd weightslab/weightslab/examples/PyTorch/ws-generation\n", - "# Install the example's own extra requirements, if any.\n", - "import os\n", - "if os.path.exists(\"requirements.txt\"):\n", - " !pip install -q -r requirements.txt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration\n", - "\n", - "Every tunable lives in **`config.yaml`** (already commented). We load it into a `config` dict\n", - "so you can tweak values here; the changes are written back before training." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml\n", - "\n", - "with open(\"config.yaml\") as f:\n", - " config = yaml.safe_load(f)\n", - "\n", - "# --- tweak any parameter here, e.g. ---\n", - "# config[\"eval_full_to_train_steps_ratio\"] = 5\n", - "config[\"serving_grpc\"] = True # expose the gRPC backend for Weights Studio\n", - "\n", - "with open(\"config.yaml\", \"w\") as f:\n", - " yaml.safe_dump(config, f, sort_keys=False)\n", - "\n", - "print(yaml.safe_dump(config, sort_keys=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## (Optional) Expose the backend for the live UI\n", - "\n", - "To watch training **live in Weights Studio on your own machine**, run this cell first (it opens a\n", - "raw-TCP [`bore`](https://github.com/ekzhang/bore) tunnel over the free public relay `bore.pub` -\n", - "**no signup, no card**), then start training below.\n", - "\n", - "> `bore.pub` is a shared public relay; the random port is the only thing protecting your endpoint.\n", - "> Fine for a demo, not for sensitive data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os, re, tarfile, threading, urllib.request, subprocess\n", - "\n", - "EXPOSE_UI = True # set False for a headless run (no tunnel)\n", - "BACKEND_PORT = 50051 # gRPC port to forward\n", - "\n", - "endpoint = None\n", - "if EXPOSE_UI:\n", - " bore = os.path.join(os.getcwd(), \"bore\")\n", - " if not os.path.exists(bore):\n", - " BORE = \"v0.6.0\"\n", - " urllib.request.urlretrieve(\n", - " f\"https://github.com/ekzhang/bore/releases/download/{BORE}/bore-{BORE}-x86_64-unknown-linux-musl.tar.gz\",\n", - " \"bore.tar.gz\")\n", - " with tarfile.open(\"bore.tar.gz\") as t:\n", - " t.extractall()\n", - " os.chmod(bore, 0o755)\n", - "\n", - " proc = subprocess.Popen(\n", - " [bore, \"local\", str(BACKEND_PORT), \"--to\", \"bore.pub\"],\n", - " stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)\n", - " for line in proc.stdout:\n", - " m = re.search(r\"bore\\.pub:(\\d+)\", line)\n", - " if m:\n", - " endpoint = f\"bore.pub:{m.group(1)}\"\n", - " break\n", - " threading.Thread(target=lambda: [None for _ in proc.stdout], daemon=True).start()\n", - "\n", - " print(\"=\" * 60)\n", - " print(\"Backend exposed at:\", endpoint)\n", - " print(\"On your OWN machine (Docker running), run:\")\n", - " print(\" weightslab ui launch\")\n", - " print(f\" weightslab tunnel {endpoint}\")\n", - " print(\"=\" * 60)\n", - "else:\n", - " print(\"EXPOSE_UI is False - no tunnel.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Train\n", - "\n", - "This runs the example's `main.py`, which wraps the model / optimizer / loaders / losses with\n", - "`wl.watch_or_edit(...)`, starts the gRPC backend, and trains while streaming per-sample signals.\n", - "\n", - "> Training runs for `training_steps` (default 200000); interrupt the cell to stop early. Interrupt the cell (stop button) to stop training; the exported history and\n", - "> dataframe are written to a temp `root_log_dir` (printed at startup)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python main.py" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## See it live in Weights Studio\n", - "\n", - "**Colab has no Docker daemon**, so run Studio on your own machine and point it at this notebook's\n", - "backend using the `bore.pub:` printed in the Expose section:\n", - "\n", - "```bash\n", - "pip install weightslab\n", - "weightslab ui launch # Terminal 1 - opens http://localhost:5173\n", - "weightslab tunnel bore.pub:12345 # Terminal 2 - the host:port from the Expose section\n", - "```\n", - "\n", - "Then open **http://localhost:5173**. Prefer local-only? Run this example directly on your machine\n", - "(`weightslab start example` selects a bundled example) and launch the UI next to it - no tunnel." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "
\n", - "Crafted by GrayboxTech - if WeightsLab helps you catch a bad label, drop us a star on GitHub.\n", - "
" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "ws-generation.ipynb", - "provenance": [], - "toc_visible": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/PyTorch/ws-segmentation.ipynb b/weightslab/examples/Notebooks/PyTorch/ws-segmentation.ipynb index fc97f425..a8a97cc4 100644 --- a/weightslab/examples/Notebooks/PyTorch/ws-segmentation.ipynb +++ b/weightslab/examples/Notebooks/PyTorch/ws-segmentation.ipynb @@ -1,241 +1,1072 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - " \n", - " \"WeightsLab\n", - "\n", - " \"License\"\n", - " \"Stars\"\n", - " \"Version\"\n", - "
\n", - " \"Open\n", - "\n", - " Welcome to the WeightsLab Semantic Segmentation (BDD100k) notebook! WeightsLab traces training signals back to the exact samples producing them. Browse the Docs for details.
" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "06579270", + "metadata": { + "id": "06579270" + }, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Welcome to the WeightsLab Semantic Segmentation (BDD100k) notebook! WeightsLab traces training signals back to the exact samples producing them. Browse the Docs for details.
" + ] + }, + { + "cell_type": "markdown", + "id": "c122b485", + "metadata": { + "id": "c122b485" + }, + "source": [ + "# Semantic Segmentation (BDD100k) with WeightsLab\n", + "\n", + "Trains a small U-Net on a **BDD100k**-style dataset and instruments per-**sample** Dice (metric) and CrossEntropy (loss), so every mask's quality is traced back to the exact sample producing it.\n", + "\n", + "### What you'll do\n", + "1. Install WeightsLab.\n", + "2. Download your dataset **zip from a Google Drive link** and unzip it (the training **code is inlined** below).\n", + "3. Set every knob in one **config** dict (like a `config.yaml`).\n", + "4. Wrap the dataloaders, model, optimizer, and per-sample Dice + CrossEntropy signals with the SDK.\n", + "5. Train while per-sample signals are captured, then (optionally) open the live **Weights Studio** UI.\n", + "\n", + "> Data note: this notebook fetches your dataset from a **Drive share link** you provide (`DATASET_URL`); all training code (dataset, model, criterions, loops) is **inlined below** - no `main.py`, no full-repo clone. The zip should unpack to a BDD100k-style layout (`images/{train,val}/` + `labels/{train,val}/`), one semantic mask per image." + ] + }, + { + "cell_type": "markdown", + "id": "ec299273", + "metadata": { + "id": "ec299273" + }, + "source": [ + "## Setup\n", + "\n", + "On Colab, pick a **T4 GPU** runtime (`Runtime -> Change runtime type -> T4 GPU`)." + ] + }, + { + "cell_type": "markdown", + "id": "db7e968a", + "metadata": { + "id": "db7e968a" + }, + "source": [ + "\"PyPI\n", + "\"PyPI\n", + "\"PyPI" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ba87ca5", + "metadata": { + "id": "2ba87ca5" + }, + "outputs": [], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install -q weightslab\n", + "%pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev7\"\n", + "%pip install -q torchmetrics" + ] + }, + { + "cell_type": "markdown", + "id": "b07a3f07", + "metadata": { + "id": "b07a3f07" + }, + "source": [ + "### Fetch the dataset from Google Drive\n", + "\n", + "Set **`DATASET_URL`** below to your Drive **share link** for the dataset **zip** (the file must be shared as *Anyone with the link*). The cell downloads it with [`gdown`](https://github.com/wkentaro/gdown), extracts it, and auto-detects the dataset root — the folder that contains an `images/` subdirectory (so it works even if your zip wraps everything in a top-level folder). `config[\"data_root\"]` then points at that folder.\n", + "\n", + "The zip should unpack to the BDD100k layout the dataset class expects:\n", + "\n", + "```\n", + "/\n", + " images/{train,val}/ # .jpg / .png inputs\n", + " labels/{train,val}/ # matching .png masks (same basename as the image)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe6b5769", + "metadata": { + "id": "fe6b5769" + }, + "outputs": [], + "source": [ + "# ===== Dataset — local copy preferred (byte-identical to ws-seg-wow-moment-bdd), Drive as fallback =====\n", + "# This notebook is set up to reproduce the \"ws-seg-wow-moment-bdd\" experiment.\n", + "# If that experiment's exact dataset is available locally, use it directly so\n", + "# results are directly comparable. Otherwise (e.g. on Colab) fall back to the\n", + "# Drive zip below (a smaller toy dataset with the same layout/format).\n", + "LOCAL_DATA_ROOT = \"/home/ubuntu/guillaume_plg/data/bdd/bdd8k\" # matches ws-seg-wow-moment-bdd/config.yaml's data_root\n", + "\n", + "# Paste your Drive SHARE link for the dataset zip here (shared as \"Anyone with the link\").\n", + "DATASET_URL = \"https://drive.google.com/file/d/1rXNsROVeneXp91VccOGIjtYWkkhWECBb/view?usp=sharing\"\n", + "\n", + "import os, sys, subprocess, zipfile\n", + "\n", + "\n", + "def _find_data_root(base):\n", + " \"\"\"Return the folder that holds an `images/` subdir (handles a wrapping top-level dir).\"\"\"\n", + " if os.path.isdir(os.path.join(base, \"images\")):\n", + " return base\n", + " for d, subdirs, _ in os.walk(base):\n", + " if \"images\" in subdirs:\n", + " return d\n", + " return base\n", + "\n", + "\n", + "if os.path.isdir(os.path.join(LOCAL_DATA_ROOT, \"images\")):\n", + " print(f\"Found the ws-seg-wow-moment-bdd dataset locally at {LOCAL_DATA_ROOT} — using it directly.\")\n", + " DATA_ROOT = LOCAL_DATA_ROOT\n", + "else:\n", + " # gdown handles Drive downloads (incl. the large-file virus-scan confirmation).\n", + " subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"gdown\"], check=True)\n", + " import gdown\n", + "\n", + " zip_path = \"dataset.zip\"\n", + " extract_dir = \"dataset\"\n", + "\n", + " # Download (fuzzy=True lets gdown accept a full \"file/d//view\" share URL).\n", + " if not os.path.exists(zip_path):\n", + " gdown.download(url=DATASET_URL, output=zip_path, quiet=False, fuzzy=True)\n", + " else:\n", + " print(f\"{zip_path} already present — skipping download.\")\n", + "\n", + " # Extract.\n", + " with zipfile.ZipFile(zip_path) as zf:\n", + " zf.extractall(extract_dir)\n", + "\n", + " DATA_ROOT = _find_data_root(extract_dir)\n", + "\n", + "print(\"Dataset root:\", DATA_ROOT)\n", + "print(\"Contents:\", sorted(os.listdir(DATA_ROOT)))\n", + "for _split in (\"train\", \"val\", \"test\"):\n", + " _p = os.path.join(DATA_ROOT, \"images\", _split)\n", + " if os.path.isdir(_p):\n", + " print(f\" images/{_split}: {len(os.listdir(_p))} files\")" + ] + }, + { + "cell_type": "markdown", + "id": "7feccf69", + "metadata": { + "id": "7feccf69" + }, + "source": [ + "## 1. Imports\n", + "\n", + "`weightslab` is imported as `wl`. The two `guard_*_context` managers scope a block as training vs. evaluation so signals are attributed to the right phase. These are the external imports gathered from the example's `main.py` and `utils/` modules." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39ef03a8", + "metadata": { + "id": "39ef03a8" + }, + "outputs": [], + "source": [ + "import os\n", + "os.environ['WEIGHTSLAB_LOG_LEVEL'] = 'DEBUG'\n", + "import time\n", + "import logging\n", + "import tempfile\n", + "\n", + "import numpy as np\n", + "import tqdm\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "from torch import optim\n", + "from torch.utils.data import Dataset\n", + "from torchvision import transforms\n", + "from PIL import Image\n", + "\n", + "import weightslab as wl\n", + "from weightslab.components.global_monitoring import (\n", + " guard_training_context,\n", + " guard_testing_context,\n", + ")\n", + "\n", + "# Setup loggers (from main.py)\n", + "logging.basicConfig(level=logging.ERROR)\n", + "logging.getLogger(\"PIL\").setLevel(logging.INFO)\n", + "\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "print(\"Using device:\", device)" + ] + }, + { + "cell_type": "markdown", + "id": "cef2413d", + "metadata": { + "id": "cef2413d" + }, + "source": [ + "## 2. The dataset (inlined `utils/data.py`)\n", + "\n", + "`BDD100kSegDataset` returns `(image, uid, mask, metadata)` per sample, where `mask` is a single `[H, W]` semantic mask (pixel value = class id). `seg_collate` stacks the batch into `images [B, C, H, W]` and `labels [B, H, W]` so signals are attributed per sample." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2413697c", + "metadata": { + "id": "2413697c" + }, + "outputs": [], + "source": [ + "# ===== utils/data.py — SAMPLE-WISE semantic segmentation =====\n", + "import os\n", + "import torch\n", + "import numpy as np\n", + "\n", + "from torchvision import transforms\n", + "\n", + "from torch.utils.data import Dataset\n", + "\n", + "from PIL import Image\n", + "\n", + "\n", + "# =============================================================================\n", + "# BDD100k segmentation dataset (sample-wise)\n", + "# =============================================================================\n", + "class BDD100kSegDataset(Dataset):\n", + " \"\"\"Returns (image, uid, mask, metadata) with ONE semantic mask per sample.\n", + "\n", + " Layout expected under `root`:\n", + " images/{train,val}/ inputs (.jpg/.png)\n", + " labels/{train,val}/ masks (.png, same basename; pixel value = class id)\n", + "\n", + " `mask` is a single [H, W] int64 tensor of class ids (0..num_classes-1;\n", + " `ignore_index` for void). No per-instance masks — signals are per sample.\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " root,\n", + " split=\"train\",\n", + " num_classes=6,\n", + " class_names=None,\n", + " ignore_index=255,\n", + " image_size=256,\n", + " max_samples=None\n", + " ):\n", + " super().__init__()\n", + " self.root = root\n", + " self.split = split\n", + " self.num_classes = num_classes\n", + " self.class_names = class_names\n", + " self.ignore_index = ignore_index\n", + " self.task_type = \"segmentation\"\n", + "\n", + " img_dir = os.path.join(root, \"images\", split)\n", + " lbl_dir = os.path.join(root, \"labels\", split)\n", + "\n", + " image_files = [\n", + " f\n", + " for f in os.listdir(img_dir)\n", + " if f.lower().endswith((\".jpg\", \".jpeg\", \".png\"))\n", + " ]\n", + " image_files = sorted(set(image_files))[:max_samples] if max_samples != None else sorted(set(image_files)) # Optionally limit number of samples for faster testing\n", + "\n", + " self.images = []\n", + " self.masks = []\n", + " for fname in image_files:\n", + " img_path = os.path.join(img_dir, fname)\n", + " base, _ = os.path.splitext(fname)\n", + " lbl_name = base + \".png\"\n", + " lbl_path = os.path.join(lbl_dir, lbl_name)\n", + " if os.path.exists(lbl_path):\n", + " self.images.append(img_path)\n", + " self.masks.append(lbl_path)\n", + "\n", + " if len(self.images) == 0:\n", + " raise RuntimeError(f\"No image/label pairs found in {img_dir} / {lbl_dir}\")\n", + "\n", + " # Resize to a fixed SQUARE (image_size, image_size). A single int would\n", + " # resize only the shorter edge and keep the aspect ratio, so non-square\n", + " # inputs come out as e.g. [128, 227] and mismatch the prediction in the\n", + " # loss. The model input is square (input_shape=(1, C, image_size, image_size)).\n", + " self.image_transform = transforms.Compose(\n", + " [\n", + " transforms.Resize(\n", + " # size=(image_size, image_size),\n", + " size=image_size, # single int = resize shorter edge, keep aspect ratio\n", + " interpolation=Image.BILINEAR\n", + " ),\n", + " transforms.ToTensor(),\n", + " ]\n", + " )\n", + " self.mask_resize = transforms.Resize(\n", + " # size=(image_size, image_size),\n", + " size=image_size,\n", + " interpolation=Image.NEAREST\n", + " )\n", + "\n", + " def __len__(self):\n", + " return len(self.images)\n", + "\n", + " def __getitem__(self, idx):\n", + " \"\"\"Returns (item, uid, target, metadata):\n", + " - item: input image tensor [C, H, W]\n", + " - uid: unique id for the sample (filename)\n", + " - target: semantic mask tensor [H, W] (int64 class ids)\n", + " - metadata: optional dict (original paths)\n", + " \"\"\"\n", + " return self.get_items(idx, include_metadata=True, include_labels=True, include_images=True)\n", + "\n", + " def get_items(self, idx, include_metadata=False, include_labels=False, include_images=False):\n", + " img_path = self.images[idx]\n", + " mask_path = self.masks[idx]\n", + " uid = os.path.basename(img_path)\n", + "\n", + " metadata = {\n", + " 'img_path': img_path,\n", + " 'mask_path': mask_path,\n", + " } if include_metadata else None\n", + "\n", + " # Process image\n", + " img_t = None\n", + " if include_images:\n", + " img = Image.open(img_path).convert(\"RGB\")\n", + " img_t = self.image_transform(img)\n", + "\n", + " # Process label: one semantic mask [H, W] per sample.\n", + " mask_t = None\n", + " if include_labels:\n", + " mask = Image.open(mask_path)\n", + " mask_r = self.mask_resize(mask)\n", + " mask_np = np.array(mask_r, dtype=np.int64)\n", + " # Mask must be a single-channel class-id map [H, W]. If the file is\n", + " # RGB/RGBA (extra channel dim), keep the first channel so it matches\n", + " # the model's [H, W] prediction.\n", + " if mask_np.ndim > 2:\n", + " mask_np = mask_np[..., 0]\n", + " mask_t = torch.from_numpy(mask_np) # [H, W] int64\n", + "\n", + " return img_t, uid, mask_t, metadata\n", + "\n", + "\n", + "def seg_collate(batch):\n", + " \"\"\"Collate WL per-sample tuples for semantic segmentation.\n", + "\n", + " Each item is ``(img, uid, mask, metadata)`` where ``mask`` is a single\n", + " [H, W] class-id tensor. Images and masks stack into batched tensors; uids\n", + " and metadata stay as per-sample lists.\n", + "\n", + " Returns:\n", + " images: FloatTensor [B, C, H, W]\n", + " ids: list[str] of length B\n", + " labels: LongTensor [B, H, W]\n", + " metas: list[B] of metadata dicts\n", + " \"\"\"\n", + " images = torch.stack([b[0] for b in batch], dim=0)\n", + " ids = [b[1] for b in batch]\n", + " labels = torch.stack([torch.as_tensor(b[2]) for b in batch], dim=0)\n", + " metas = [b[3] if len(b) > 3 else None for b in batch]\n", + " return images, ids, labels, metas" + ] + }, + { + "cell_type": "markdown", + "id": "352304ca", + "metadata": { + "id": "352304ca" + }, + "source": [ + "## 3. The model (inlined `utils/model.py`)\n", + "\n", + "A compact U-Net (`SmallUNet`) with `task_type=\"segmentation\"` so WeightsLab renders masks in Weights Studio." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ecaac87", + "metadata": { + "id": "0ecaac87" + }, + "outputs": [], + "source": [ + "# ===== utils/model.py =====\n", + "# =============================================================================\n", + "# Small UNet-ish segmentation model\n", + "# =============================================================================\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "\n", + "class DoubleConv(nn.Module):\n", + " def __init__(self, in_ch, out_ch):\n", + " super().__init__()\n", + " self.block = nn.Sequential(\n", + " nn.Conv2d(in_ch, out_ch, 3, padding=1),\n", + " nn.BatchNorm2d(out_ch),\n", + " nn.ReLU(inplace=True),\n", + " nn.Conv2d(out_ch, out_ch, 3, padding=1),\n", + " nn.BatchNorm2d(out_ch),\n", + " nn.ReLU(inplace=True),\n", + " )\n", + "\n", + " def forward(self, x):\n", + " return self.block(x)\n", + "\n", + "\n", + "class SmallUNet(nn.Module):\n", + " def __init__(self, in_channels=3, num_classes=6, image_size=256):\n", + " super().__init__()\n", + " # For WeightsLab\n", + " self.task_type = \"segmentation\"\n", + " self.num_classes = num_classes\n", + " self.class_names = [\"Background\", \"Ego Road\", \"Driveable Area\", \"Lane Line 1\", \"Lane Line 2\", \"Lane Line 3\"]\n", + " self.input_shape = (1, in_channels, image_size, image_size)\n", + "\n", + " self.enc1 = DoubleConv(in_channels, 32)\n", + " self.pool1 = nn.MaxPool2d(2)\n", + " self.enc2 = DoubleConv(32, 64)\n", + " self.pool2 = nn.MaxPool2d(2)\n", + "\n", + " self.bottleneck = DoubleConv(64, 128)\n", + "\n", + " self.up2 = nn.ConvTranspose2d(128, 64, 2, stride=2)\n", + " self.dec2 = DoubleConv(64 + 64, 64)\n", + " self.up1 = nn.ConvTranspose2d(64, 32, 2, stride=2)\n", + " self.dec1 = DoubleConv(32 + 32, 32)\n", + "\n", + " self.head = nn.Conv2d(32, num_classes, 1)\n", + "\n", + " def forward(self, x):\n", + " # Encoder\n", + " e1 = self.enc1(x)\n", + " p1 = self.pool1(e1)\n", + "\n", + " e2 = self.enc2(p1)\n", + " p2 = self.pool2(e2)\n", + "\n", + " # Bottleneck\n", + " b = self.bottleneck(p2)\n", + "\n", + " # Decoder\n", + " u2 = self.up2(b)\n", + " # Important: no `if` on shapes; always interpolate\n", + " u2 = F.interpolate(u2, size=e2.shape[-2:], mode=\"bilinear\", align_corners=False)\n", + " d2 = self.dec2(torch.cat([u2, e2], dim=1))\n", + "\n", + " u1 = self.up1(d2)\n", + " u1 = F.interpolate(u1, size=e1.shape[-2:], mode=\"bilinear\", align_corners=False)\n", + " d1 = self.dec1(torch.cat([u1, e1], dim=1))\n", + "\n", + " logits = self.head(d1) # [B, C, H, W]\n", + " return logits" + ] + }, + { + "cell_type": "markdown", + "id": "7d382efc", + "metadata": { + "id": "7d382efc" + }, + "source": [ + "## 4. Losses & metrics (inlined `utils/criterions.py`)\n", + "\n", + "Per-sample combined **CrossEntropy + Dice** (loss) and **Dice** (metric), ported to match the `ws-seg-wow-moment-bdd` experiment's `criterions.py::CrossEntropySegLoss` exactly (same formula, same per-class weights, same `ignore_index` handling — see that class's docstring below for a quirk in the reference project that's preserved here for faithful reproduction). Each scores the model's `[B, C, H, W]` output against the `[B, H, W]` mask and returns **one value per sample** (`[B]`), wrapped with `per_sample=True` so WeightsLab logs it per `sample_id`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c483178e", + "metadata": { + "id": "c483178e" + }, + "outputs": [], + "source": [ + "# ===== utils/criterions.py — SAMPLE-WISE Dice (metric) + combined CE+Dice (loss) =====\n", + "# The loss below is ported to match ws-seg-wow-moment-bdd/criterions.py's\n", + "# CrossEntropySegLoss exactly (same formula, same per-class weighting, same\n", + "# ignore_index handling) so this notebook reproduces that experiment's loss.\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "_EPS = 1e-7 # matches PerSampleMultiClassDiceLoss's eps in ws-seg-wow-moment-bdd/criterions.py\n", + "\n", + "\n", + "class PerSampleMultiClassDiceLoss(nn.Module):\n", + " \"\"\"Per-sample, per-class Dice LOSS (1 - diceScore), averaged over classes -> [B].\n", + "\n", + " Ported verbatim (same formula/eps/masking) from\n", + " ws-seg-wow-moment-bdd/criterions.py::PerSampleMultiClassDiceLoss.\n", + " \"\"\"\n", + "\n", + " def __init__(self, ignore_index=255, eps=_EPS, weights=None, ignore_classes=None):\n", + " super().__init__()\n", + " self.ignore_index = ignore_index\n", + " self.eps = eps\n", + " self.weights = weights\n", + " self.ignore_classes = ignore_classes\n", + "\n", + " def forward(self, logits, targets):\n", + " bs, num_classes, H, W = logits.shape\n", + " probs = torch.softmax(logits, dim=1) # [B, C, H, W]\n", + " valid = (targets != self.ignore_index) # [B, H, W]\n", + " targets_safe = targets.clone()\n", + " targets_safe[~valid] = 0\n", + " one_hot = F.one_hot(targets_safe, num_classes=num_classes).permute(0, 3, 1, 2).float()\n", + "\n", + " valid_f = valid.unsqueeze(1).float() # [B, 1, H, W]\n", + " probs = probs * valid_f\n", + " one_hot = one_hot * valid_f\n", + "\n", + " intersection = (probs * one_hot).sum(dim=(2, 3)) # [B, C]\n", + " cardinality = (probs + one_hot).sum(dim=(2, 3)) # [B, C]\n", + " dice_per_class = 1.0 - (2.0 * intersection + self.eps) / (cardinality + self.eps) # [B, C] — a LOSS, not a score\n", + "\n", + " if self.ignore_classes:\n", + " class_mask = torch.ones(num_classes, dtype=torch.bool, device=logits.device)\n", + " class_mask[list(self.ignore_classes)] = False\n", + " dice_per_class = dice_per_class[:, class_mask]\n", + " weights = self.weights[class_mask] if self.weights is not None else None\n", + " else:\n", + " weights = self.weights\n", + "\n", + " if weights is not None:\n", + " dice_per_class = dice_per_class * weights\n", + " return dice_per_class.mean(dim=1) # [B]\n", + "\n", + "\n", + "class PerSampleCEDice(nn.Module):\n", + " \"\"\"Combined CrossEntropy + Dice loss -> [B] (differentiable, per-sample).\n", + "\n", + " Ported from ws-seg-wow-moment-bdd/criterions.py::CrossEntropySegLoss, with\n", + " one fix applied on both sides: `PerSampleMultiClassDiceLoss` above already\n", + " returns a dice LOSS (`1 - diceScore`), so it's used directly here. The\n", + " reference project originally did `(1 - dice)` again on top of that, which\n", + " algebraically reduces to the raw dice SCORE (i.e. a *better* dice score\n", + " increased the loss) — that double-negation has been fixed in both this\n", + " notebook and ws-seg-wow-moment-bdd/criterions.py.\n", + " \"\"\"\n", + "\n", + " def __init__(self, weights=None, ignore_index=255, bce_weight=0.5, dice_weight=0.5):\n", + " super().__init__()\n", + " self.register_buffer(\n", + " \"weights\",\n", + " torch.as_tensor(weights, dtype=torch.float32) if weights is not None else None,\n", + " )\n", + " self.ignore_index = ignore_index\n", + " self.bce_weight = bce_weight\n", + " self.dice_weight = dice_weight\n", + " self.dice = PerSampleMultiClassDiceLoss(ignore_index=ignore_index, weights=self.weights)\n", + "\n", + " def forward(self, outputs, labels):\n", + " # outputs [B, C, H, W]; labels [B, H, W] (int64 class ids)\n", + " ce = F.cross_entropy(\n", + " outputs, labels.long(),\n", + " weight=self.weights, ignore_index=self.ignore_index, reduction=\"none\",\n", + " ) # [B, H, W]\n", + " # Mean over ALL pixels (ignored ones included as 0), matching the\n", + " # source project's un-masked mean (not a valid-pixel-only average).\n", + " ce = ce.flatten(1).mean(dim=1) # [B]\n", + "\n", + " dice_loss = self.dice(outputs, labels.long()) # [B] — already a LOSS (1 - diceScore)\n", + "\n", + " return self.bce_weight * ce + self.dice_weight * dice_loss\n", + "\n", + "\n", + "class PerSampleDice(nn.Module):\n", + " \"\"\"Per-sample mean foreground Dice -> [B] (metric, detached; for the WL dashboard).\"\"\"\n", + "\n", + " def forward(self, outputs, labels):\n", + " probs = torch.softmax(outputs, dim=1) # [B, C, H, W]\n", + " C = probs.shape[1]\n", + " tgt = torch.where(labels < C, labels, torch.zeros_like(labels)).long() # void/ignore -> background\n", + " onehot = F.one_hot(tgt, num_classes=C).permute(0, 3, 1, 2).float() # [B, C, H, W]\n", + " inter = (probs * onehot).sum(dim=(2, 3))\n", + " denom = probs.sum(dim=(2, 3)) + onehot.sum(dim=(2, 3))\n", + " dice = (2.0 * inter + _EPS) / (denom + _EPS) # [B, C]\n", + " fg = dice[:, 1:].mean(dim=1) if C > 1 else dice.mean(dim=1) # exclude background channel\n", + " return fg.detach() # [B]" + ] + }, + { + "cell_type": "markdown", + "id": "2965ed80", + "metadata": { + "id": "2965ed80" + }, + "source": [ + "## 5. Configuration\n", + "\n", + "Every tunable lives here in one dict - this is `config.yaml` inlined, with its comments. Wrapping it with `flag=\"hyperparameters\"` lets Weights Studio read (and live-edit) these values while training. `data_root` points at the sample fetched above.\n", + "\n", + "**Reproducing `ws-seg-wow-moment-bdd`:** `ignore_index`, `image_size`, `training_steps_to_do`, `eval_full_to_train_steps_ratio`, and the train `batch_size` below are set to match that experiment's `config.yaml` exactly, since these directly change training results (model architecture, optimizer, and dataset format were already identical)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00e4c364", + "metadata": { + "id": "00e4c364" + }, + "outputs": [], + "source": [ + "config = {\n", + " # -- Global configuration ------------------------------------------------\n", + " \"device\": \"auto\", # 'auto' -> resolved to `device` from the Imports cell\n", + " \"experiment_name\": \"seg_bdd_training\", # name shown in Weights Studio\n", + " \"training_steps_to_do\": 10000, # matches ws-seg-wow-moment-bdd/config.yaml (training_steps_to_do)\n", + " # \"root_log_dir\": \"/tmp/tmpg947ov7y/\", # None -> a temp dir is created (history + dataframe land here)\n", + "\n", + " \"checkpoint_manager\": {\n", + " \"load_config\": False, # don't reload a previous checkpoint's config, so edits here take effect\n", + " },\n", + "\n", + " # Initially compute natural sorting values, e.g. average intensity (see the example README).\n", + " \"compute_natural_sort\": True,\n", + "\n", + " # -- Experiment parameters ----------------------------------------------\n", + " \"eval_full_to_train_steps_ratio\": 256, # matches ws-seg-wow-moment-bdd (test every 256 steps)\n", + " \"experiment_dump_to_train_steps_ratio\": 256,\n", + " \"write_export_ratio\": 100, # export history + dataframe every N steps (main.py default)\n", + " \"tqdm_display\": True,\n", + " \"is_training\": False, # start training immediately or not\n", + "\n", + " # -- Serving (the notebook serves via gRPC + a bore tunnel; see the Serve cell) --\n", + " \"serving_grpc\": True,\n", + " \"serving_cli\": True,\n", + "\n", + " # -- Global dataframe storage -------------------------------------------\n", + " \"ledger_enable_h5_persistence\": True,\n", + " \"ledger_enable_flushing_threads\": True,\n", + " \"ledger_flush_max_rows\": 100,\n", + " \"ledger_flush_interval\": 60.0,\n", + "\n", + " # -- Data ----------------------------------------------------------------\n", + " \"num_classes\": 6,\n", + " \"class_names\": [\"Background\", \"Ego Road\", \"Driveable Area\", \"Lane Line 1\", \"Lane Line 2\", \"Lane Line 3\"],\n", + " \"ignore_index\": 0, # matches ws-seg-wow-moment-bdd: excludes \"Background\" (class 0) from loss/metric\n", + " \"image_size\": 180, # matches ws-seg-wow-moment-bdd (shortest edge resized to 720, aspect ratio kept)\n", + " \"data_root\": DATA_ROOT, # local ws-seg-wow-moment-bdd dataset if found, else the Drive zip (see the data-fetch cell)\n", + " \"data\": {\n", + " \"train_loader\": {\"batch_size\": 8, \"shuffle\": True}, # matches ws-seg-wow-moment-bdd (train batch_size 8)\n", + " \"test_loader\": {\"batch_size\": 2, \"shuffle\": False},\n", + " },\n", + "\n", + " # -- Optimizer -----------------------------------------------------------\n", + " \"optimizer\": {\"lr\": 1e-3}, # Adam learning rate (matches ws-seg-wow-moment-bdd, which has no `optimizer:` key so falls back to this same 1e-3 default)\n", + "}\n", + "\n", + "# Resolve the 'auto' device to the one picked in the Imports cell.\n", + "if config.get(\"device\", \"auto\") == \"auto\":\n", + " config[\"device\"] = str(device)\n", + "\n", + "# Register the config so Weights Studio can read (and live-edit) it while training.\n", + "wl.watch_or_edit(config, flag=\"hyperparameters\", name=config[\"experiment_name\"],\n", + " defaults=config, poll_interval=1.0)\n", + "\n", + "# Logging directory: None -> a temp dir is created.\n", + "if not config.get(\"root_log_dir\"):\n", + " config[\"root_log_dir\"] = tempfile.mkdtemp(prefix=\"weightslab_seg_\")\n", + "os.makedirs(config[\"root_log_dir\"], exist_ok=True)\n", + "log_dir = config[\"root_log_dir\"]\n", + "\n", + "# Schedule knobs used by the training loop.\n", + "eval_full_to_train_steps_ratio = config[\"eval_full_to_train_steps_ratio\"]\n", + "write_export_ratio = config.get(\"write_export_ratio\", 100)\n", + "tqdm_display = config.get(\"tqdm_display\", True)\n", + "print(\"Experiment logs ->\", log_dir)" + ] + }, + { + "cell_type": "markdown", + "id": "88cc2afa", + "metadata": { + "id": "88cc2afa" + }, + "source": [ + "## 6. Build data, model & optimizer\n", + "\n", + "The heart of WeightsLab: each object is passed through `wl.watch_or_edit(...)` with a `flag` describing its role. The returned objects behave like the originals but report their state and per-sample signals. The loaders use `collate_fn=seg_collate` to stack the image and mask batches." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9df9c8cf", + "metadata": { + "id": "9df9c8cf" + }, + "outputs": [], + "source": [ + "# --- Hyperparameters read back after watch_or_edit ---\n", + "num_classes = int(config[\"num_classes\"])\n", + "class_names = config[\"class_names\"]\n", + "ignore_index = int(config[\"ignore_index\"])\n", + "image_size = int(config[\"image_size\"])\n", + "\n", + "# --- Data (local ws-seg-wow-moment-bdd dataset, or the Drive zip fetched above) ---\n", + "data_root = config.get(\"data_root\", DATA_ROOT)\n", + "if os.path.exists(data_root):\n", + " print(f\"Using data root: {data_root}\")\n", + "else:\n", + " raise FileNotFoundError(\n", + " f\"Data root not found: {data_root!r}. Run the data-fetch cell above first.\")\n", + "\n", + "train_cfg = config.get(\"data\", {}).get(\"train_loader\", {})\n", + "test_cfg = config.get(\"data\", {}).get(\"test_loader\", {})\n", + "\n", + "# ws-seg-wow-moment-bdd's dataset uses images/{train,test}; the packaged Drive\n", + "# toy dataset uses images/{train,val} — detect whichever is actually present.\n", + "eval_split = \"test\" if os.path.isdir(os.path.join(data_root, \"images\", \"test\")) else \"val\"\n", + "print(f\"Eval split: {eval_split!r}\")\n", + "\n", + "_train_dataset = BDD100kSegDataset(\n", + " root=data_root,\n", + " split=\"train\",\n", + " num_classes=num_classes,\n", + " class_names=class_names,\n", + " ignore_index=ignore_index,\n", + " image_size=image_size,\n", + " max_samples=train_cfg.get(\"max_samples\", None),\n", + ")\n", + "_test_dataset = BDD100kSegDataset(\n", + " root=data_root,\n", + " split=eval_split,\n", + " num_classes=num_classes,\n", + " class_names=class_names,\n", + " ignore_index=ignore_index,\n", + " image_size=image_size,\n", + " max_samples=test_cfg.get(\"max_samples\", None),\n", + ")\n", + "\n", + "train_loader = wl.watch_or_edit(\n", + " _train_dataset, flag=\"data\", loader_name=\"train_loader\",\n", + " batch_size=train_cfg.get(\"batch_size\", 2), shuffle=train_cfg.get(\"shuffle\", True),\n", + " compute_hash=False, is_training=True,\n", + " array_autoload_arrays=False, array_return_proxies=True, array_use_cache=True,\n", + " preload_labels=False, collate_fn=seg_collate,\n", + ")\n", + "test_loader = wl.watch_or_edit(\n", + " _test_dataset, flag=\"data\", loader_name=\"test_loader\",\n", + " batch_size=test_cfg.get(\"batch_size\", 2), shuffle=test_cfg.get(\"shuffle\", False),\n", + " compute_hash=False, is_training=False,\n", + " array_autoload_arrays=False, array_return_proxies=True, array_use_cache=True,\n", + " preload_labels=True, collate_fn=seg_collate,\n", + ")\n", + "\n", + "_model = SmallUNet(in_channels=3, num_classes=num_classes, image_size=image_size).to(device)\n", + "model = wl.watch_or_edit(_model, flag=\"model\", device=device, compute_dependencies=True)\n", + "\n", + "lr = config.get(\"optimizer\", {}).get(\"lr\", 1e-3)\n", + "_optimizer = optim.Adam(model.parameters(), lr=lr)\n", + "optimizer = wl.watch_or_edit(_optimizer, flag=\"optimizer\")" + ] + }, + { + "cell_type": "markdown", + "id": "ec7508f0", + "metadata": { + "id": "ec7508f0" + }, + "source": [ + "## 7. Train & eval steps (+ tracked signals)\n", + "\n", + "The `guard_training_context` / `guard_testing_context` blocks tell WeightsLab which phase it's in. `_run_signals` computes and logs the per-sample **Dice** (metric) and **CrossEntropy** (loss); `wl.save_signals(...)` records custom per-sample diagnostics (predicted vs present classes). Class weights are computed from the training split, then the tracked signals are built." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52c07147", + "metadata": { + "id": "52c07147" + }, + "outputs": [], + "source": [ + "# ===== train / eval step functions (sample-wise) =====\n", + "from torchmetrics import JaccardIndex\n", + "\n", + "\n", + "def _run_signals(sig, outputs, labels, ids, preds, return_metric=False):\n", + " \"\"\"Compute + log the per-sample combined CE+Dice (loss) and Dice (metric).\"\"\"\n", + " loss_sample = sig[\"loss_sample\"](outputs, labels, batch_ids=ids, preds=preds)\n", + " dice_sample = sig[\"dice_sample\"](outputs, labels, batch_ids=ids)\n", + " if return_metric:\n", + " return loss_sample, dice_sample\n", + " return loss_sample\n", + "\n", + "\n", + "def _user_custom_signals(preds, labels):\n", + " \"\"\"Per-sample diagnostics: which classes are predicted vs present in the mask.\"\"\"\n", + " B = preds.shape[0]\n", + " return {\n", + " \"preds_classes_per_sample\": [preds[i].unique() for i in range(B)],\n", + " \"target_classes_per_sample\": [labels[i].unique() for i in range(B)],\n", + " \"tp_classes_per_sample\": [\n", + " torch.tensor([c for c in labels[i].unique() if c in preds[i].unique()]) for i in range(B)\n", + " ],\n", + " \"fp_classes_per_sample\": [\n", + " torch.tensor([c for c in preds[i].unique() if c not in labels[i].unique()]) for i in range(B)\n", + " ],\n", + " \"fn_classes_per_sample\": [\n", + " torch.tensor([c for c in labels[i].unique() if c not in preds[i].unique()]) for i in range(B)\n", + " ],\n", + " }\n", + "\n", + "\n", + "def train(loader, model, optimizer, sig, metric, device):\n", + " \"\"\"Single training step. loader yields (inputs, ids, labels, metadata);\n", + " `labels` is a [B, H, W] semantic mask (see seg_collate).\"\"\"\n", + " with guard_training_context:\n", + " (inputs, ids, labels, _) = next(loader)\n", + " inputs = inputs.to(device)\n", + " labels = labels.to(device) # [B, H, W]\n", + "\n", + " optimizer.zero_grad()\n", + " outputs = model(inputs) # [B, C, H, W]\n", + " preds = outputs.argmax(dim=1) # [B, H, W]\n", + "\n", + " # Per-sample combined CE+Dice (loss) + Dice (metric), tracked & saved per sample.\n", + " loss_per_sample = _run_signals(sig, outputs, labels, ids, preds=preds) # [B]\n", + " loss = loss_per_sample.mean()\n", + "\n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " # Per-sample class diagnostics for the UI (predicted vs present classes).\n", + " wl.save_signals(_user_custom_signals(preds, labels), ids)\n", + "\n", + " # Batch IoU, matching ws-seg-wow-moment-bdd's train() (reset every step).\n", + " metric.update(preds, labels.detach())\n", + " train_iou = metric.compute().detach().cpu().item() * 100.0\n", + " metric.reset()\n", + "\n", + " return float(loss.detach().cpu().item()), train_iou\n", + "\n", + "\n", + "def test(loader, model, sig, metric, device, test_loader_len):\n", + " \"\"\"Full evaluation pass over the eval loader.\"\"\"\n", + " losses = torch.tensor([0.]).to(device)\n", + " dices = torch.tensor([0.]).to(device)\n", + " metric.reset()\n", + " with guard_testing_context, torch.no_grad():\n", + " for inputs, ids, labels, _ in loader:\n", + " inputs = inputs.to(device)\n", + " labels = labels.to(device) # [B, H, W]\n", + "\n", + " outputs = model(inputs)\n", + " preds = outputs.argmax(dim=1) # [B, H, W]\n", + "\n", + " loss_per_sample, dice_sample = _run_signals(sig, outputs, labels, ids, preds=preds, return_metric=True)\n", + " losses += torch.mean(loss_per_sample) # accumulate batch mean\n", + " dices += torch.mean(dice_sample)\n", + " metric.update(preds, labels)\n", + "\n", + " wl.save_signals(_user_custom_signals(preds, labels), ids)\n", + "\n", + " loss = float((losses / test_loader_len).detach().cpu().item())\n", + " dice = float((dices / test_loader_len).detach().cpu().item())\n", + " iou = metric.compute().detach().cpu().item() * 100.0\n", + " return loss, dice * 100.0, iou # Dice/IoU as percentages\n", + "\n", + "\n", + "# ===== tracked per-sample Dice (metric) + combined CE+Dice (loss) signals =====\n", + "# Both are wrapped with per_sample=True so WeightsLab logs one value per\n", + "# sample_id. No per-instance signals.\n", + "def _make_seg_signals(split: str, weights=None, ignore_index: int = 255) -> dict:\n", + " return {\n", + " \"dice_sample\": wl.watch_or_edit(\n", + " PerSampleDice(), flag=\"metric\",\n", + " name=f\"{split}_dice/sample\", per_sample=True, log=True,\n", + " ),\n", + " \"loss_sample\": wl.watch_or_edit(\n", + " PerSampleCEDice(weights=weights, ignore_index=ignore_index), flag=\"loss\",\n", + " name=f\"{split}_CE\", per_sample=True, log=True,\n", + " ),\n", + " }\n", + "\n", + "\n", + "def compute_class_weights(dataset, num_classes, ignore_index=255, max_samples=100):\n", + " print(\"\\n\" + \"=\" * 60, flush=True)\n", + " print(f\"Computing class weights for {num_classes} classes (max {max_samples} samples)...\", flush=True)\n", + " class_counts = np.zeros(num_classes, dtype=np.float64)\n", + " num_samples = min(len(dataset), max_samples)\n", + "\n", + " for idx in tqdm.tqdm(range(num_samples), desc=\" Analyzing Distribution\"):\n", + " _, _, label, _ = dataset.get_items(idx, include_labels=True) # semantic mask [H, W]\n", + " label_np = label.numpy() if hasattr(label, 'numpy') else np.array(label)\n", + " for c in range(num_classes):\n", + " class_counts[c] += (label_np == c).sum()\n", + "\n", + " class_counts = np.maximum(class_counts, 1) # Avoid div by zero\n", + " total_pixels = class_counts.sum()\n", + " class_weights = total_pixels / (num_classes * class_counts)\n", + " class_weights = class_weights / class_weights.mean() # Normalize\n", + "\n", + " print(\"\\nClass distribution and weights:\", flush=True)\n", + " for c in range(num_classes):\n", + " pct = (class_counts[c] / total_pixels) * 100\n", + " print(f\"Class {c}: {pct:6.2f}% -> weight: {class_weights[c]:.3f}\", flush=True)\n", + " print(\"=\" * 60 + \"\\n\", flush=True)\n", + " return torch.FloatTensor(class_weights).to(device)\n", + "\n", + "weights = compute_class_weights(_train_dataset, num_classes, max_samples=100)\n", + "\n", + "# Matches ws-seg-wow-moment-bdd: the train criterion is class-weighted, the\n", + "# test criterion is NOT (its CrossEntropySegLoss gets no `weights=` argument there).\n", + "train_sig = _make_seg_signals(\"train\", weights=weights, ignore_index=ignore_index)\n", + "test_sig = _make_seg_signals(\"test\", weights=None, ignore_index=ignore_index)\n", + "\n", + "# IoU (macro, ignore_index=0), matching ws-seg-wow-moment-bdd's train_iou/test_iou.\n", + "train_metric = wl.watch_or_edit(\n", + " JaccardIndex(task=\"multiclass\", num_classes=num_classes, ignore_index=ignore_index, average=\"macro\").to(device),\n", + " flag=\"metric\", name=\"train_iou\", per_sample=False, log=True,\n", + ")\n", + "test_metric = wl.watch_or_edit(\n", + " JaccardIndex(task=\"multiclass\", num_classes=num_classes, ignore_index=ignore_index, average=\"macro\").to(device),\n", + " flag=\"metric\", name=\"test_iou\", per_sample=False, log=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "fb9890dd", + "metadata": { + "id": "fb9890dd" + }, + "source": [ + "## 8. Serve and train\n", + "\n", + "`wl.serve(serving_grpc=True, serving_bore=True)` starts the background gRPC server and opens a public `bore.pub:` tunnel (printed below) that Weights Studio connects to. `wl.start_training(...)` flips the experiment into the *training* state, then we run a finite loop, periodically evaluating and exporting signals.\n", + "\n", + "Leave this cell **running** while you watch it stream in the UI." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "635014b7", + "metadata": { + "collapsed": true, + "id": "635014b7" + }, + "outputs": [], + "source": [ + "wl.serve(serving_grpc=config.get(\"serving_grpc\", True), serving_bore=True)\n", + "# wl.start_training(timeout=60)\n", + "\n", + "# training_steps_to_do may be None (open-ended); cap it here so the notebook run is finite.\n", + "max_steps = config[\"training_steps_to_do\"] or 500\n", + "train_range = tqdm.tqdm(range(max_steps), desc=\"Training\") if tqdm_display else range(max_steps)\n", + "test_loss, test_dice, test_iou = None, None, None\n", + "start_time = time.time()\n", + "for train_step in train_range:\n", + " age = model.get_age() if hasattr(model, \"get_age\") else train_step\n", + "\n", + " # Train one step\n", + " train_loss, train_iou = train(train_loader, model, optimizer, train_sig, train_metric, device)\n", + "\n", + " # Full evaluation pass\n", + " if age == 0 or age % eval_full_to_train_steps_ratio == 0:\n", + " test_loader_len = len(test_loader)\n", + " test_loader_it = tqdm.tqdm(test_loader, desc=\"Evaluating\", leave=False) if tqdm_display else test_loader\n", + " test_loss, test_dice, test_iou = test(test_loader_it, model, test_sig, test_metric, device, test_loader_len)\n", + "\n", + " if tqdm_display:\n", + " train_range.set_description(\"Step\")\n", + " train_range.set_postfix(\n", + " train_loss=f\"{train_loss:.4f}\",\n", + " train_iou=f\"{train_iou:.2f}%\",\n", + " test_loss=f\"{test_loss:.4f}\" if test_loss is not None else \"N/A\",\n", + " test_dice=f\"{test_dice:.2f}%\" if test_dice is not None else \"N/A\",\n", + " test_iou=f\"{test_iou:.2f}%\" if test_iou is not None else \"N/A\",\n", + " )\n", + "\n", + "print(\"\\n\" + \"=\" * 60)\n", + "print(f\" Training completed in {time.time() - start_time:.2f} seconds\")\n", + "print(f\" Logs saved to: {log_dir}\")\n", + "print(\"=\" * 60)\n", + "\n", + "# Final export of signal history and data grid to root_log_dir\n", + "wl.write_history()\n", + "wl.write_dataframe()" + ] + }, + { + "cell_type": "markdown", + "id": "3812a421", + "metadata": { + "id": "3812a421" + }, + "source": [ + "## See it live in Weights Studio\n", + "\n", + "**Colab has no Docker daemon**, so run Studio on your own machine and point it at this notebook's\n", + "backend using the `bore.pub:` printed in the Expose section:\n", + "\n", + "```bash\n", + "pip install weightslab\n", + "weightslab ui launch # Terminal 1 - opens http://localhost:5173\n", + "weightslab tunnel bore.pub:12345 # Terminal 2 - the host:port from the Expose section\n", + "```\n", + "\n", + "Then open **http://localhost:5173**. Prefer local-only? Run this example directly on your machine\n", + "(`weightslab start example` selects a bundled example) and launch the UI next to it - no tunnel." + ] + }, + { + "cell_type": "markdown", + "id": "36c018d3", + "metadata": { + "id": "36c018d3" + }, + "source": [ + "---\n", + "\n", + "
\n", + "Crafted by GrayboxTech - if WeightsLab helps you catch a bad label, drop us a star on GitHub.\n", + "
" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "ws-segmentation.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Semantic Segmentation (BDD100k) with WeightsLab\n", - "\n", - "Trains a small U-Net on a **BDD100k** subset and instruments per-**instance** and per-**sample** Dice (metric) and BCE (loss), so every mask's quality is traced back to the exact sample and annotation producing it.\n", - "\n", - "> Data: The BDD100k subset (`BDD_subset/`) **ships in the repo**, so no download is needed.\n", - "\n", - "Unlike the self-contained classification demo, this example uses local helper modules\n", - "(`utils/`) and bundled/downloaded data, so the notebook **clones the repo** and runs the\n", - "example in place." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "On Colab, pick a **T4 GPU** runtime (`Runtime -> Change runtime type -> T4 GPU`)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\"PyPI\n", - "\"PyPI\n", - "\"PyPI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Clone the repo (for the example's utils/ + data) and install WeightsLab.\n", - "import os\n", - "if not os.path.isdir(\"weightslab\"):\n", - " !git clone --branch torch_collab_examples https://github.com/GrayboxTech/weightslab.git\n", - "%pip install -q ./weightslab\n", - "!pip install --upgrade \"protobuf>=6.31.1\"\n", - "%pip install \"torchvision>=0.16\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Move into the example directory so its `utils/` and config.yaml resolve.\n", - "%cd weightslab/weightslab/examples/PyTorch/ws-segmentation\n", - "# Install the example's own extra requirements, if any.\n", - "import os\n", - "if os.path.exists(\"requirements.txt\"):\n", - " !pip install -q -r requirements.txt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration\n", - "\n", - "Every tunable lives in **`config.yaml`** (already commented). We load it into a `config` dict\n", - "so you can tweak values here; the changes are written back before training." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml\n", - "\n", - "with open(\"config.yaml\") as f:\n", - " config = yaml.safe_load(f)\n", - "\n", - "# --- tweak any parameter here, e.g. ---\n", - "# config[\"eval_full_to_train_steps_ratio\"] = 5\n", - "config[\"serving_grpc\"] = True # expose the gRPC backend for Weights Studio\n", - "\n", - "with open(\"config.yaml\", \"w\") as f:\n", - " yaml.safe_dump(config, f, sort_keys=False)\n", - "\n", - "print(yaml.safe_dump(config, sort_keys=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## (Optional) Expose the backend for the live UI\n", - "\n", - "To watch training **live in Weights Studio on your own machine**, run this cell first (it opens a\n", - "raw-TCP [`bore`](https://github.com/ekzhang/bore) tunnel over the free public relay `bore.pub` -\n", - "**no signup, no card**), then start training below.\n", - "\n", - "> `bore.pub` is a shared public relay; the random port is the only thing protecting your endpoint.\n", - "> Fine for a demo, not for sensitive data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os, re, tarfile, threading, urllib.request, subprocess\n", - "\n", - "EXPOSE_UI = True # set False for a headless run (no tunnel)\n", - "BACKEND_PORT = 50051 # gRPC port to forward\n", - "\n", - "endpoint = None\n", - "if EXPOSE_UI:\n", - " bore = os.path.join(os.getcwd(), \"bore\")\n", - " if not os.path.exists(bore):\n", - " BORE = \"v0.6.0\"\n", - " urllib.request.urlretrieve(\n", - " f\"https://github.com/ekzhang/bore/releases/download/{BORE}/bore-{BORE}-x86_64-unknown-linux-musl.tar.gz\",\n", - " \"bore.tar.gz\")\n", - " with tarfile.open(\"bore.tar.gz\") as t:\n", - " t.extractall()\n", - " os.chmod(bore, 0o755)\n", - "\n", - " proc = subprocess.Popen(\n", - " [bore, \"local\", str(BACKEND_PORT), \"--to\", \"bore.pub\"],\n", - " stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)\n", - " for line in proc.stdout:\n", - " m = re.search(r\"bore\\.pub:(\\d+)\", line)\n", - " if m:\n", - " endpoint = f\"bore.pub:{m.group(1)}\"\n", - " break\n", - " threading.Thread(target=lambda: [None for _ in proc.stdout], daemon=True).start()\n", - "\n", - " print(\"=\" * 60)\n", - " print(\"Backend exposed at:\", endpoint)\n", - " print(\"On your OWN machine (Docker running), run:\")\n", - " print(\" weightslab ui launch\")\n", - " print(f\" weightslab tunnel {endpoint}\")\n", - " print(\"=\" * 60)\n", - "else:\n", - " print(\"EXPOSE_UI is False - no tunnel.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Train\n", - "\n", - "This runs the example's `main.py`, which wraps the model / optimizer / loaders / losses with\n", - "`wl.watch_or_edit(...)`, starts the gRPC backend, and trains while streaming per-sample signals.\n", - "\n", - "> This example trains **open-ended** (until you interrupt the cell). Interrupt the cell (stop button) to stop training; the exported history and\n", - "> dataframe are written to a temp `root_log_dir` (printed at startup)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python main.py" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## See it live in Weights Studio\n", - "\n", - "**Colab has no Docker daemon**, so run Studio on your own machine and point it at this notebook's\n", - "backend using the `bore.pub:` printed in the Expose section:\n", - "\n", - "```bash\n", - "pip install weightslab\n", - "weightslab ui launch # Terminal 1 - opens http://localhost:5173\n", - "weightslab tunnel bore.pub:12345 # Terminal 2 - the host:port from the Expose section\n", - "```\n", - "\n", - "Then open **http://localhost:5173**. Prefer local-only? Run this example directly on your machine\n", - "(`weightslab start example` selects a bundled example) and launch the UI next to it - no tunnel." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "
\n", - "Crafted by GrayboxTech - if WeightsLab helps you catch a bad label, drop us a star on GitHub.\n", - "
" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "ws-segmentation.ipynb", - "provenance": [], - "toc_visible": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 0 + "nbformat": 4, + "nbformat_minor": 5 } \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-brain-tumor-detection-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-brain-tumor-detection-dataset.ipynb new file mode 100644 index 00000000..bd49ca5c --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-brain-tumor-detection-dataset.ipynb @@ -0,0 +1,1908 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Jh24MV9yKs7J" + }, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO detector on the MRI brain-tumor dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ImSwebklKs7S" + }, + "source": [ + "# Brain Tumor Detection (MRI) · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO detector on the [brain-tumor](https://docs.ultralytics.com/datasets/detect/brain-tumor/) MRI dataset **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "**Dataset** — 893 training images and 223 validation images, each with detection annotations. Classes: `0: negative`, `1: positive`. Ultralytics auto-downloads it (~4 MB) the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QA3hHC2xKs7h" + }, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically — no manual setup needed. For reference, `brain-tumor.yaml` looks like this:\n", + "\n", + "```yaml\n", + "path: brain-tumor # dataset root dir (auto-downloaded)\n", + "train: images/train # 893 images\n", + "val: images/val # 223 images\n", + "names:\n", + " 0: negative\n", + " 1: positive\n", + "```\n", + "\n", + "To train on **your own** data, point `data` (in the next cell) at your own `data.yaml` in YOLO format." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "Svz7NK9qKs7k", + "outputId": "2931e6b9-a06e-4dc0-9f67-e2e6216ee1c9" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-09:29:52.825 INFO:weightslab.src:watch_or_edit: LoggerQueue for experiment history has been initialized and registered.\n", + "16/07/2026-09:29:52.828 INFO:weightslab.components.checkpoint_manager:__init__: CheckpointManager initialized at /tmp/tmp3abnx3ay\n", + "16/07/2026-09:29:52.829 INFO:weightslab.src:watch_or_edit: Registered new checkpoint manager in ledger\n", + "16/07/2026-09:29:52.830 INFO:root:set_log_directory: Log file moved from /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log to /tmp/tmp3abnx3ay/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:52.832 INFO:root:set_log_directory: Log directory updated to: /tmp/tmp3abnx3ay\n", + "16/07/2026-09:29:52.833 INFO:root:set_log_directory: Log file: /tmp/tmp3abnx3ay/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:52.835 INFO:weightslab.trainer.trainer_services:_run_security_preflight: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "[gRPC] Security preflight checks:\n", + "\tTLS: DISABLED (unencrypted traffic)\n", + "\tAuth tokens: NONE configured\n", + "\t! WARNING: GRPC_TLS_ENABLED=0. Traffic will be unencrypted. Use only for development.\n", + "\t! WARNING: No GRPC_AUTH_TOKEN/GRPC_AUTH_TOKENS configured. Only transport-level trust (TLS/mTLS) will protect RPC access.\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "16/07/2026-09:29:52.837 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Watchdogs disabled via WEIGHTSLAB_DISABLE_WATCHDOGS.\n", + "16/07/2026-09:29:52.839 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Thread callback started\n", + "16/07/2026-09:29:52.840 INFO:weightslab.trainer.trainer_services:grpc_serve: [gRPC] Server started with watchdogs disabled (host=0.0.0.0 port=50051 workers=None)\n", + "16/07/2026-09:29:52.841 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Creating ThreadPoolExecutor with 6 worker threads (n_workers_grpc=None, max_concurrent_rpcs=None)\n", + "16/07/2026-09:29:52.842 WARNING:weightslab.src:serve: Running in a notebook/Colab with serving_grpc=True but serving_bore=False. Weights Studio runs on your OWN machine and cannot reach this backend directly. Pass serving_bore=True to open a tunnel and sync with the UI: wl.serve(serving_grpc=True, serving_bore=True).\n", + "16/07/2026-09:29:52.852 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Server object created\n", + "16/07/2026-09:29:52.854 INFO:weightslab.backend.cli:cli_serve: cli_thread_started\n", + "16/07/2026-09:29:52.859 INFO:weightslab.trainer.services.agent.agent:__init__: Initializing DataManipulationAgent\n", + "16/07/2026-09:29:52.860 INFO:weightslab.trainer.services.agent.agent:_setup_model_schema: [Agent] components.get('model') returned None (ledger-registered model names: []). Model-related agent requests will report 'no model registered'. If a model IS registered under a name other than the experiment name / 'experiment' / 'main', ExperimentContext.ensure_components()'s model-resolution heuristic won't find it.\n", + "16/07/2026-09:29:52.867 WARNING:weightslab.trainer.services.agent.agent:_load_config: Error loading config from /usr/local/lib/python3.12/dist-packages: [Errno 21] Is a directory: '/usr/local/lib/python3.12/dist-packages'\n", + "16/07/2026-09:29:52.869 INFO:weightslab.trainer.services.agent.agent:_load_config: \n", + "\n", + "# #######################################\n", + "# #######################################\n", + "Agent initialized from configuration /content/agent_config.yaml: \n", + "\tFinal Agent Configuration: Preferred Provider=openrouter, \n", + "\tFallback to Local=True, \n", + "\tOpenRouter Model=~google/gemini-flash-latest with:\n", + "\t\tAPI Key=None\n", + "\t\tBase URL=https://openrouter.ai/api/v1, \n", + "\tOllama Model=llama3.2:3b\n", + "# #######################################\n", + "# #######################################\n", + "\n", + "16/07/2026-09:29:52.870 INFO:weightslab.trainer.services.agent.agent:_setup_providers: Setting up Ollama with model llama3.2:3b\n", + "16/07/2026-09:29:52.915 INFO:weightslab.components.global_monitoring:resume: \n", + "Attempting to resume training...\n", + "16/07/2026-09:29:52.916 INFO:weightslab.components.checkpoint_manager:update_experiment_hash: First time initialization; skipping hash update.\n", + "16/07/2026-09:29:52.917 INFO:weightslab.components.experiment_hash:has_changed: Experiment configuration changed: {'hp'}\n", + "16/07/2026-09:29:52.918 INFO:weightslab.components.experiment_hash:generate_hash: Generated experiment hash: 1b111ac40000000000000000- (HP: 1b111ac4, Model: 00000000, Data: 00000000)\n", + "16/07/2026-09:29:52.919 INFO:weightslab.components.checkpoint_manager:update_experiment_hash: Initial experiment hash set: 1b111ac4-00000000-00000000\n", + "16/07/2026-09:29:52.919 INFO:weightslab.components.checkpoint_manager:update_experiment_hash: Changed components: {'hp'}\n", + "16/07/2026-09:29:52.922 INFO:weightslab.components.checkpoint_manager:_save_changes: Dumping hyperparameters config...\n", + "16/07/2026-09:29:52.924 INFO:weightslab.components.checkpoint_manager:save_config: Saved config: 1b111ac4_config.yaml with exp_hash prefix: 1b111ac4\n", + "16/07/2026-09:29:52.925 WARNING:weightslab.components.checkpoint_manager:_save_changes: Could not save weights: no model available\n", + "16/07/2026-09:29:52.931 INFO:weightslab.components.checkpoint_manager:save_logger_snapshot: Saved logger snapshot: /tmp/tmp3abnx3ay/checkpoints/loggers/loggers.manifest.json (1 chunks)\n", + "16/07/2026-09:29:52.935 INFO:weightslab.components.global_monitoring:resume: Hashes by module: ['1b111ac4', '00000000', '00000000']\n", + "16/07/2026-09:29:52.935 WARNING:weightslab.components.global_monitoring:resume: Cannot resume training: experiment hash not computed yet for every modules ['1b111ac4', '00000000', '00000000'].\n", + "16/07/2026-09:29:53.028 INFO:weightslab.trainer.services.agent.agent:_setup_providers: [Agent] Ollama enabled: llama3.2:3b\n", + "16/07/2026-09:29:53.031 INFO:weightslab.trainer.services.data_service:__init__: DataService initialized.\n", + "16/07/2026-09:29:53.036 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Servicer added\n", + "16/07/2026-09:29:53.040 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Attempting to bind to 0.0.0.0:50051\n", + "16/07/2026-09:29:53.044 WARNING:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] TLS disabled; using insecure transport on 0.0.0.0:50051\n", + "16/07/2026-09:29:53.046 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Port 50051 bound successfully.\n", + "16/07/2026-09:29:53.051 INFO:weightslab.trainer.trainer_services:serving_thread_callback: [gRPC] Server started and listening on 0.0.0.0:50051\n", + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "\u001b[34m\u001b[1mengine/trainer: \u001b[0magnostic_nms=False, amp=False, angle=1.0, augment=False, auto_augment=None, batch=16, bgr=0.0, box=7.5, cache=False, cfg=None, classes=None, close_mosaic=10, cls=0.5, cls_pw=0.0, cls_remap=True, compile=False, conf=None, copy_paste=0.0, copy_paste_mode=flip, cos_lr=False, cutmix=0.0, data=/content/datasets/brain-tumor/brain-tumor.yaml, degrees=0.0, deterministic=True, device=cpu, dfl=1.5, dis=6.0, distill_model=None, dnn=False, dropout=0.0, dynamic=False, embed=None, end2end=None, epochs=10, erasing=0.0, exist_ok=False, fliplr=0.0, flipud=0.0, format=torchscript, fraction=1.0, freeze=None, hsv_h=0.0, hsv_s=0.0, hsv_v=0.0, imgsz=640, iou=0.7, keras=False, kobj=1.0, line_width=None, lr0=0.001, lrf=0.01, mask_ratio=4, max_det=300, mixup=0.0, mode=train, model=yolo11n.pt, momentum=0.937, mosaic=0.0, multi_scale=0.0, name=brain-tumor-4, nbs=64, nms=False, opset=None, optimize=False, optimizer=SGD, overlap_mask=True, patience=100, perspective=0.0, plots=True, pose=12.0, pretrained=True, profile=False, project=./wl_logs, quantize=None, rect=False, resume=False, retina_masks=False, rle=1.0, save=True, save_conf=False, save_crop=False, save_dir=/content/runs/detect/wl_logs/brain-tumor-4, save_frames=False, save_json=False, save_period=-1, save_txt=False, scale=0.0, seed=0, shear=0.0, show=False, show_boxes=True, show_conf=True, show_labels=True, simplify=True, single_cls=False, source=None, split=val, stream_buffer=False, task=detect, time=None, tracker=tracktrack.yaml, translate=0.0, val=True, verbose=True, vid_stride=1, visualize=False, warmup_bias_lr=0.1, warmup_epochs=3.0, warmup_momentum=0.8, weight_decay=0.0005, workers=0, workspace=None\n", + "Overriding model.yaml nc=80 with nc=2\n", + "\n", + " from n params module arguments \n", + " 0 -1 1 464 ultralytics.nn.modules.conv.Conv [3, 16, 3, 2] \n", + " 1 -1 1 4672 ultralytics.nn.modules.conv.Conv [16, 32, 3, 2] \n", + " 2 -1 1 6640 ultralytics.nn.modules.block.C3k2 [32, 64, 1, False, 0.25] \n", + " 3 -1 1 36992 ultralytics.nn.modules.conv.Conv [64, 64, 3, 2] \n", + " 4 -1 1 26080 ultralytics.nn.modules.block.C3k2 [64, 128, 1, False, 0.25] \n", + " 5 -1 1 147712 ultralytics.nn.modules.conv.Conv [128, 128, 3, 2] \n", + " 6 -1 1 87040 ultralytics.nn.modules.block.C3k2 [128, 128, 1, True] \n", + " 7 -1 1 295424 ultralytics.nn.modules.conv.Conv [128, 256, 3, 2] \n", + " 8 -1 1 346112 ultralytics.nn.modules.block.C3k2 [256, 256, 1, True] \n", + " 9 -1 1 164608 ultralytics.nn.modules.block.SPPF [256, 256, 5] \n", + " 10 -1 1 249728 ultralytics.nn.modules.block.C2PSA [256, 256, 1] \n", + " 11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n", + " 12 [-1, 6] 1 0 ultralytics.nn.modules.conv.Concat [1] \n", + " 13 -1 1 111296 ultralytics.nn.modules.block.C3k2 [384, 128, 1, False] \n", + " 14 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n", + " 15 [-1, 4] 1 0 ultralytics.nn.modules.conv.Concat [1] \n", + " 16 -1 1 32096 ultralytics.nn.modules.block.C3k2 [256, 64, 1, False] \n", + " 17 -1 1 36992 ultralytics.nn.modules.conv.Conv [64, 64, 3, 2] \n", + " 18 [-1, 13] 1 0 ultralytics.nn.modules.conv.Concat [1] \n", + " 19 -1 1 86720 ultralytics.nn.modules.block.C3k2 [192, 128, 1, False] \n", + " 20 -1 1 147712 ultralytics.nn.modules.conv.Conv [128, 128, 3, 2] \n", + " 21 [-1, 10] 1 0 ultralytics.nn.modules.conv.Concat [1] \n", + " 22 -1 1 378880 ultralytics.nn.modules.block.C3k2 [384, 256, 1, True] \n", + " 23 [16, 19, 22] 1 431062 ultralytics.nn.modules.head.Detect [2, 16, None, [64, 128, 256]] \n", + "YOLO11n summary: 182 layers, 2,590,230 parameters, 2,590,214 gradients, 6.4 GFLOPs\n", + "\n", + "Transferred 448/499 items from pretrained weights\n", + "Freezing layer 'model.23.dfl.conv.weight'\n", + "\u001b[34m\u001b[1mtrain: \u001b[0mFast image access ✅ (ping: 0.0±0.0 ms, read: 115.3±57.6 MB/s, size: 3.6 KB)\n", + "\u001b[K\u001b[34m\u001b[1mtrain: \u001b[0mScanning /content/datasets/brain-tumor/labels/train.cache... 878 images, 15 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 893/893 101.2Mit/s 0.0s\n", + "\u001b[34m\u001b[1malbumentations: \u001b[0mBlur(p=0.01, blur_limit=(3, 7)), MedianBlur(p=0.01, blur_limit=(3, 7)), ToGray(p=0.01, method='weighted_average', num_output_channels=3), CLAHE(p=0.01, clip_limit=(1.0, 4.0), tile_grid_size=(8, 8))\n", + "16/07/2026-09:29:54.526 INFO:weightslab.data.data_samples_with_ops:__init__: [DataSampleTrackingWrapper] H5 persistence enabled at /tmp/tmp3abnx3ay/checkpoints/data/data.h5\n", + "16/07/2026-09:29:54.529 INFO:weightslab.data.data_samples_with_ops:__init__: Preloading sample statistics for PHYSICAL indices in split 'train_loader' with: preload_labels=True, preload_metadata=True...\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Initializing ledger for split 'train_loader': 100%|██████████| 893/893 [00:00<00:00, 13970.17it/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-09:29:54.600 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] Registering split 'train_loader' with 893 samples.\n", + "16/07/2026-09:29:54.615 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] After annotation expansion: 893 samples → 984 annotation rows.\n", + "16/07/2026-09:29:54.616 INFO:weightslab.data.h5_array_store:__init__: [H5ArrayStore] Initialized with cache limit: 2048MB\n", + "16/07/2026-09:29:54.636 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loading checkpoint 1b111ac400000000...\n", + "16/07/2026-09:29:54.637 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Target: HP=1b111ac4 MODEL=00000000 DATA=00000000\n", + "16/07/2026-09:29:54.638 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Current: HP=1b111ac4 MODEL=00000000 DATA=00000000\n", + "16/07/2026-09:29:54.639 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Model architecture unchanged, using current model\n", + "16/07/2026-09:29:54.639 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Config unchanged, using current config\n", + "16/07/2026-09:29:54.641 WARNING:weightslab.components.checkpoint_manager:load_checkpoint: [WARNING] Data snapshot file not found: /tmp/tmp3abnx3ay/checkpoints/data/00000000/00000000_data_snapshot.json\n", + "16/07/2026-09:29:54.641 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loaded components: set()\n", + "\u001b[34m\u001b[1mval: \u001b[0mFast image access ✅ (ping: 0.0±0.0 ms, read: 62.7±38.0 MB/s, size: 3.9 KB)\n", + "\u001b[K\u001b[34m\u001b[1mval: \u001b[0mScanning /content/datasets/brain-tumor/labels/val.cache... 223 images, 0 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 223/223 32.3Mit/s 0.0s\n", + "16/07/2026-09:29:54.660 INFO:weightslab.data.data_samples_with_ops:__init__: [DataSampleTrackingWrapper] H5 persistence enabled at /tmp/tmp3abnx3ay/checkpoints/data/data.h5\n", + "16/07/2026-09:29:54.662 INFO:weightslab.data.data_samples_with_ops:__init__: Preloading sample statistics for PHYSICAL indices in split 'val_loader' with: preload_labels=True, preload_metadata=True...\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n", + "Initializing ledger for split 'val_loader': 100%|██████████| 223/223 [00:00<00:00, 5362.24it/s]" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-09:29:54.708 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] Registering split 'val_loader' with 223 samples.\n", + "16/07/2026-09:29:54.714 INFO:weightslab.data.dataframe_manager:register_split: [LedgeredDataFrameManager] After annotation expansion: 223 samples → 257 annotation rows.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16/07/2026-09:29:54.734 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loading checkpoint 1b111ac400000000...\n", + "16/07/2026-09:29:54.736 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Target: HP=1b111ac4 MODEL=00000000 DATA=00000000\n", + "16/07/2026-09:29:54.739 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Current: HP=1b111ac4 MODEL=00000000 DATA=00000000\n", + "16/07/2026-09:29:54.740 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Model architecture unchanged, using current model\n", + "16/07/2026-09:29:54.742 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Config unchanged, using current config\n", + "16/07/2026-09:29:54.743 WARNING:weightslab.components.checkpoint_manager:load_checkpoint: [WARNING] Data snapshot file not found: /tmp/tmp3abnx3ay/checkpoints/data/00000000/00000000_data_snapshot.json\n", + "16/07/2026-09:29:54.746 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loaded components: set()\n", + "\u001b[34m\u001b[1moptimizer:\u001b[0m SGD(lr=0.001, momentum=0.937) with parameter groups 81 weight(decay=0.0), 88 weight(decay=0.0005), 87 bias(decay=0.0)\n", + "Plotting labels to /content/runs/detect/wl_logs/brain-tumor-4/labels.jpg... \n", + "16/07/2026-09:29:55.639 INFO:weightslab.backend.model_interface:__init__: Using checkpoint manager from ledger\n", + "16/07/2026-09:29:55.644 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loading checkpoint 1b111ac400000000...\n", + "16/07/2026-09:29:55.645 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Target: HP=1b111ac4 MODEL=00000000 DATA=00000000\n", + "16/07/2026-09:29:55.645 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Current: HP=1b111ac4 MODEL=00000000 DATA=00000000\n", + "16/07/2026-09:29:55.646 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Model architecture unchanged, using current model\n", + "16/07/2026-09:29:55.647 WARNING:weightslab.components.checkpoint_manager:load_checkpoint: [WARNING] No weight files found for 00000000\n", + "16/07/2026-09:29:55.648 INFO:weightslab.components.checkpoint_manager:load_checkpoint: [-] Config unchanged, using current config\n", + "16/07/2026-09:29:55.648 INFO:weightslab.components.checkpoint_manager:load_checkpoint: Loaded components: set()\n", + "Image sizes 640 train, 640 val\n", + "Using 0 dataloader workers\n", + "Logging results to \u001b[1m/content/runs/detect/wl_logs/brain-tumor-4\u001b[0m\n", + "Starting training for 10 epochs...\n", + "16/07/2026-09:29:55.661 INFO:weightslab.backend.dataloader_interface:set_batch_size: Batch size updated: 16 -> 4 (Loader: train_loader)\n", + "Closing dataloader mosaic\n", + "\u001b[34m\u001b[1malbumentations: \u001b[0mBlur(p=0.01, blur_limit=(3, 7)), MedianBlur(p=0.01, blur_limit=(3, 7)), ToGray(p=0.01, method='weighted_average', num_output_channels=3), CLAHE(p=0.01, clip_limit=(1.0, 4.0), tile_grid_size=(8, 8))\n", + "\n", + " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n", + "\u001b[K: 0% ──────────── 0/56 1:03\n" + ] + }, + { + "output_type": "error", + "ename": "KeyboardInterrupt", + "evalue": "", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_6805/1732828701.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[0;31m# Read back the (now live) hyperparameters and hand training to WLAwareTrainer.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mYOLO\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcfg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"model\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 32\u001b[0;31m results = model.train(\n\u001b[0m\u001b[1;32m 33\u001b[0m \u001b[0mtrainer\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mWLAwareTrainer\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 34\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcfg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"data\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/ultralytics/engine/model.py\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(self, trainer, **kwargs)\u001b[0m\n\u001b[1;32m 814\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 815\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 816\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 817\u001b[0m \u001b[0;31m# Update model and cfg after training\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 818\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mRANK\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/ultralytics/engine/trainer.py\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 239\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 240\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 241\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_train\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 242\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 243\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_setup_scheduler\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/ultralytics/engine/trainer.py\u001b[0m in \u001b[0;36m_do_train\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 430\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 431\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpbar\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 432\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_callbacks\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"on_train_batch_start\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 433\u001b[0m \u001b[0;31m# Warmup\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 434\u001b[0m \u001b[0mni\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mi\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mnb\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mepoch\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/ultralytics/engine/trainer.py\u001b[0m in \u001b[0;36mrun_callbacks\u001b[0;34m(self, event)\u001b[0m\n\u001b[1;32m 210\u001b[0m \u001b[0;34m\"\"\"Run all existing callbacks associated with a particular event.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 211\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcallback\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcallbacks\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 212\u001b[0;31m \u001b[0mcallback\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 213\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 214\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/weightslab/integrations/ultralytics/trainer.py\u001b[0m in \u001b[0;36m_on_train_batch_start\u001b[0;34m(trainer)\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_on_train_batch_start\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrainer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 99\u001b[0;31m \u001b[0mwl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mguard_training_context\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__enter__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 100\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_on_train_batch_end\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrainer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/weightslab/components/global_monitoring.py\u001b[0m in \u001b[0;36m__enter__\u001b[0;34m(self, f)\u001b[0m\n\u001b[1;32m 224\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 225\u001b[0m \u001b[0mpause_controller\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresume\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mforce\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 226\u001b[0;31m \u001b[0mpause_controller\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwait_if_paused\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 227\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marchitecture_guard\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__enter__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 228\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/weightslab/components/global_monitoring.py\u001b[0m in \u001b[0;36mwait_if_paused\u001b[0;34m(self, skip_pause)\u001b[0m\n\u001b[1;32m 119\u001b[0m \u001b[0;31m# Also wakes up early when an evaluation is pending/running so the\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 120\u001b[0m \u001b[0;31m# training loop and dataloaders can service evaluation mode.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 121\u001b[0;31m \u001b[0;32mwhile\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_event\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwait\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 122\u001b[0m \u001b[0;31m# Timeout occurred – check for evaluation request before looping\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 123\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.12/threading.py\u001b[0m in \u001b[0;36mwait\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 653\u001b[0m \u001b[0msignaled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_flag\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 654\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0msignaled\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 655\u001b[0;31m \u001b[0msignaled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_cond\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwait\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 656\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msignaled\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 657\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.12/threading.py\u001b[0m in \u001b[0;36mwait\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 357\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 358\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mtimeout\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 359\u001b[0;31m \u001b[0mgotit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwaiter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0macquire\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 360\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 361\u001b[0m \u001b[0mgotit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwaiter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0macquire\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n.pt\", # pretrained checkpoint\n", + " \"data\": \"/content/datasets/brain-tumor/brain-tumor.yaml\", # Ultralytics auto-downloads (~4 MB)\n", + " \"image_size\": 640,\n", + " \"epochs\": 10,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"brain-tumor\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " device='cpu',\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iCI7FL86Ks7v" + }, + "source": [ + "## Ultralytics Citation\n", + "\n", + "```bibtex\n", + "@dataset{Jocher_Ultralytics_Datasets_2024,\n", + " author = {Jocher, Glenn and Rizwan, Muhammad},\n", + " license = {AGPL-3.0},\n", + " title = {Ultralytics Datasets: Brain-tumor Detection Dataset},\n", + " url = {https://docs.ultralytics.com/datasets/detect/brain-tumor/},\n", + " version = {1.0.0},\n", + " year = {2024}\n", + "}\n", + "```" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-carparts-segmentation-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-carparts-segmentation-dataset.ipynb new file mode 100644 index 00000000..a15cf670 --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-carparts-segmentation-dataset.ipynb @@ -0,0 +1,1704 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO segmentation model on the carparts-seg dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Carparts Segmentation · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO segmentation model on the [carparts-seg](https://docs.ultralytics.com/) dataset (segmenting individual car body parts) **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareSegmentationTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "Ultralytics auto-downloads the dataset the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically. For reference:" + ] + }, + { + "cell_type": "markdown", + "source": [ + "```yaml\n", + "# Ultralytics YOLO 🚀, AGPL-3.0 license\n", + "# Carparts-seg dataset by Ultralytics\n", + "# Documentation: https://docs.ultralytics.com/datasets/segment/carparts-seg/\n", + "# Example usage: yolo train data=carparts-seg.yaml\n", + "# parent\n", + "# ├── ultralytics\n", + "# └── datasets\n", + "# └── carparts-seg ← downloads here (133 MB)\n", + "\n", + "# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\n", + "path: carparts-seg # dataset root dir\n", + "train: images/train # train images (relative to 'path') 3516 images\n", + "val: images/val # val images (relative to 'path') 276 images\n", + "test: images/test # test images (relative to 'path') 401 images\n", + "\n", + "# Classes\n", + "names:\n", + " 0: back_bumper\n", + " 1: back_door\n", + " 2: back_glass\n", + " 3: back_left_door\n", + " 4: back_left_light\n", + " 5: back_light\n", + " 6: back_right_door\n", + " 7: back_right_light\n", + " 8: front_bumper\n", + " 9: front_door\n", + " 10: front_glass\n", + " 11: front_left_door\n", + " 12: front_left_light\n", + " 13: front_light\n", + " 14: front_right_door\n", + " 15: front_right_light\n", + " 16: hood\n", + " 17: left_mirror\n", + " 18: object\n", + " 19: right_mirror\n", + " 20: tailgate\n", + " 21: trunk\n", + " 22: wheel\n", + "\n", + "# Download script/URL (optional)\n", + "download: https://github.com/ultralytics/assets/releases/download/v0.0.0/carparts-seg.zip\n", + "```" + ], + "metadata": { + "id": "h8go3HNgN0WU" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareSegmentationTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n-seg.pt\", # pretrained checkpoint\n", + " \"data\": \"carparts-seg.yaml\", # Ultralytics auto-downloads\n", + " \"image_size\": 640,\n", + " \"epochs\": 10,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareSegmentationTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareSegmentationTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"carparts-seg\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + } + ] +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-construction-ppe-detection-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-construction-ppe-detection-dataset.ipynb new file mode 100644 index 00000000..29104bcf --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-construction-ppe-detection-dataset.ipynb @@ -0,0 +1,1716 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO detector on the construction-ppe dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Construction PPE Detection · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO detector on the [construction-ppe](https://docs.ultralytics.com/) dataset (detecting personal protective equipment (helmets, vests, gloves, …) on construction sites) **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "Ultralytics auto-downloads the dataset the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically. For reference:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h8go3HNgN0WU" + }, + "source": [ + "```yaml\n", + "# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license\n", + "\n", + "# Construction-PPE dataset by Ultralytics\n", + "# Documentation: https://docs.ultralytics.com/datasets/detect/construction-ppe/\n", + "# Example usage: yolo train data=construction-ppe.yaml\n", + "# parent\n", + "# ├── ultralytics\n", + "# └── datasets\n", + "# └── construction-ppe ← downloads here (178.4 MB)\n", + "\n", + "# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\n", + "path: construction-ppe # dataset root dir\n", + "train: images/train # train images (relative to 'path') 1132 images\n", + "val: images/val # val images (relative to 'path') 143 images\n", + "test: images/test # test images (relative to 'path') 141 images\n", + "\n", + "# Classes\n", + "names:\n", + " 0: helmet\n", + " 1: gloves\n", + " 2: vest\n", + " 3: boots\n", + " 4: goggles\n", + " 5: none\n", + " 6: Person\n", + " 7: no_helmet\n", + " 8: no_goggle\n", + " 9: no_gloves\n", + " 10: no_boots\n", + "\n", + "# Download script/URL (optional)\n", + "download: https://github.com/ultralytics/assets/releases/download/v0.0.0/construction-ppe.zip\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n.pt\", # pretrained checkpoint\n", + " \"data\": \"construction-ppe.yaml\", # Ultralytics auto-downloads\n", + " \"image_size\": 640,\n", + " \"epochs\": 5,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"construction-ppe\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vlHp09Nueb3d" + }, + "source": [ + "## Citation,License and Attribution\n", + "\n", + "```bibtex\n", + "@dataset{Dalvi_Construction_PPE_Dataset_2025,\n", + " author = {Mrunmayee Dalvi and Niyati Singh and Sahil Bhingarde and Ketaki Chalke},\n", + " title = {Construction-PPE: Personal Protective Equipment Detection Dataset},\n", + " month = {January},\n", + " year = {2025},\n", + " version = {1.0.0},\n", + " license = {AGPL-3.0},\n", + " url = {https://docs.ultralytics.com/datasets/detect/construction-ppe/},\n", + " publisher = {Ultralytics}\n", + "}\n", + "```\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-crack-segmentation-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-crack-segmentation-dataset.ipynb new file mode 100644 index 00000000..71021aa4 --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-crack-segmentation-dataset.ipynb @@ -0,0 +1,1684 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO segmentation model on the crack-seg dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Crack Segmentation · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO segmentation model on the [crack-seg](https://docs.ultralytics.com/) dataset (segmenting cracks in concrete/asphalt for infrastructure inspection) **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareSegmentationTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "Ultralytics auto-downloads the dataset the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically. For reference:" + ] + }, + { + "cell_type": "markdown", + "source": [ + "```yaml\n", + "# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license\n", + "\n", + "# Crack-seg dataset by Ultralytics\n", + "# Documentation: https://docs.ultralytics.com/datasets/segment/crack-seg/\n", + "# Example usage: yolo train data=crack-seg.yaml\n", + "# parent\n", + "# ├── ultralytics\n", + "# └── datasets\n", + "# └── crack-seg ← downloads here (91.6 MB)\n", + "\n", + "# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\n", + "path: crack-seg # dataset root dir\n", + "train: images/train # train images (relative to 'path') 3717 images\n", + "val: images/val # val images (relative to 'path') 112 images\n", + "test: images/test # test images (relative to 'path') 200 images\n", + "\n", + "# Classes\n", + "names:\n", + " 0: crack\n", + "\n", + "# Download script/URL (optional)\n", + "download: https://github.com/ultralytics/assets/releases/download/v0.0.0/crack-seg.zip\n", + "```" + ], + "metadata": { + "id": "h8go3HNgN0WU" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareSegmentationTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n-seg.pt\", # pretrained checkpoint\n", + " \"data\": \"crack-seg.yaml\", # Ultralytics auto-downloads\n", + " \"image_size\": 640,\n", + " \"epochs\": 3,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareSegmentationTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareSegmentationTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"crack-seg\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + } + ] +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-homeobjects-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-homeobjects-dataset.ipynb new file mode 100644 index 00000000..e57e277b --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-homeobjects-dataset.ipynb @@ -0,0 +1,1716 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO detector on the HomeObjects-3K dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# HomeObjects-3K Detection · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO detector on the [HomeObjects-3K](https://docs.ultralytics.com/) dataset (detecting common indoor household objects) **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "Ultralytics auto-downloads the dataset the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically. For reference:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h8go3HNgN0WU" + }, + "source": [ + "```yaml\n", + "# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license\n", + "\n", + "# HomeObjects-3K dataset by Ultralytics\n", + "# Documentation: https://docs.ultralytics.com/datasets/detect/homeobjects-3k/\n", + "# Example usage: yolo train data=HomeObjects-3K.yaml\n", + "# parent\n", + "# ├── ultralytics\n", + "# └── datasets\n", + "# └── homeobjects-3K ← downloads here (390 MB)\n", + "\n", + "# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\n", + "path: homeobjects-3K # dataset root dir\n", + "train: images/train # train images (relative to 'path') 2285 images\n", + "val: images/val # val images (relative to 'path') 404 images\n", + "\n", + "# Classes\n", + "names:\n", + " 0: bed\n", + " 1: sofa\n", + " 2: chair\n", + " 3: table\n", + " 4: lamp\n", + " 5: tv\n", + " 6: laptop\n", + " 7: wardrobe\n", + " 8: window\n", + " 9: door\n", + " 10: potted plant\n", + " 11: photo frame\n", + "\n", + "# Download script/URL (optional)\n", + "download: https://github.com/ultralytics/assets/releases/download/v0.0.0/homeobjects-3K.zip\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n.pt\", # pretrained checkpoint\n", + " \"data\": \"HomeObjects-3K.yaml\", # Ultralytics auto-downloads\n", + " \"image_size\": 640,\n", + " \"epochs\": 3,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"homeobjects-3k\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vlHp09Nueb3d" + }, + "source": [ + "## 💡Citation\n", + "\n", + "```bibtex\n", + "@dataset{Jocher_Ultralytics_Datasets_2025,\n", + " author = {Jocher, Glenn and Rizwan, Muhammad},\n", + " license = {AGPL-3.0},\n", + " month = {May},\n", + " title = {Ultralytics Datasets: HomeObjects-3K Detection Dataset},\n", + " url = {https://docs.ultralytics.com/datasets/detect/homeobject-3k/},\n", + " version = {1.0.0},\n", + " year = {2025}\n", + "}\n", + "```\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "A100", + "machine_shape": "hm", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-kitti-detection-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-kitti-detection-dataset.ipynb new file mode 100644 index 00000000..d7c2edaa --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-kitti-detection-dataset.ipynb @@ -0,0 +1,1707 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO detector on the KITTI dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# KITTI Detection · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO detector on the [KITTI](https://docs.ultralytics.com/) dataset (detecting cars, pedestrians and cyclists in autonomous-driving street scenes) **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "Ultralytics auto-downloads the dataset the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically. For reference:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h8go3HNgN0WU" + }, + "source": [ + "```yaml\n", + "# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license\n", + "\n", + "# Kitti dataset by Karlsruhe Institute of Technology and Toyota Technological Institute at Chicago\n", + "# Documentation: https://docs.ultralytics.com/datasets/detect/kitti/\n", + "# Example usage: yolo train data=kitti.yaml\n", + "# parent\n", + "# ├── ultralytics\n", + "# └── datasets\n", + "# └── kitti ← downloads here (390.5 MB)\n", + "\n", + "# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\n", + "path: kitti # dataset root dir\n", + "train: images/train # train images (relative to 'path') 5985 images\n", + "val: images/val # val images (relative to 'path') 1496 images\n", + "\n", + "names:\n", + " 0: car\n", + " 1: van\n", + " 2: truck\n", + " 3: pedestrian\n", + " 4: person_sitting\n", + " 5: cyclist\n", + " 6: tram\n", + " 7: misc\n", + "\n", + "# Download script/URL (optional)\n", + "download: https://github.com/ultralytics/assets/releases/download/v0.0.0/kitti.zip\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n.pt\", # pretrained checkpoint\n", + " \"data\": \"kitti.yaml\", # Ultralytics auto-downloads\n", + " \"image_size\": 640,\n", + " \"epochs\": 10,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"kitti\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vlHp09Nueb3d" + }, + "source": [ + "## Citation,License and Attribution\n", + "\n", + "```bibtex\n", + "@article{Geiger2013IJRR,\n", + " author = {Andreas Geiger and Philip Lenz and Christoph Stiller and Raquel Urtasun},\n", + " title = {Vision meets Robotics: The KITTI Dataset},\n", + " journal = {International Journal of Robotics Research (IJRR)},\n", + " year = {2013}\n", + "}\n", + "```" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-medical-pills-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-medical-pills-dataset.ipynb new file mode 100644 index 00000000..3e79aa5c --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-medical-pills-dataset.ipynb @@ -0,0 +1,1701 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO detector on the medical-pills dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Medical Pills Detection · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO detector on the [medical-pills](https://docs.ultralytics.com/) dataset (detecting medical pills for pharmaceutical quality control) **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "Ultralytics auto-downloads the dataset the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically. For reference:" + ] + }, + { + "cell_type": "markdown", + "source": [ + "```yaml\n", + "# Ultralytics YOLO 🚀, AGPL-3.0 license\n", + "# Medical-pills dataset by Ultralytics\n", + "# Documentation: https://docs.ultralytics.com/datasets/detect/medical-pills/\n", + "# Example usage: yolo train data=medical-pills.yaml\n", + "# parent\n", + "# ├── ultralytics\n", + "# └── datasets\n", + "# └── medical-pills ← downloads here (8.19 MB)\n", + "\n", + "# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\n", + "path: medical-pills # dataset root dir\n", + "train: images/train # train images (relative to 'path') 92 images\n", + "val: images/val # val images (relative to 'path') 23 images\n", + "\n", + "# Classes\n", + "names:\n", + " 0: pill\n", + "\n", + "# Download script/URL (optional)\n", + "download: https://github.com/ultralytics/assets/releases/download/v0.0.0/medical-pills.zip\n", + "```" + ], + "metadata": { + "id": "h8go3HNgN0WU" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n.pt\", # pretrained checkpoint\n", + " \"data\": \"medical-pills.yaml\", # Ultralytics auto-downloads\n", + " \"image_size\": 640,\n", + " \"epochs\": 20,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"medical-pills\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Citation\n", + "\n", + "```bibtex\n", + "@dataset{Jocher_Ultralytics_Datasets_2024,\n", + " author = {Jocher, Glenn and Rizwan, Muhammad},\n", + " license = {AGPL-3.0},\n", + " month = {Dec},\n", + " title = {Ultralytics Datasets:Medical-pills Detection Dataset},\n", + " url = {https://docs.ultralytics.com/datasets/detect/medical-pills/},\n", + " version = {1.0.0},\n", + " year = {2024}\n", + "}\n" + ], + "metadata": { + "id": "vlHp09Nueb3d" + } + } + ] +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-package-segmentation-dataset.ipynb b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-package-segmentation-dataset.ipynb new file mode 100644 index 00000000..ebcc2224 --- /dev/null +++ b/weightslab/examples/Notebooks/Ultralytics/wl-how-to-train-ultralytics-yolo-on-package-segmentation-dataset.ipynb @@ -0,0 +1,1683 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \"WeightsLab\n", + "\n", + " \"License\"\n", + " \"Stars\"\n", + " \"Version\"\n", + "
\n", + " \"Open\n", + "\n", + " Train an Ultralytics YOLO segmentation model on the package-seg dataset and stream every sample, prediction and metric live into Weights Studio to inspect, edit and evolve your run.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Package Segmentation · Ultralytics YOLO + WeightsLab\n", + "\n", + "This notebook trains a YOLO segmentation model on the [package-seg](https://docs.ultralytics.com/) dataset (segmenting shipping packages on conveyors for logistics automation) **through the WeightsLab training pipeline**.\n", + "\n", + "Instead of Ultralytics' one-shot `train`/`predict` flow, we hand training to `WLAwareSegmentationTrainer`. It wraps the train/val dataloaders, logs a **per-sample** signal for every image (loss, prediction stats, tags) and ships live prediction overlays to **Weights Studio** — where you inspect the data grid, remove/weight samples, edit the model and resume, all while training runs.\n", + "\n", + "Ultralytics auto-downloads the dataset the first time you train." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o7YFKEN1Ks7Y" + }, + "source": [ + "## 1 · Setup\n", + "\n", + "Install WeightsLab (which pulls in Ultralytics) and verify the environment." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "71be394c", + "outputId": "c9ef4767-84f5-4bea-e395-9e11766002f2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "%pip install setuptools wheel" + ], + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (75.2.0)\n", + "Requirement already satisfied: wheel in /usr/local/lib/python3.12/dist-packages (0.47.0)\n", + "Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from wheel) (26.2)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install git+https://github.com/GrayboxTech/weightslab.git@landingcollab" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "jV1oi_PQN0xK", + "outputId": "eda8eca1-f4b3-4ffd-eaa6-0f67ce857153" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting git+https://github.com/GrayboxTech/weightslab.git@landingcollab\n", + " Cloning https://github.com/GrayboxTech/weightslab.git (to revision landingcollab) to /tmp/pip-req-build-z4ba7cx9\n", + " Running command git clone --filter=blob:none --quiet https://github.com/GrayboxTech/weightslab.git /tmp/pip-req-build-z4ba7cx9\n", + " Running command git checkout -b landingcollab --track origin/landingcollab\n", + " Switched to a new branch 'landingcollab'\n", + " Branch 'landingcollab' set up to track remote branch 'landingcollab' from 'origin'.\n", + " Resolved https://github.com/GrayboxTech/weightslab.git to commit 3f1cf143bc93e67df43adc71ab831fa71477e0d2\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: numpy<3,>=1.24 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.0.2)\n", + "Requirement already satisfied: pandas<3,>=2.2.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.2.2)\n", + "Requirement already satisfied: duckdb<2,>=1.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.2)\n", + "Requirement already satisfied: PyYAML<7,>=6.0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (6.0.3)\n", + "Requirement already satisfied: dill<0.5,>=0.3.8 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.3.8)\n", + "Requirement already satisfied: zstandard<1,>=0.22 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.25.0)\n", + "Requirement already satisfied: h5py<4,>=3.10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.16.0)\n", + "Requirement already satisfied: xxhash<4.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.7.1)\n", + "Requirement already satisfied: tables<4,>=3.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (3.10.2)\n", + "Requirement already satisfied: torch<=2.9,>=2.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.9.0)\n", + "Requirement already satisfied: torchvision<1,>=0.16 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.24.0)\n", + "Requirement already satisfied: torchmetrics>=1.9 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: grpcio<2,>=1.80 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.81.1)\n", + "Requirement already satisfied: protobuf<8,>=5.28.1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (5.29.6)\n", + "Requirement already satisfied: pydantic<3,>=2.7 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (2.13.4)\n", + "Requirement already satisfied: Pillow<12,>=10 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (11.3.0)\n", + "Requirement already satisfied: graphviz<1,>=0.20 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (0.21)\n", + "Requirement already satisfied: onnx<=1.20,>=1.15 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.20.0)\n", + "Requirement already satisfied: tqdm<5,>=4.66 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (4.67.3)\n", + "Requirement already satisfied: python-dotenv<2,>=1 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.2.2)\n", + "Requirement already satisfied: langchain-core<2,>=0.3 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.4.9)\n", + "Requirement already satisfied: langchain-ollama<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.1.0)\n", + "Requirement already satisfied: langchain-openai<2,>=0.2 in /usr/local/lib/python3.12/dist-packages (from weightslab==1.3.3.dev8) (1.3.5)\n", + "Requirement already satisfied: typing-extensions~=4.12 in /usr/local/lib/python3.12/dist-packages (from grpcio<2,>=1.80->weightslab==1.3.3.dev8) (4.15.0)\n", + "Requirement already satisfied: jsonpatch<2.0.0,>=1.33.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.33)\n", + "Requirement already satisfied: langchain-protocol>=0.0.17 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.0.18)\n", + "Requirement already satisfied: langsmith<1.0.0,>=0.3.45 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.9.1)\n", + "Requirement already satisfied: packaging>=23.2.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (26.2)\n", + "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (9.1.4)\n", + "Requirement already satisfied: uuid-utils<1.0,>=0.12.0 in /usr/local/lib/python3.12/dist-packages (from langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.2)\n", + "Requirement already satisfied: ollama<1.0.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from langchain-ollama<2,>=0.2->weightslab==1.3.3.dev8) (0.6.2)\n", + "Requirement already satisfied: openai<3.0.0,>=2.45.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2.45.0)\n", + "Requirement already satisfied: tiktoken<1.0.0,>=0.7.0 in /usr/local/lib/python3.12/dist-packages (from langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.13.0)\n", + "Requirement already satisfied: ml_dtypes>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from onnx<=1.20,>=1.15->weightslab==1.3.3.dev8) (0.5.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (2026.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (2.46.4)\n", + "Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.12/dist-packages (from pydantic<3,>=2.7->weightslab==1.3.3.dev8) (0.4.2)\n", + "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (2.14.1)\n", + "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (9.0.0)\n", + "Requirement already satisfied: blosc2>=2.3.0 in /usr/local/lib/python3.12/dist-packages (from tables<4,>=3.9->weightslab==1.3.3.dev8) (4.5.1)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.29.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (75.2.0)\n", + "Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.14.0)\n", + "Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.6.1)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.1.6)\n", + "Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2025.3.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (9.10.2.21)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.4.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.3.3.83)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (10.3.9.90)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (11.7.3.90)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.5.8.93)\n", + "Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (0.7.1)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (2.27.5)\n", + "Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.3.20)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.90)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (12.8.93)\n", + "Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.13.1.3)\n", + "Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.5.0)\n", + "Requirement already satisfied: lightning-utilities>=0.15.3 in /usr/local/lib/python3.12/dist-packages (from torchmetrics>=1.9->weightslab==1.3.3.dev8) (0.15.3)\n", + "Requirement already satisfied: ndindex in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.10.1)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.2.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.32.4)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (13.9.4)\n", + "Requirement already satisfied: textual in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (6.2.1)\n", + "Requirement already satisfied: textual-plotext in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (1.0.1)\n", + "Requirement already satisfied: threadpoolctl in /usr/local/lib/python3.12/dist-packages (from blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.6.0)\n", + "Requirement already satisfied: jsonpointer>=1.9 in /usr/local/lib/python3.12/dist-packages (from jsonpatch<2.0.0,>=1.33.0->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.1.1)\n", + "Requirement already satisfied: anyio>=3.5.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (4.14.0)\n", + "Requirement already satisfied: distro>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.28.1)\n", + "Requirement already satisfied: orjson>=3.9.14 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.11.9)\n", + "Requirement already satisfied: requests-toolbelt>=1.0.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.3.1)\n", + "Requirement already satisfied: websockets>=15.0 in /usr/local/lib/python3.12/dist-packages (from langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (15.0.1)\n", + "Requirement already satisfied: jiter<1,>=0.10.0 in /usr/local/lib/python3.12/dist-packages (from openai<3.0.0,>=2.45.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (0.15.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas<3,>=2.2.2->weightslab==1.3.3.dev8) (1.17.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (1.3.0)\n", + "Requirement already satisfied: regex in /usr/local/lib/python3.12/dist-packages (from tiktoken<1.0.0,>=0.7.0->langchain-openai<2,>=0.2->weightslab==1.3.3.dev8) (2025.11.3)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch<=2.9,>=2.1->weightslab==1.3.3.dev8) (3.0.3)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.5.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (3.18)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (2026.6.17)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith<1.0.0,>=0.3.45->langchain-core<2,>=0.3->weightslab==1.3.3.dev8) (0.16.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (3.4.7)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.5.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.2.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.12/dist-packages (from rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.20.0)\n", + "Requirement already satisfied: platformdirs<5,>=3.6.0 in /usr/local/lib/python3.12/dist-packages (from textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (4.10.0)\n", + "Requirement already satisfied: plotext<6.0.0,>=5.2.8 in /usr/local/lib/python3.12/dist-packages (from textual-plotext->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (5.3.2)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=2.2.0->rich->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.1.2)\n", + "Requirement already satisfied: linkify-it-py<3,>=1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.1.0)\n", + "Requirement already satisfied: mdit-py-plugins>=0.5.0 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (0.6.1)\n", + "Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py<3,>=1->markdown-it-py[linkify,plugins]>=2.1.0->textual->blosc2>=2.3.0->tables<4,>=3.9->weightslab==1.3.3.dev8) (2.0.0)\n", + "Building wheels for collected packages: weightslab\n", + " Building wheel for weightslab (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for weightslab: filename=weightslab-1.3.3.dev8-py3-none-any.whl size=2827715 sha256=5c4e7c14bbf95a48b9a8c86b5a8c744af67e1eb395197cbe79d3e443c21cf86e\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-__zwgf7x/wheels/4d/c7/cd/817c2745790555e7b6c532f5b6055d47567f9416fdfc65879b\n", + "Successfully built weightslab\n", + "Installing collected packages: weightslab\n", + " Attempting uninstall: weightslab\n", + " Found existing installation: weightslab 1.3.3.dev7\n", + " Uninstalling weightslab-1.3.3.dev7:\n", + " Successfully uninstalled weightslab-1.3.3.dev7\n", + "Successfully installed weightslab-1.3.3.dev8\n" + ] + }, + { + "output_type": "display_data", + "data": { + "application/vnd.colab-display-data+json": { + "pip_warning": { + "packages": [ + "weightslab" + ] + }, + "id": "b7fce274042740d19087972fe9c01c9b" + } + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pJhvREbaKs7f", + "outputId": "651baa36-9ebb-49da-830b-e6c8a6e2b64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ultralytics 8.4.96 🚀 Python-3.12.13 torch-2.9.0+cu128 CPU (Intel Xeon CPU @ 2.20GHz)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.5/107.7 GB disk)\n", + "16/07/2026-09:29:29.683 INFO:root:setup_logging: WeightsLab logging initialized - Log file: /tmp/tmp6meuzqdz/weightslab_logs/weightslab_20260716_092929.log\n", + "16/07/2026-09:29:29.685 INFO:weightslab:: WeightsLab package initialized - Log level: INFO, Log to file: True\n", + "16/07/2026-09:29:29.686 INFO:weightslab:: \n", + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ │\n", + "│ \u001b[32m$$\\ $$\\ \u001b[0m $$\\ $$\\ $$\\ \u001b[31m$$\\ \u001b[0m $$\\ │\n", + "│ \u001b[32m$$ | $\\ $$ |\u001b[0m \\__| $$ | $$ | \u001b[31m$$ | \u001b[0m $$ | │\n", + "│ \u001b[32m$$ |$$$\\ $$ |\u001b[0m $$$$$$\\ $$\\ $$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$\\ $$$$$$$\\ │\n", + "│ \u001b[32m$$ $$ $$\\$$ |\u001b[0m$$ __$$\\ $$ |$$ __$$\\ $$ __$$\\ \\_$$ _| $$ _____|\u001b[31m$$ | \u001b[0m \\____$$\\ $$ __$$\\ │\n", + "│ \u001b[32m$$$$ _$$$$ |\u001b[0m$$$$$$$$ |$$ |$$ / $$ |$$ | $$ | $$ | \\$$$$$$\\ \u001b[31m$$ | \u001b[0m $$$$$$$ |$$ | $$ | │\n", + "│ \u001b[32m$$$ / \\$$$ |\u001b[0m$$ ____|$$ |$$ | $$ |$$ | $$ | $$ |$$\\ \\____$$\\ \u001b[31m$$ | \u001b[0m$$ __$$ |$$ | $$ | │\n", + "│ \u001b[32m$$ / \\$$ |\u001b[0m\\$$$$$$$\\ $$ |\\$$$$$$$ |$$ | $$ | \\$$$$ |$$$$$$$ |\u001b[31m$$$$$$$$\\ \u001b[0m\\$$$$$$$ |$$$$$$$ | │\n", + "│ \u001b[32m\\__/ \\__|\u001b[0m \\_______|\\__| \\____$$ |\\__| \\__| \\____/ \\_______/ \u001b[31m\\________|\u001b[0m \\_______|\\_______/ │\n", + "│ \u001b[32m \u001b[0m $$\\ $$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\$$$$$$ |\u001b[31m\u001b[0m │\n", + "│ \u001b[32m \u001b[0m \\______/\u001b[31m\u001b[0m │\n", + "│ │\n", + "│ Inspect - Edit - Evolve Neural Networks │\n", + "│ │\n", + "╰─── By GrayBx, v1.3.3.dev8 ──────────────────────────────────────────────────────────────────────────╯\n", + "\n", + "\n", + "16/07/2026-09:29:29.690 INFO:weightslab.utils.telemetry:ping_import: WeightsLab uses anonymous usage data (package version used, and OS name). Set WL_NO_TELEMETRY=1 to disable.\n" + ] + } + ], + "source": [ + "# Install WeightsLab. Colab already ships torch, torchvision, numpy, scikit-learn\n", + "# and Pillow, so nothing extra is needed here.\n", + "# %pip install weightslab\n", + "# %pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ \"weightslab==1.3.3.dev8\"\n", + "%pip install ultralytics\n", + "\n", + "import ultralytics\n", + "ultralytics.checks()\n", + "\n", + "import weightslab as wl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 · Dataset\n", + "\n", + "The dataset is described by a small YAML file that Ultralytics resolves and downloads automatically. For reference:" + ] + }, + { + "cell_type": "markdown", + "source": [ + "```yaml\n", + "# Ultralytics YOLO 🚀, AGPL-3.0 license\n", + "# Package-seg dataset by Ultralytics\n", + "# Documentation: https://docs.ultralytics.com/datasets/segment/package-seg/\n", + "# Example usage: yolo train data=package-seg.yaml\n", + "# parent\n", + "# ├── ultralytics\n", + "# └── datasets\n", + "# └── package-seg ← downloads here (103 MB)\n", + "\n", + "# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]\n", + "path: package-seg # dataset root dir\n", + "train: images/train # train images (relative to 'path') 1920 images\n", + "val: images/val # val images (relative to 'path') 89 images\n", + "test: images/test # test images (relative to 'path') 188 images\n", + "\n", + "# Classes\n", + "names:\n", + " 0: package\n", + "\n", + "# Download script/URL (optional)\n", + "download: https://github.com/ultralytics/assets/releases/download/v0.0.0/package-seg.zip\n", + "```" + ], + "metadata": { + "id": "h8go3HNgN0WU" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rH79t30YKs7i" + }, + "source": [ + "## 3 · Train through WeightsLab\n", + "\n", + "We register the run's hyperparameters with `wl.watch_or_edit(...)` (so they become live-editable from Studio), start the backend services with `wl.serve()`, then hand training to `WLAwareTrainer` via the standard `YOLO(...).train(trainer=...)` entry point.\n", + "\n", + "> **Connect Weights Studio** — run `weightslab ui launch` locally (opens `http://localhost:5173`) *before or during* training to watch the run live. On **Colab**, serve over a public tunnel instead: use `wl.serve(serving_grpc=True, serving_bore=True)` and connect your local Studio with `weightslab tunnel bore.pub:PORT` (the port is printed when serving starts).\n", + "\n", + "Data augmentations are disabled so each sample maps cleanly to its ground truth in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ.setdefault(\"WL_PRELOAD_IMAGE_OVERVIEW\", \"0\")\n", + "os.environ.setdefault(\"WEIGHTSLAB_LOG_LEVEL\", \"WARNING\")\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "from weightslab.integrations.ultralytics import WLAwareSegmentationTrainer\n", + "from ultralytics import YOLO\n", + "\n", + "# Hyperparameters registered with WeightsLab -> live-editable from Weights Studio.\n", + "cfg = {\n", + " \"model\": \"yolo11n-seg.pt\", # pretrained checkpoint\n", + " \"data\": \"package-seg.yaml\", # Ultralytics auto-downloads\n", + " \"image_size\": 640,\n", + " \"epochs\": 10,\n", + " # Per-sample prediction overlays streamed to Studio (NMS on the train set).\n", + " \"signals_cfg\": {\n", + " \"train_nms\": {\"conf_thres\": 0.25, \"iou_thres\": 0.45, \"max_nms\": 7},\n", + " },\n", + "}\n", + "wl.watch_or_edit(cfg, flag=\"hyperparameters\", defaults=cfg, poll_interval=1.0)\n", + "\n", + "# Start the WeightsLab backend services that Weights Studio connects to.\n", + "wl.serve(serving_grpc=True)\n", + "# Colab -> local Studio: wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "wl.start_training() # equivalent to pressing \"play\" in Studio\n", + "\n", + "# Read back the (now live) hyperparameters and hand training to WLAwareSegmentationTrainer.\n", + "model = YOLO(cfg[\"model\"])\n", + "results = model.train(\n", + " trainer=WLAwareSegmentationTrainer,\n", + " data=str(cfg[\"data\"]),\n", + " imgsz=cfg[\"image_size\"],\n", + " epochs=int(cfg[\"epochs\"]),\n", + " project=\"./wl_logs\", name=\"package-seg\", # -> WL log_dir/name\n", + " workers=0, # WL invariant (parent-process uid counter)\n", + " optimizer=\"SGD\", lr0=0.001, amp=False,\n", + " # All augmentations off for a clean sample <-> ground-truth association.\n", + " mosaic=0.0, mixup=0.0, copy_paste=0.0,\n", + " hsv_h=0.0, hsv_s=0.0, hsv_v=0.0,\n", + " degrees=0.0, translate=0.0, scale=0.0, shear=0.0, perspective=0.0,\n", + " flipud=0.0, fliplr=0.0, erasing=0.0, auto_augment=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f-Uw_s_8Ks7m" + }, + "source": [ + "## 4 · Explore the data grid\n", + "\n", + "The table below is exactly the data behind Weights Studio's **grid explorer**: one row per sample, keyed by split (`origin`) and sample id, with the per-sample loss, prediction stats and tags accumulated during training. This is the ledger that Studio reads from — here we render it inline with pandas.\n", + "\n", + "The image thumbnails and bounding-box overlays themselves are rendered by **Weights Studio** (which streams full previews over gRPC); the link below opens the interactive grid there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "collapsed": true, + "id": "uOJdXGA3Ks7o", + "outputId": "26652fa3-6a15-43ce-9d75-034eaba21ccd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1241 samples tracked\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " prediction prediction_raw \\\n", + "sample_id annotation_id \n", + "0 0 None None \n", + "1 0 None None \n", + "2 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "3 0 None None \n", + "4 0 None None \n", + "5 0 None None \n", + "6 0 None None \n", + "7 0 None None \n", + "8 0 None None \n", + "9 0 None None \n", + "10 0 None None \n", + "11 0 None None \n", + "12 0 None None \n", + "13 0 None None \n", + "14 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "15 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "16 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "17 0 None None \n", + "18 0 None None \n", + "19 0 None None \n", + "20 0 None None \n", + " 1 None None \n", + " 2 None None \n", + " 3 None None \n", + "21 0 None None \n", + "22 0 None None \n", + "23 0 None None \n", + "24 0 None None \n", + "25 0 None None \n", + " 1 None None \n", + " 2 None None \n", + "26 0 None None \n", + "27 0 None None \n", + "28 0 None None \n", + "29 0 None None \n", + "30 0 None None \n", + "31 0 None None \n", + "32 0 None None \n", + "33 0 None None \n", + "34 0 None None \n", + "35 0 None None \n", + "36 0 None None \n", + "\n", + " target \\\n", + "sample_id annotation_id \n", + "0 0 [0.2335685, 0.254695, 0.4553995, 0.43075103, 1... \n", + "1 0 [0.251174, 0.24061048, 0.44366202, 0.4307515, ... \n", + "2 0 None \n", + " 1 [0.523474, 0.3978875, 0.634976, 0.48122054, 1.... \n", + " 2 [0.40610352, 0.415493, 0.5234745, 0.522301, 1.... \n", + "3 0 [0.403756, 0.3826295, 0.637324, 0.5152585, 1.0... \n", + "4 0 [0.4436615, 0.3814555, 0.5938965, 0.4518785, 1... \n", + "5 0 [0.6044605, 0.3990615, 0.67253554, 0.46596247,... \n", + "6 0 [0.410798, 0.4436625, 0.556338, 0.51173747, 1.... \n", + "7 0 [0.650235, 0.4166665, 0.73826295, 0.48356748, ... \n", + "8 0 [0.64788747, 0.388498, 0.7582165, 0.49413198, ... \n", + "9 0 [0.6807515, 0.40140802, 0.75704247, 0.45774597... \n", + "10 0 [0.431925, 0.1701875, 0.561033, 0.3180745, 1.0... \n", + "11 0 [0.42018852, 0.34507048, 0.5434275, 0.4941315,... \n", + "12 0 [0.3732395, 0.348592, 0.54812247, 0.46009403, ... \n", + "13 0 [0.37793398, 0.3814555, 0.48122, 0.45892054, 1... \n", + "14 0 None \n", + " 1 [0.43427247, 0.3556335, 0.51173747, 0.43075046... \n", + " 2 [0.4929575, 0.44718304, 0.5375585, 0.485915, 1... \n", + "15 0 None \n", + " 1 [0.629108, 0.39788702, 0.67840403, 0.453051, 1... \n", + " 2 [0.4530515, 0.4107985, 0.58568054, 0.5105635, ... \n", + "16 0 None \n", + " 1 [0.45070451, 0.3967135, 0.5762915, 0.5152585, ... \n", + " 2 [0.63732404, 0.403756, 0.684272, 0.46009403, 1... \n", + "17 0 [0.43779355, 0.3826295, 0.61032856, 0.5223005,... \n", + "18 0 [0.4401405, 0.374413, 0.6068075, 0.529343, 1.0... \n", + "19 0 [0.536385, 0.4495305, 0.600939, 0.49999946, 0.... \n", + "20 0 None \n", + " 1 [0.52347445, 0.4424885, 0.6150235, 0.5281695, ... \n", + " 2 [0.456573, 0.515258, 0.504695, 0.562206, 0.0, ... \n", + " 3 [0.504695, 0.44248852, 0.54342705, 0.48004752,... \n", + "21 0 [0.54107946, 0.456573, 0.6103285, 0.511737, 0.... \n", + "22 0 [0.3298125, 0.2042255, 0.4225355, 0.2957745, 0... \n", + "23 0 [0.517606, 0.18661949, 0.615024, 0.2875585, 1.... \n", + "24 0 [0.51173747, 0.18779299, 0.6126765, 0.308685, ... \n", + "25 0 None \n", + " 1 [0.30985948, 0.3626765, 0.3861505, 0.43896753,... \n", + " 2 [0.41314548, 0.3427225, 0.48122054, 0.42018753... \n", + "26 0 [0.25821602, 0.30046952, 0.46830997, 0.4577465... \n", + "27 0 [0.2734745, 0.2828635, 0.44718352, 0.4577465, ... \n", + "28 0 [0.5915495, 0.23591551, 0.6455405, 0.2887325, ... \n", + "29 0 [0.5903755, 0.231221, 0.6420185, 0.280517, 1.0... \n", + "30 0 [0.40493003, 0.2018775, 0.469484, 0.2699525, 1... \n", + "31 0 [0.39554, 0.19483551, 0.483568, 0.2887325, 1.0... \n", + "32 0 [0.4025825, 0.212441, 0.45539945, 0.269953, 1.... \n", + "33 0 [0.2265255, 0.2136145, 0.3438965, 0.30751148, ... \n", + "34 0 [0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0] \n", + "35 0 [0.6079815, 0.320423, 0.7969485, 0.489437, 1.0... \n", + "36 0 [0.6185445, 0.30164298, 0.7758215, 0.494131, 1... \n", + "\n", + " discarded origin task_type last_seen group_id \\\n", + "sample_id annotation_id \n", + "0 0 False train_loader -1.0 0 \n", + "1 0 False train_loader -1.0 1 \n", + "2 0 False train_loader -1.0 2 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "3 0 False train_loader -1.0 3 \n", + "4 0 False train_loader -1.0 4 \n", + "5 0 False train_loader -1.0 5 \n", + "6 0 False train_loader -1.0 6 \n", + "7 0 False train_loader -1.0 7 \n", + "8 0 False train_loader -1.0 8 \n", + "9 0 False train_loader -1.0 9 \n", + "10 0 False train_loader -1.0 10 \n", + "11 0 False train_loader -1.0 11 \n", + "12 0 False train_loader -1.0 12 \n", + "13 0 False train_loader -1.0 13 \n", + "14 0 False train_loader -1.0 14 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "15 0 False train_loader -1.0 15 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "16 0 False train_loader -1.0 16 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "17 0 False train_loader -1.0 17 \n", + "18 0 False train_loader -1.0 18 \n", + "19 0 False train_loader -1.0 19 \n", + "20 0 False train_loader -1.0 20 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + " 3 None NaN None NaN None \n", + "21 0 False train_loader -1.0 21 \n", + "22 0 False train_loader -1.0 22 \n", + "23 0 False train_loader -1.0 23 \n", + "24 0 False train_loader -1.0 24 \n", + "25 0 False train_loader -1.0 25 \n", + " 1 None NaN None NaN None \n", + " 2 None NaN None NaN None \n", + "26 0 False train_loader -1.0 26 \n", + "27 0 False train_loader -1.0 27 \n", + "28 0 False train_loader -1.0 28 \n", + "29 0 False train_loader -1.0 29 \n", + "30 0 False train_loader -1.0 30 \n", + "31 0 False train_loader -1.0 31 \n", + "32 0 False train_loader -1.0 32 \n", + "33 0 False train_loader -1.0 33 \n", + "34 0 False train_loader -1.0 34 \n", + "35 0 False train_loader -1.0 35 \n", + "36 0 False train_loader -1.0 36 \n", + "\n", + " member_rank \\\n", + "sample_id annotation_id \n", + "0 0 0.0 \n", + "1 0 0.0 \n", + "2 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "3 0 0.0 \n", + "4 0 0.0 \n", + "5 0 0.0 \n", + "6 0 0.0 \n", + "7 0 0.0 \n", + "8 0 0.0 \n", + "9 0 0.0 \n", + "10 0 0.0 \n", + "11 0 0.0 \n", + "12 0 0.0 \n", + "13 0 0.0 \n", + "14 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "15 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "16 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "17 0 0.0 \n", + "18 0 0.0 \n", + "19 0 0.0 \n", + "20 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + " 3 NaN \n", + "21 0 0.0 \n", + "22 0 0.0 \n", + "23 0 0.0 \n", + "24 0 0.0 \n", + "25 0 0.0 \n", + " 1 NaN \n", + " 2 NaN \n", + "26 0 0.0 \n", + "27 0 0.0 \n", + "28 0 0.0 \n", + "29 0 0.0 \n", + "30 0 0.0 \n", + "31 0 0.0 \n", + "32 0 0.0 \n", + "33 0 0.0 \n", + "34 0 0.0 \n", + "35 0 0.0 \n", + "36 0 0.0 \n", + "\n", + " img_path \\\n", + "sample_id annotation_id \n", + "0 0 /content/datasets/brain-tumor/images/train/000... \n", + "1 0 /content/datasets/brain-tumor/images/train/000... \n", + "2 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "3 0 /content/datasets/brain-tumor/images/train/000... \n", + "4 0 /content/datasets/brain-tumor/images/train/000... \n", + "5 0 /content/datasets/brain-tumor/images/train/000... \n", + "6 0 /content/datasets/brain-tumor/images/train/000... \n", + "7 0 /content/datasets/brain-tumor/images/train/000... \n", + "8 0 /content/datasets/brain-tumor/images/train/000... \n", + "9 0 /content/datasets/brain-tumor/images/train/000... \n", + "10 0 /content/datasets/brain-tumor/images/train/000... \n", + "11 0 /content/datasets/brain-tumor/images/train/000... \n", + "12 0 /content/datasets/brain-tumor/images/train/000... \n", + "13 0 /content/datasets/brain-tumor/images/train/000... \n", + "14 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "15 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "16 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "17 0 /content/datasets/brain-tumor/images/train/000... \n", + "18 0 /content/datasets/brain-tumor/images/train/000... \n", + "19 0 /content/datasets/brain-tumor/images/train/000... \n", + "20 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 /content/datasets/brain-tumor/images/train/000... \n", + "22 0 /content/datasets/brain-tumor/images/train/000... \n", + "23 0 /content/datasets/brain-tumor/images/train/000... \n", + "24 0 /content/datasets/brain-tumor/images/train/000... \n", + "25 0 /content/datasets/brain-tumor/images/train/000... \n", + " 1 None \n", + " 2 None \n", + "26 0 /content/datasets/brain-tumor/images/train/000... \n", + "27 0 /content/datasets/brain-tumor/images/train/000... \n", + "28 0 /content/datasets/brain-tumor/images/train/000... \n", + "29 0 /content/datasets/brain-tumor/images/train/000... \n", + "30 0 /content/datasets/brain-tumor/images/train/000... \n", + "31 0 /content/datasets/brain-tumor/images/train/000... \n", + "32 0 /content/datasets/brain-tumor/images/train/000... \n", + "33 0 /content/datasets/brain-tumor/images/train/000... \n", + "34 0 /content/datasets/brain-tumor/images/train/000... \n", + "35 0 /content/datasets/brain-tumor/images/train/000... \n", + "36 0 /content/datasets/brain-tumor/images/train/000... \n", + "\n", + " cls \n", + "sample_id annotation_id \n", + "0 0 [[1.0]] \n", + "1 0 [[1.0]] \n", + "2 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "3 0 [[1.0]] \n", + "4 0 [[1.0]] \n", + "5 0 [[1.0]] \n", + "6 0 [[1.0]] \n", + "7 0 [[1.0]] \n", + "8 0 [[1.0]] \n", + "9 0 [[1.0]] \n", + "10 0 [[1.0]] \n", + "11 0 [[1.0]] \n", + "12 0 [[1.0]] \n", + "13 0 [[1.0]] \n", + "14 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "15 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "16 0 [[1.0], [1.0]] \n", + " 1 None \n", + " 2 None \n", + "17 0 [[1.0]] \n", + "18 0 [[1.0]] \n", + "19 0 [[0.0]] \n", + "20 0 [[0.0], [0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + " 3 None \n", + "21 0 [[0.0]] \n", + "22 0 [[0.0]] \n", + "23 0 [[1.0]] \n", + "24 0 [[1.0]] \n", + "25 0 [[0.0], [0.0]] \n", + " 1 None \n", + " 2 None \n", + "26 0 [[0.0]] \n", + "27 0 [[0.0]] \n", + "28 0 [[1.0]] \n", + "29 0 [[1.0]] \n", + "30 0 [[1.0]] \n", + "31 0 [[1.0]] \n", + "32 0 [[1.0]] \n", + "33 0 [[1.0]] \n", + "34 0 [[1.0]] \n", + "35 0 [[1.0]] \n", + "36 0 [[1.0]] " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predictionprediction_rawtargetdiscardedorigintask_typelast_seengroup_idmember_rankimg_pathcls
sample_idannotation_id
00NoneNone[0.2335685, 0.254695, 0.4553995, 0.43075103, 1...Falsetrain_loader-1.000.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
10NoneNone[0.251174, 0.24061048, 0.44366202, 0.4307515, ...Falsetrain_loader-1.010.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
20NoneNoneNoneFalsetrain_loader-1.020.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.523474, 0.3978875, 0.634976, 0.48122054, 1....NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.40610352, 0.415493, 0.5234745, 0.522301, 1....NoneNaNNoneNaNNoneNaNNoneNone
30NoneNone[0.403756, 0.3826295, 0.637324, 0.5152585, 1.0...Falsetrain_loader-1.030.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
40NoneNone[0.4436615, 0.3814555, 0.5938965, 0.4518785, 1...Falsetrain_loader-1.040.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
50NoneNone[0.6044605, 0.3990615, 0.67253554, 0.46596247,...Falsetrain_loader-1.050.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
60NoneNone[0.410798, 0.4436625, 0.556338, 0.51173747, 1....Falsetrain_loader-1.060.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
70NoneNone[0.650235, 0.4166665, 0.73826295, 0.48356748, ...Falsetrain_loader-1.070.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
80NoneNone[0.64788747, 0.388498, 0.7582165, 0.49413198, ...Falsetrain_loader-1.080.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
90NoneNone[0.6807515, 0.40140802, 0.75704247, 0.45774597...Falsetrain_loader-1.090.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
100NoneNone[0.431925, 0.1701875, 0.561033, 0.3180745, 1.0...Falsetrain_loader-1.0100.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
110NoneNone[0.42018852, 0.34507048, 0.5434275, 0.4941315,...Falsetrain_loader-1.0110.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
120NoneNone[0.3732395, 0.348592, 0.54812247, 0.46009403, ...Falsetrain_loader-1.0120.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
130NoneNone[0.37793398, 0.3814555, 0.48122, 0.45892054, 1...Falsetrain_loader-1.0130.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
140NoneNoneNoneFalsetrain_loader-1.0140.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.43427247, 0.3556335, 0.51173747, 0.43075046...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4929575, 0.44718304, 0.5375585, 0.485915, 1...NoneNaNNoneNaNNoneNaNNoneNone
150NoneNoneNoneFalsetrain_loader-1.0150.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.629108, 0.39788702, 0.67840403, 0.453051, 1...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.4530515, 0.4107985, 0.58568054, 0.5105635, ...NoneNaNNoneNaNNoneNaNNoneNone
160NoneNoneNoneFalsetrain_loader-1.0160.0/content/datasets/brain-tumor/images/train/000...[[1.0], [1.0]]
1NoneNone[0.45070451, 0.3967135, 0.5762915, 0.5152585, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.63732404, 0.403756, 0.684272, 0.46009403, 1...NoneNaNNoneNaNNoneNaNNoneNone
170NoneNone[0.43779355, 0.3826295, 0.61032856, 0.5223005,...Falsetrain_loader-1.0170.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
180NoneNone[0.4401405, 0.374413, 0.6068075, 0.529343, 1.0...Falsetrain_loader-1.0180.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
190NoneNone[0.536385, 0.4495305, 0.600939, 0.49999946, 0....Falsetrain_loader-1.0190.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
200NoneNoneNoneFalsetrain_loader-1.0200.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0], [0.0]]
1NoneNone[0.52347445, 0.4424885, 0.6150235, 0.5281695, ...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.456573, 0.515258, 0.504695, 0.562206, 0.0, ...NoneNaNNoneNaNNoneNaNNoneNone
3NoneNone[0.504695, 0.44248852, 0.54342705, 0.48004752,...NoneNaNNoneNaNNoneNaNNoneNone
210NoneNone[0.54107946, 0.456573, 0.6103285, 0.511737, 0....Falsetrain_loader-1.0210.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
220NoneNone[0.3298125, 0.2042255, 0.4225355, 0.2957745, 0...Falsetrain_loader-1.0220.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
230NoneNone[0.517606, 0.18661949, 0.615024, 0.2875585, 1....Falsetrain_loader-1.0230.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
240NoneNone[0.51173747, 0.18779299, 0.6126765, 0.308685, ...Falsetrain_loader-1.0240.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
250NoneNoneNoneFalsetrain_loader-1.0250.0/content/datasets/brain-tumor/images/train/000...[[0.0], [0.0]]
1NoneNone[0.30985948, 0.3626765, 0.3861505, 0.43896753,...NoneNaNNoneNaNNoneNaNNoneNone
2NoneNone[0.41314548, 0.3427225, 0.48122054, 0.42018753...NoneNaNNoneNaNNoneNaNNoneNone
260NoneNone[0.25821602, 0.30046952, 0.46830997, 0.4577465...Falsetrain_loader-1.0260.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
270NoneNone[0.2734745, 0.2828635, 0.44718352, 0.4577465, ...Falsetrain_loader-1.0270.0/content/datasets/brain-tumor/images/train/000...[[0.0]]
280NoneNone[0.5915495, 0.23591551, 0.6455405, 0.2887325, ...Falsetrain_loader-1.0280.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
290NoneNone[0.5903755, 0.231221, 0.6420185, 0.280517, 1.0...Falsetrain_loader-1.0290.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
300NoneNone[0.40493003, 0.2018775, 0.469484, 0.2699525, 1...Falsetrain_loader-1.0300.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
310NoneNone[0.39554, 0.19483551, 0.483568, 0.2887325, 1.0...Falsetrain_loader-1.0310.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
320NoneNone[0.4025825, 0.212441, 0.45539945, 0.269953, 1....Falsetrain_loader-1.0320.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
330NoneNone[0.2265255, 0.2136145, 0.3438965, 0.30751148, ...Falsetrain_loader-1.0330.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
340NoneNone[0.634976, 0.349765, 0.7723, 0.456573, 1.0, 1.0]Falsetrain_loader-1.0340.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
350NoneNone[0.6079815, 0.320423, 0.7969485, 0.489437, 1.0...Falsetrain_loader-1.0350.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
360NoneNone[0.6185445, 0.30164298, 0.7758215, 0.494131, 1...Falsetrain_loader-1.0360.0/content/datasets/brain-tumor/images/train/000...[[1.0]]
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "repr_error": "Out of range float values are not JSON compliant: nan" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "🔗 Open the full grid in Weights Studio" + ] + }, + "metadata": {} + } + ], + "source": [ + "import pandas as pd\n", + "from IPython.display import display, HTML\n", + "from weightslab.backend.ledgers import get_dataframe\n", + "\n", + "# `get_df_view()` is the same per-sample DataFrame that powers Studio's grid.\n", + "dfm = get_dataframe()\n", + "grid = dfm.get_df_view() if hasattr(dfm, \"get_df_view\") else pd.DataFrame()\n", + "print(f\"{len(grid)} samples tracked\")\n", + "display(grid.head(50))\n", + "\n", + "# Open the live, interactive grid (with image + bbox previews) in Weights Studio.\n", + "# Studio must be running locally (`weightslab ui launch`).\n", + "display(HTML(\n", + " '🔗 Open the full grid in Weights Studio'\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FXQ0gBOSKs7t" + }, + "source": [ + "> **Can clicking a grid row here jump to that sample in Studio?** Not out of the box today. The notebook and Studio are separate processes, and the link above opens Studio at its root. Per-sample deep-linking (e.g. `http://localhost:5173/?sample=` selecting and scrolling to that row) is a small frontend addition to Weights Studio — the sample `uid` is already the grid's index here, so the notebook side is ready for it once Studio accepts the query param." + ] + } + ] +} \ No newline at end of file diff --git a/weightslab/examples/Notebooks/Usecases/ws-segmentation-loss-shapes-classification.ipynb b/weightslab/examples/Notebooks/Usecases/ws-segmentation-loss-shapes-classification.ipynb index dccbdf01..359da8bd 100644 --- a/weightslab/examples/Notebooks/Usecases/ws-segmentation-loss-shapes-classification.ipynb +++ b/weightslab/examples/Notebooks/Usecases/ws-segmentation-loss-shapes-classification.ipynb @@ -62,13 +62,23 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "if not os.path.isdir(\"weightslab\"):\n", - " !git clone --branch torch_collab_examples https://github.com/GrayboxTech/weightslab.git\n", - "%pip install -q ./weightslab\n", - "!pip install --upgrade \"protobuf>=6.31.1\"\n", - "%pip install \"torchvision>=0.16\"\n", - "%cd weightslab/weightslab/examples/PyTorch/ws-segmentation" + "# 1 · Install WeightsLab (+ Colab compatibility overrides)\n", + "# Colab already has a compatible torch/torchvision + numpy 1.26 — do NOT reinstall them.\n", + "# One resolution, pin numpy<2 and protobuf into weightslab's range.\n", + "%pip install weightslab # --extra-index-url https://download.pytorch.org/whl/cpu\n", + "\n", + "# 2 · Import\n", + "import weightslab as wl\n", + "\n", + "# 3 · Serve to Weights Studio over a public bore tunnel.\n", + "# Prints a `bore.pub:PORT` endpoint. Then, on your own machine (Docker running):\n", + "# weightslab ui launch # opens http://localhost:5173\n", + "# weightslab tunnel bore.pub:PORT # PORT = the port printed below\n", + "wl.serve(serving_grpc=True, serving_bore=True)\n", + "\n", + "# 4 · Register first the configuration then YOUR training objects here (uncomment and fill in):\n", + "# wl.watch_or_edit(config, flag=\"hyperparameters\", poll_interval=1.0)\n", + "# wl.watch_or_edit(model, optimizer, train_loader, test_loader, ...)" ] }, { @@ -92,7 +102,6 @@ "from torch import optim\n", "from tqdm.auto import tqdm\n", "\n", - "import weightslab as wl\n", "from weightslab.components.global_monitoring import (\n", " guard_training_context, guard_testing_context,\n", ")\n", @@ -404,7 +413,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 7. Serve and train\n", + "## 7. Train\n", "\n", "Watch the progress bar: before step 505 the `seg_loss_shape_classifier` stays silent; once the\n", "warm-up gate opens it starts tagging samples by loss shape (visible in Studio and in the exported\n", @@ -417,7 +426,6 @@ "metadata": {}, "outputs": [], "source": [ - "wl.serve(serving_grpc=config[\"serving_grpc\"])\n", "wl.start_training(timeout=3)\n", "\n", "steps = config[\"training_steps_to_do\"]\n", @@ -519,4 +527,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/weightslab/integrations/ultralytics/README.md b/weightslab/integrations/ultralytics/README.md index 9b401a4b..ef9e464c 100644 --- a/weightslab/integrations/ultralytics/README.md +++ b/weightslab/integrations/ultralytics/README.md @@ -1,21 +1,26 @@ # WeightsLab × Ultralytics -Drop-in trainer that wires WeightsLab into UL's `YOLO.train()` — per-sample +Drop-in trainers that wire WeightsLab into UL's `YOLO.train()` — per-sample signals, live studio overlay, and ledger-driven discard control with no changes to the model or to UL's training loop. +| trainer | UL base | model | task | +|------------------------------|---------------------|------------------|----------| +| `WLAwareTrainer` | `DetectionTrainer` | `yolo*n.pt` | detect | +| `WLAwareSegmentationTrainer` | `SegmentationTrainer` | `yolo*n-seg.pt` | segment | + ## Minimal use ```python import weightslab as wl from ultralytics import YOLO -from weightslab.integrations.ultralytics import WLAwareTrainer +from weightslab.integrations.ultralytics import WLAwareTrainer # or WLAwareSegmentationTrainer wl.watch_or_edit(cfg, flag="hyperparameters", defaults=cfg) wl.serve() -YOLO("yolov8n.pt").train( - trainer=WLAwareTrainer, +YOLO("yolo11n.pt").train( # yolo11n-seg.pt for segmentation + trainer=WLAwareTrainer, # WLAwareSegmentationTrainer for segmentation data="my_dataset.yaml", imgsz=640, epochs=100, batch=16, project="./logs", name="exp", workers=0, amp=False, @@ -23,6 +28,19 @@ YOLO("yolov8n.pt").train( wl.keep_serving() ``` +## Segmentation + +`WLAwareSegmentationTrainer` reuses the detection signal pack: `v8SegmentationLoss` +routes through the same `get_assigned_targets_and_loss` sync point, `Segment` +subclasses `Detect`, and `SegmentationValidator._process_batch(preds, batch)` +matches the val-IoU tap — so per-sample **box/cls/dfl** losses and **box-IoU** +flow unchanged, plus aggregate `train/seg` and mask `(M)` val metrics. Masks are +trained on (they ride through the collate into UL's loss) but tracked/rendered at +the **bbox** level in Studio for now (`task_type="detection"`); per-sample mask +signals + mask overlays are a future addition. Any segmentation-only overlay +incompatibility is absorbed by `_ship_round`'s best-effort per-signal guards, so +it never crashes training. + ## Required train kwargs | kwarg | value | why | @@ -42,7 +60,7 @@ which becomes the WL logger's `log_dir/name`). | component | tested | notes | |-------------|---------------------|------------------------------------------------| -| Ultralytics | 8.3.x | DetectionTrainer subclass; YOLOv8/v11 detect | +| Ultralytics | 8.3.x–8.4.x | Detection + Segmentation trainers; YOLOv8/v11 | | Python | 3.11 | | | Linux | ✅ | CPU + CUDA | | Windows | ⚠️ caveats | install `torchvision` CUDA wheels (else `torchvision::nms` CUDA backend missing); `dill` cannot pickle some module graphs — set `dump_model_architecture: false` in `config.yaml` | @@ -53,8 +71,10 @@ which becomes the WL logger's `log_dir/name`). - **per-sample TRAIN signals:** `train/box_per_sample`, `train/cls_per_sample`, `train/dfl_per_sample`, live NMS predictions overlay - **per-sample VAL signals:** `val/iou_per_sample`, post-NMS predictions overlay -- **aggregate train losses:** `train/{box,cls,dfl}` (from `trainer.loss_items`) +- **aggregate train losses:** `train/{box,cls,dfl}` (+ `train/seg` for + segmentation) from `trainer.loss_items` - **aggregate val metrics:** `val/{precision,recall,mAP50,mAP50-95,fitness}` + (+ mask `_mask` variants for segmentation) ## Behavior under discard diff --git a/weightslab/integrations/ultralytics/__init__.py b/weightslab/integrations/ultralytics/__init__.py index 15f7c57e..3ec79271 100644 --- a/weightslab/integrations/ultralytics/__init__.py +++ b/weightslab/integrations/ultralytics/__init__.py @@ -1,13 +1,16 @@ """WeightsLab ↔ Ultralytics integration. -Two-name public surface: +Public surface: * `WLAwareTrainer` — UL DetectionTrainer subclass that wires WL through the canonical pattern (both train/val loaders go through `wl.watch_or_edit(flag='data')`, per-sample TRAIN + VAL signals installed, live train + val NMS prediction overlays shipped to studio). - * `WLAwareDataset` — UL YOLODataset subclass that returns the WL - preview-protocol 4-tuple and provides a PIL fallback in - `fast_get_label` for ledger-init. + * `WLAwareSegmentationTrainer` — the same wiring on UL's SegmentationTrainer + for `*-seg.pt` models (masks train through the collate; per-sample signals + are box-level today — see trainer.py). + * `WLAwareDataset` / `WLAwareSegmentationDataset` — UL YOLODataset + subclasses that return the WL preview-protocol 4-tuple and provide a PIL + fallback in `fast_get_label` for ledger-init. Minimal user surface: @@ -19,7 +22,7 @@ wl.serve() YOLO(cfg["model"]).train( - trainer=WLAwareTrainer, + trainer=WLAwareTrainer, # or WLAwareSegmentationTrainer data=cfg["data_root"], imgsz=640, epochs=1000, batch=4, project="./logs", name="exp", # → WL log_dir/name workers=0, # WL invariant (parent-process uid counter) @@ -28,7 +31,12 @@ See README.md for the supported-setup matrix. """ -from .dataset import WLAwareDataset -from .trainer import WLAwareTrainer +from .dataset import WLAwareDataset, WLAwareSegmentationDataset +from .trainer import WLAwareSegmentationTrainer, WLAwareTrainer -__all__ = ["WLAwareTrainer", "WLAwareDataset"] +__all__ = [ + "WLAwareTrainer", + "WLAwareSegmentationTrainer", + "WLAwareDataset", + "WLAwareSegmentationDataset", +] diff --git a/weightslab/integrations/ultralytics/dataset.py b/weightslab/integrations/ultralytics/dataset.py index ff4be229..f3284a2a 100644 --- a/weightslab/integrations/ultralytics/dataset.py +++ b/weightslab/integrations/ultralytics/dataset.py @@ -89,3 +89,25 @@ def get_items(self, i, include_metadata=False, include_labels=False, include_ima ) labels = _to_six_col(xyxy_np, np.asarray(data["cls"])) return image, str(i), labels, metadata + + +class WLAwareSegmentationDataset(WLAwareDataset): + """YOLO segmentation dataset that speaks WL's preview protocol. + + The parent `WLAwareDataset` already extracts the per-instance bboxes that + YOLO segmentation labels carry (`lab["bboxes"]` / `data["bboxes"]`), and + the full UL sample dict — masks included — rides through + `metadata["batch"]` untouched, so `wl_ul_dict_collate` -> UL's own + `collate_fn` still assembles the `masks` tensor the segmentation loss + needs. We therefore only need to (a) keep the segmentation dataset build + flags intact (this class is assigned onto an already-built segment dataset + via ``dataset.__class__ = WLAwareSegmentationDataset``, which preserves + ``use_segments`` / ``task``), and (b) advertise the task. + + Studio tracks and renders these runs at the **bbox** level (the per-sample + signals and overlays are box-based), so `task_type` stays ``"detection"`` + — masks are trained on but not yet streamed to the grid. When per-sample + mask signals/overlays land, flip this to ``"segmentation"``. + """ + + task_type = "detection" diff --git a/weightslab/integrations/ultralytics/signals.py b/weightslab/integrations/ultralytics/signals.py index e6a733f7..c58650aa 100644 --- a/weightslab/integrations/ultralytics/signals.py +++ b/weightslab/integrations/ultralytics/signals.py @@ -23,6 +23,7 @@ """ from __future__ import annotations +import logging from dataclasses import dataclass from typing import Any, Callable, Optional @@ -41,6 +42,9 @@ from ._utils import normalize_post_nms_preds +logger = logging.getLogger(__name__) + + # ─── overlay fallback tunables ───────────────────────────────────────── # Train overlay's NMS pulls thresholds from `model.args.{conf,iou}` (the # values you'd pass through `YOLO.train(conf=..., iou=...)`). These @@ -174,19 +178,35 @@ def _ship_round(signals, channels, batch): """Iterate the signal list, ship anything the reducers produce. Wrapped in `no_grad` because signal capture is observational — running e.g. `Detect._inference` + NMS inside the train autograd graph would keep - those tensors alive until backward and compound across steps.""" + those tensors alive until backward and compound across steps. + + Signal capture is best-effort: every reducer / preds / channel call is + guarded so a signal that can't run on a given model or task (e.g. a + detection-style bbox overlay attempted on a `Segment` head whose + `_inference` output NMS can't parse) is skipped for that round instead + of crashing UL's training loop. Failures are logged at debug level.""" ids = batch["ids"] with th.no_grad(): for s in signals: - v = s.reduce(batch) + try: + v = s.reduce(batch) + except Exception as e: + logger.debug("Signal %s reduce failed; skipping round: %s", s.name, e) + continue if v is None: continue kw = {"batch_ids": ids} if s.preds is not None: - p = s.preds(batch) - if p is not None: - kw["preds"] = p - channels[s.name](v, **kw) + try: + p = s.preds(batch) + if p is not None: + kw["preds"] = p + except Exception as e: + logger.debug("Signal %s preds failed; shipping scalar only: %s", s.name, e) + try: + channels[s.name](v, **kw) + except Exception as e: + logger.debug("Signal %s channel emit failed: %s", s.name, e) def install_train_pipeline(model, signals: list[Signal]): diff --git a/weightslab/integrations/ultralytics/trainer.py b/weightslab/integrations/ultralytics/trainer.py index 6efc7aaa..2fac84cc 100644 --- a/weightslab/integrations/ultralytics/trainer.py +++ b/weightslab/integrations/ultralytics/trainer.py @@ -1,24 +1,36 @@ -"""WLAwareTrainer — UL DetectionTrainer subclass that wires WL through the -canonical pattern. +"""WL-aware UL trainers — DetectionTrainer / SegmentationTrainer subclasses +that wire WL through the canonical pattern. -What this trainer does for you: - * Builds both `train_loader` and `test_loader` through +What these trainers do for you: + * Build both `train_loader` and `test_loader` through `wl.watch_or_edit(flag='data', loader_name=...)` so each split has its own `DataSampleTrackingWrapper` with a disjoint uid range — no cross-origin collisions in the shared ledger. - * Registers the optimizer and model with WL on `on_train_start`. - * Installs per-sample TRAIN signals (cls/box/dfl per image + live NMS + * Register the optimizer and model with WL on `on_train_start`. + * Install per-sample TRAIN signals (cls/box/dfl per image + live NMS predictions overlay). - * Installs per-sample VAL signals (per-image IoU + AP@0.5 + post-NMS + * Install per-sample VAL signals (per-image IoU + AP@0.5 + post-NMS predictions overlay). - * Ships aggregate train losses + val metrics (P/R/mAP50/mAP50-95/fitness) + * Ship aggregate train losses + val metrics (P/R/mAP50/mAP50-95/fitness) as curves through WL channels. - * Wraps each train/val batch in WL's training/testing guard contexts. + * Wrap each train/val batch in WL's training/testing guard contexts. + +Two public trainers, one shared wiring mixin: + * `WLAwareTrainer` — `detect` task (YOLO*n.pt). + * `WLAwareSegmentationTrainer` — `segment` task (YOLO*n-seg.pt). Reuses the + detection signal pack: `v8SegmentationLoss` calls the same + `get_assigned_targets_and_loss` sync point, `Segment` subclasses `Detect`, + and `SegmentationValidator._process_batch(preds, batch)` matches the + val-IoU tap — so per-sample box/cls/dfl + box-IoU flow unchanged. Masks are + trained on (they ride through the collate) but tracked at the bbox level; + per-sample mask signals/overlays are a future addition. Segmentation-only + overlay quirks are absorbed by `_ship_round`'s best-effort guards. Aggregate curves shipped via UL callbacks: - * `train/{box,cls,dfl}` from `trainer.loss_items` - * `val/{precision,recall,mAP50,mAP50-95,fitness}` from - `validator.metrics.results_dict` + * `train/{box,cls,dfl}` (+ `train/seg` for segmentation) from + `trainer.loss_items` + * `val/{precision,recall,mAP50,mAP50-95,fitness}` (+ mask `(M)` variants for + segmentation) from `validator.metrics.results_dict` """ from __future__ import annotations @@ -26,29 +38,78 @@ from torch.nn import Identity from ultralytics.models.yolo.detect import DetectionTrainer +from ultralytics.models.yolo.segment import SegmentationTrainer import weightslab as wl from weightslab.backend import ledgers from .collate import wl_ul_dict_collate -from .dataset import WLAwareDataset +from .dataset import WLAwareDataset, WLAwareSegmentationDataset from .signals import install_per_sample_signals, install_per_sample_val_signals -class WLAwareTrainer(DetectionTrainer): +# ─── per-task wiring config ───────────────────────────────────────────── +# `loss_items` order per task (drives both channel names and index mapping): +# detect : (box, cls, dfl) +# segment: (box, seg, cls, dfl, sem) ← we ship the first four; sem is 0 for +# models without a semantic head. +_WL_TRAIN_LOSS_NAMES = { + "detect": ("box", "cls", "dfl"), + "segment": ("box", "seg", "cls", "dfl"), +} + +# (results_dict key -> WL channel key). Segmentation adds the mask `(M)` metrics +# alongside the box `(B)` ones. +_WL_VAL_METRICS = { + "detect": ( + ("metrics/precision(B)", "val/precision"), + ("metrics/recall(B)", "val/recall"), + ("metrics/mAP50(B)", "val/mAP50"), + ("metrics/mAP50-95(B)", "val/mAP50-95"), + ("fitness", "val/fitness"), + ), + "segment": ( + ("metrics/precision(B)", "val/precision"), + ("metrics/recall(B)", "val/recall"), + ("metrics/mAP50(B)", "val/mAP50"), + ("metrics/mAP50-95(B)", "val/mAP50-95"), + ("metrics/precision(M)", "val/precision_mask"), + ("metrics/recall(M)", "val/recall_mask"), + ("metrics/mAP50(M)", "val/mAP50_mask"), + ("metrics/mAP50-95(M)", "val/mAP50-95_mask"), + ("fitness", "val/fitness"), + ), +} + + +class _WLTrainerMixin: + """Shared WL callback wiring + dataloader construction for UL trainers. + + Concrete subclasses set two class attributes: + * `_WL_TASK` — "detect" | "segment" + * `_WL_DATASET_CLS` — the `WLAwareDataset` subclass to re-class the + UL-built dataset onto. + """ + + _WL_TASK = "detect" + _WL_DATASET_CLS = WLAwareDataset + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + task = self._WL_TASK + train_loss_names = _WL_TRAIN_LOSS_NAMES[task] + val_metrics = _WL_VAL_METRICS[task] state = {"channels": {}} def _register_channels(): ch = state["channels"] - for n in ("box", "cls", "dfl"): + for n in train_loss_names: ch[f"train/{n}"] = wl.watch_or_edit( Identity(), flag="loss", name=f"train/{n}", log=True, ) - for key in ("precision", "recall", "mAP50", "mAP50-95", "fitness"): - ch[f"val/{key}"] = wl.watch_or_edit( - Identity(), flag="metric", name=f"val/{key}", log=True, + for _ul_key, wl_key in val_metrics: + ch[wl_key] = wl.watch_or_edit( + Identity(), flag="metric", name=wl_key, log=True, ) def _on_train_start(trainer): @@ -102,8 +163,9 @@ def _on_train_batch_end(trainer): ch = state["channels"] li = getattr(trainer, "loss_items", None) if li is not None and ch: - for i, n in enumerate(("box", "cls", "dfl")): - ch[f"train/{n}"](li[i:i+1].detach()) + for i, n in enumerate(train_loss_names): + if i < li.numel(): + ch[f"train/{n}"](li[i:i+1].detach()) wl.guard_training_context.__exit__(None, None, None) def _on_val_batch_start(validator): @@ -118,13 +180,7 @@ def _on_val_end(validator): rd = getattr(md, "results_dict", None) if md is not None else None if not rd or not ch: return - for ul_key, wl_key in ( - ("metrics/precision(B)", "val/precision"), - ("metrics/recall(B)", "val/recall"), - ("metrics/mAP50(B)", "val/mAP50"), - ("metrics/mAP50-95(B)", "val/mAP50-95"), - ("fitness", "val/fitness"), - ): + for ul_key, wl_key in val_metrics: if ul_key in rd and wl_key in ch: ch[wl_key](torch.tensor([float(rd[ul_key])])) @@ -141,14 +197,14 @@ def validate(self): # so loader len reflects the active set. ({}, 0.0) unpacks cleanly into # UL's `{**self.metrics, ...}` which (None, None) does not. if len(self.test_loader) == 0: - print("[WLAwareTrainer] val skipped: all val samples are discarded", - flush=True) + print("[%s] val skipped: all val samples are discarded" + % type(self).__name__, flush=True) return {}, 0.0 return super().validate() def get_dataloader(self, dataset_path, batch_size=16, rank=0, mode="train"): dataset = self.build_dataset(dataset_path, mode, batch_size) - dataset.__class__ = WLAwareDataset + dataset.__class__ = self._WL_DATASET_CLS is_train = (mode == "train") # Get data configuration from ledger @@ -176,6 +232,26 @@ def get_dataloader(self, dataset_path, batch_size=16, rank=0, mode="train"): preload_metadata=cfg.get('preload_metadata', True), ) - if not hasattr(loader, "reset"): - loader.reset = lambda *a, **k: None + # NOTE: no `reset` shim needed here. `loader` is a ledger Proxy whose + # __getattr__ raises AttributeError for missing attrs; + # DataLoaderInterface.reset() provides the real Ultralytics-compatible + # implementation that the proxy forwards to. return loader + + +class WLAwareTrainer(_WLTrainerMixin, DetectionTrainer): + """WL-aware YOLO **detection** trainer. Drop-in for `YOLO(...).train(trainer=...)`.""" + + _WL_TASK = "detect" + _WL_DATASET_CLS = WLAwareDataset + + +class WLAwareSegmentationTrainer(_WLTrainerMixin, SegmentationTrainer): + """WL-aware YOLO **segmentation** trainer. Drop-in for `YOLO(...-seg.pt).train(trainer=...)`. + + See the module docstring: reuses the detection signal pack; masks train but + are tracked at the bbox level for now. + """ + + _WL_TASK = "segment" + _WL_DATASET_CLS = WLAwareSegmentationDataset diff --git a/weightslab/proto/experiment_service_pb2.py b/weightslab/proto/experiment_service_pb2.py index e02bc82e..dd74b19b 100644 --- a/weightslab/proto/experiment_service_pb2.py +++ b/weightslab/proto/experiment_service_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: weightslab/proto/experiment_service.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.28.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,8 +11,8 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, + 5, + 28, 1, '', 'weightslab/proto/experiment_service.proto' diff --git a/weightslab/proto/experiment_service_pb2_grpc.py b/weightslab/proto/experiment_service_pb2_grpc.py index e75d391c..440a5a51 100644 --- a/weightslab/proto/experiment_service_pb2_grpc.py +++ b/weightslab/proto/experiment_service_pb2_grpc.py @@ -5,7 +5,7 @@ from weightslab.proto import experiment_service_pb2 as weightslab_dot_proto_dot_experiment__service__pb2 -GRPC_GENERATED_VERSION = '1.76.0' +GRPC_GENERATED_VERSION = '1.68.1' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -18,7 +18,7 @@ if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in weightslab/proto/experiment_service_pb2_grpc.py depends on' + + f' but the generated code in weightslab/proto/experiment_service_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' diff --git a/weightslab/src.py b/weightslab/src.py index 0a14b7e3..c8b67a1e 100644 --- a/weightslab/src.py +++ b/weightslab/src.py @@ -30,7 +30,7 @@ from weightslab.trainer.trainer_services import grpc_serve from weightslab.data.sample_stats import SampleStatsEx from weightslab.utils.logs import set_log_directory -from weightslab.utils.tools import detach_to_cpu +from weightslab.utils.tools import detach_to_cpu, _running_in_notebook from weightslab.backend.logger import LoggerQueue from weightslab.backend.cli import cli_serve from weightslab.backend import ledgers @@ -1485,23 +1485,6 @@ def start_training(timeout: int = None) -> None: pause_ctrl.resume() # Ensure we're not paused if start_training is called after serve -def _running_in_notebook() -> bool: - """True when running inside a Jupyter notebook kernel or Google Colab. - - Distinguishes a notebook kernel (``ZMQInteractiveShell``) / Colab from a - plain script or a terminal IPython session, so we only nudge users who can't - reach a locally-launched Weights Studio without a tunnel. - """ - if "google.colab" in sys.modules: - return True - try: - from IPython import get_ipython - shell = get_ipython() - return shell is not None and shell.__class__.__name__ == "ZMQInteractiveShell" - except Exception: - return False - - def serve(serving_cli: bool = True, serving_grpc: bool = False, spawn_cli_client: bool = False, serving_bore: bool = False, bore_port: int = None, allow_unconfigured: bool = True, **kwargs): @@ -3751,6 +3734,7 @@ def write_history( graph_name=None, experiment_hash: str | None = None, sample_id=None, + orient: str = "columns", instance_id=None, verbose: bool = False, ) -> str: @@ -4016,6 +4000,9 @@ def write_history( if write_instance: payload["instance"] = instance_rows with open(path, "w", encoding="utf-8") as fh: + # payload is a dict of record-lists (one list of row dicts per + # section); json.dump takes no `orient` (that's a pandas arg). The + # per-section records layout is the documented/tested contract. _json.dump(payload, fh, indent=2) elif fmt == "csv": @@ -4165,6 +4152,7 @@ def write_dataframe( columns=None, sample_id=None, instance_id=None, + orient: str = "columns", verbose: bool = False, loss_shape_signal: str | None = None, ) -> str: @@ -4392,7 +4380,7 @@ def write_dataframe( df_out = df_out.reset_index() if fmt == "json": - _json_str = df_out.to_json(orient="records", default_handler=str) + _json_str = df_out.to_json(orient=orient, default_handler=str) with open(path, "w", encoding="utf-8") as fh: _json.dump(_json.loads(_json_str), fh, indent=2) diff --git a/weightslab/trainer/services/agent/agent.py b/weightslab/trainer/services/agent/agent.py index dcede2c7..08aaa572 100644 --- a/weightslab/trainer/services/agent/agent.py +++ b/weightslab/trainer/services/agent/agent.py @@ -731,6 +731,13 @@ def _load_config(self): self.openrouter_base_url = os.environ.get("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1") self.openrouter_api_key = os.environ.get("OPENROUTER_API_KEY", None) self.openrouter_request_timeout = float(os.environ.get("OPENROUTER_REQUEST_TIMEOUT", "15.0")) + # Cap the completion length. OpenRouter pre-authorizes + # `max_tokens * completion_price` against the key's remaining budget + # BEFORE generating; with no cap the client requests the model's full + # output window (tens of thousands of tokens), which 402s on a + # credit/weekly-limited key even though the actual intent-planning + # response is only a few hundred tokens. Keep this modest. + self.openrouter_max_tokens = int(os.environ.get("OPENROUTER_MAX_TOKENS", "2048")) # Bias OpenRouter's upstream provider selection ("throughput"/"latency"/ # "price"); empty string lets OpenRouter choose freely (see Phase B.2). self.openrouter_provider_sort = os.environ.get("OPENROUTER_PROVIDER_SORT", "throughput") @@ -783,6 +790,7 @@ def _load_config(self): self.openrouter_base_url = a_cfg.get("openrouter_base_url", self.openrouter_base_url) self.openrouter_api_key = a_cfg.get("openrouter_api_key", self.openrouter_api_key) self.openrouter_request_timeout = float(a_cfg.get("openrouter_request_timeout", self.openrouter_request_timeout)) + self.openrouter_max_tokens = int(a_cfg.get("openrouter_max_tokens", self.openrouter_max_tokens)) self.openrouter_provider_sort = a_cfg.get("openrouter_provider_sort", self.openrouter_provider_sort) self.openrouter_structured_output = bool(a_cfg.get("openrouter_structured_output", self.openrouter_structured_output)) @@ -872,6 +880,7 @@ def _setup_providers(self): api_key=self.openrouter_api_key, base_url=openrouter_base_url, streaming=False, max_retries=1, request_timeout=self.openrouter_request_timeout, + max_tokens=self.openrouter_max_tokens, extra_body=extra_body, ) self.chain_openrouter = llm @@ -930,7 +939,17 @@ def _check_chat_provider(self, provider: str) -> "tuple[bool, str]": return False, f"{provider} client was not initialized." try: - response = chain.invoke("Reply with OK.") + # Keep the probe's reservation tiny. OpenRouter pre-authorizes + # `max_tokens * price` before generating, so a large (or unset) + # cap makes the health check 402 on a budget-limited key even + # though the model is perfectly usable for real (short) requests. + probe = chain + if provider == "openrouter" and hasattr(chain, "bind"): + try: + probe = chain.bind(max_tokens=16) + except Exception: + probe = chain + response = probe.invoke("Reply with OK.") except Exception as e: _LOGGER.warning(f"[{provider}] connectivity check failed: {e}") return False, f"{provider} connectivity check failed: {e}" diff --git a/weightslab/trainer/services/data_service.py b/weightslab/trainer/services/data_service.py index 454e2a12..474e7cab 100755 --- a/weightslab/trainer/services/data_service.py +++ b/weightslab/trainer/services/data_service.py @@ -860,8 +860,10 @@ def _pull_into_all_data_view_df(self): # The manager now expands samples into one row per (sample_id, annotation_id) # instance. Collapse back to one row per sample for the sample-centric UI/agent - # view, nesting per-instance signals into a dict column. - df = self._df_manager.get_collapse_annotations_to_samples_df() + # view, nesting per-instance signals into a dict column. Reuse the frame we just + # pulled — otherwise collapse would re-run get_combined_df() (full copy + buffer + # merge + proxy conversion) a second time over the whole dataset every refresh. + df = self._df_manager.get_collapse_annotations_to_samples_df(df) # Ensure sample_id is a column if it was the index df = safe_reset_index(df) diff --git a/weightslab/tunnel.py b/weightslab/tunnel.py index f86c6cb8..ff768d38 100644 --- a/weightslab/tunnel.py +++ b/weightslab/tunnel.py @@ -25,6 +25,9 @@ import sys import threading +from weightslab.utils.tools import _running_in_notebook + + logger = logging.getLogger(__name__) # Env var read as the default ENDPOINT so a bare ``weightslab tunnel`` works @@ -321,8 +324,8 @@ def serve_bore(port: int = DEFAULT_LISTEN_PORT, relay: str = _BORE_RELAY, proc = subprocess.Popen( [bore, "local", str(port), "--to", relay], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, - ) + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, start_new_session=True if _running_in_notebook() else False + ) # Bore independent of the parent process (so it keeps running if the notebook kernel restarts) _BORE_PROCS.append(proc) result = {"endpoint": None} diff --git a/weightslab/utils/tools.py b/weightslab/utils/tools.py index b57ff83a..f8f25e54 100644 --- a/weightslab/utils/tools.py +++ b/weightslab/utils/tools.py @@ -1,4 +1,5 @@ import io +import sys import xxhash import types import logging @@ -22,6 +23,23 @@ # -------------------------- Utils Functions --------------------------------- # ---------------------------------------------------------------------------- +def _running_in_notebook() -> bool: + """True when running inside a Jupyter notebook kernel or Google Colab. + + Distinguishes a notebook kernel (``ZMQInteractiveShell``) / Colab from a + plain script or a terminal IPython session, so we only nudge users who can't + reach a locally-launched Weights Studio without a tunnel. + """ + if "google.colab" in sys.modules: + return True + try: + from IPython import get_ipython + shell = get_ipython() + return shell is not None and shell.__class__.__name__ == "ZMQInteractiveShell" + except Exception: + return False + + def safe_reset_index(df: "pd.DataFrame") -> "pd.DataFrame": """Reset DataFrame index levels into columns, skipping any level whose name is already a column.