From 48d2aab271bfcf67e6fd514920e0b1d52c975e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 9 Mar 2026 14:29:15 +0000 Subject: [PATCH 001/284] feat: correction tiles --- configs/correction.yaml | 13 +++++++ configs/correction/correct_metadata.yaml | 3 ++ configs/correction/correct_tiles.yaml | 4 ++ configs/data/mmci_sl_group_1.yaml | 6 +-- configs/data/mmci_sl_group_2.yaml | 6 +-- .../correct_metadata/mmci_sl_group_1.yaml | 1 + .../correct_metadata/mmci_sl_group_2.yaml | 1 + .../correct_tiles/mmci_sl_group_1_224.yaml | 8 ++++ .../correct_tiles/mmci_sl_group_1_512.yaml | 8 ++++ .../correct_tiles/mmci_sl_group_2_224.yaml | 8 ++++ .../correct_tiles/mmci_sl_group_2_512.yaml | 8 ++++ correction/correct_metadata.py | 37 +++++++++++++++++++ correction/correct_tiles.py | 35 ++++++++++++++++++ exploration/mmci_2k.py | 1 + 14 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 configs/correction.yaml create mode 100644 configs/correction/correct_metadata.yaml create mode 100644 configs/correction/correct_tiles.yaml create mode 100644 configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml create mode 100644 configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml create mode 100644 configs/experiment/correction/correct_tiles/mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/correction/correct_tiles/mmci_sl_group_1_512.yaml create mode 100644 configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml create mode 100644 configs/experiment/correction/correct_tiles/mmci_sl_group_2_512.yaml create mode 100644 correction/correct_metadata.py create mode 100644 correction/correct_tiles.py diff --git a/configs/correction.yaml b/configs/correction.yaml new file mode 100644 index 00000000..00bfbc41 --- /dev/null +++ b/configs/correction.yaml @@ -0,0 +1,13 @@ +# @package _global_ + +defaults: + - /default + - _self_ + +data: ??? +source_of_truth: ??? +correction_type: ??? + +metadata: + run_name: ${correction_type} ${data.data_name} + description: ${correction_type} ${data.data_name} diff --git a/configs/correction/correct_metadata.yaml b/configs/correction/correct_metadata.yaml new file mode 100644 index 00000000..dd28985f --- /dev/null +++ b/configs/correction/correct_metadata.yaml @@ -0,0 +1,3 @@ +# @package _global_ + +correction_type: "Metadata Table Re-label" diff --git a/configs/correction/correct_tiles.yaml b/configs/correction/correct_tiles.yaml new file mode 100644 index 00000000..70d6713e --- /dev/null +++ b/configs/correction/correct_tiles.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +correction_type: "Tile Datasets Re-label" +tiles_to_correct: ??? diff --git a/configs/data/mmci_sl_group_1.yaml b/configs/data/mmci_sl_group_1.yaml index 3500c7fe..29e3e1cf 100644 --- a/configs/data/mmci_sl_group_1.yaml +++ b/configs/data/mmci_sl_group_1.yaml @@ -1,10 +1,10 @@ -tiles_uri_512: "mlflow-artifacts:/65/bf00086e446d48d78b3fe98c6e9a3ad7/artifacts/MMCI SL Group 1 512" -tiles_uri_224: "mlflow-artifacts:/65/18c0edd862c5463a8c0bd6e9a11b248e/artifacts/MMCI SL Group 1 224" +tiles_uri_512: "mlflow-artifacts:/65/ba747c14ca6f4a6991b18f2fd9beda10/artifacts/MMCI SL Group 1 512" +tiles_uri_224: "mlflow-artifacts:/65/05cbc53468d24748938a1e645bfbe0fe/artifacts/MMCI SL Group 1 224" pgp_embeddings_uri: "mlflow-artifacts:/65/b3cc8f1190e2469eb0e4a81fbfe6e8d7/artifacts/MMCI 2k (val. part) - 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/611adae302e94f629d46968d83d19517/artifacts/MMCI SL Group 1 224" -metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_1_with_extra.csv" +metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_1_with_extra_corrected.csv" tissue_masks_uri: "mlflow-artifacts:/65/7e0f909430cd48459511d0dd683e7629/artifacts/tissue_masks" diff --git a/configs/data/mmci_sl_group_2.yaml b/configs/data/mmci_sl_group_2.yaml index c9c69f74..82acf3ce 100644 --- a/configs/data/mmci_sl_group_2.yaml +++ b/configs/data/mmci_sl_group_2.yaml @@ -1,10 +1,10 @@ -tiles_uri_512: "mlflow-artifacts:/65/a4bfeabdbb9a488290071465cce2af50/artifacts/MMCI 2k Group 2" -tiles_uri_224: "mlflow-artifacts:/65/bed79a9ae8b34cf7a82e3b474effd6d8/artifacts/MMCI 2k Group 2 224" +tiles_uri_512: "mlflow-artifacts:/65/a31f80e863f0461d803ae0027ffed3ae/artifacts/MMCI SL Group 2 512" +tiles_uri_224: "mlflow-artifacts:/65/4be5807467e74794b103dfd8a2b00e1f/artifacts/MMCI SL Group 2 224" pgp_embeddings_uri: "mlflow-artifacts:/65/c7d9ccaceb5744d1b8f7778de7d61931/artifacts/MMCI 2k Group 2 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/611adae302e94f629d46968d83d19517/artifacts/MMCI 2k Group 2 224" -metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_2_with_extra.csv" +metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_2_with_extra_corrected.csv" tissue_masks_uri: "mlflow-artifacts:/65/7e0f909430cd48459511d0dd683e7629/artifacts/tissue_masks" diff --git a/configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml b/configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml new file mode 100644 index 00000000..6b81d9cd --- /dev/null +++ b/configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml @@ -0,0 +1 @@ +# TBD \ No newline at end of file diff --git a/configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml b/configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml new file mode 100644 index 00000000..6b81d9cd --- /dev/null +++ b/configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml @@ -0,0 +1 @@ +# TBD \ No newline at end of file diff --git a/configs/experiment/correction/correct_tiles/mmci_sl_group_1_224.yaml b/configs/experiment/correction/correct_tiles/mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..e1475762 --- /dev/null +++ b/configs/experiment/correction/correct_tiles/mmci_sl_group_1_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /data/mmci_sl_group_1@data + - _self_ + +source_of_truth: ${data.metadata_table} +tiles_to_correct: ${data.tiles_uri_224} diff --git a/configs/experiment/correction/correct_tiles/mmci_sl_group_1_512.yaml b/configs/experiment/correction/correct_tiles/mmci_sl_group_1_512.yaml new file mode 100644 index 00000000..151651d4 --- /dev/null +++ b/configs/experiment/correction/correct_tiles/mmci_sl_group_1_512.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /data/mmci_sl_group_1@data + - _self_ + +source_of_truth: ${data.metadata_table} +tiles_to_correct: ${data.tiles_uri_512} diff --git a/configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml b/configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml new file mode 100644 index 00000000..e1475762 --- /dev/null +++ b/configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /data/mmci_sl_group_1@data + - _self_ + +source_of_truth: ${data.metadata_table} +tiles_to_correct: ${data.tiles_uri_224} diff --git a/configs/experiment/correction/correct_tiles/mmci_sl_group_2_512.yaml b/configs/experiment/correction/correct_tiles/mmci_sl_group_2_512.yaml new file mode 100644 index 00000000..9d4edf2e --- /dev/null +++ b/configs/experiment/correction/correct_tiles/mmci_sl_group_2_512.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /data/mmci_sl_group_2@data + - _self_ + +source_of_truth: ${data.metadata_table} +tiles_to_correct: ${data.tiles_uri_512} diff --git a/correction/correct_metadata.py b/correction/correct_metadata.py new file mode 100644 index 00000000..e727aecb --- /dev/null +++ b/correction/correct_metadata.py @@ -0,0 +1,37 @@ +from pathlib import Path +from tempfile import TemporaryDirectory + +import hydra +import mlflow +import pandas as pd +from omegaconf import DictConfig +from rationai.mlkit import with_cli_args +from rationai.mlkit.autolog import autolog +from rationai.mlkit.lightning.loggers import MLFlowLogger + + +@with_cli_args(["+correction=correct_metadata"]) +@hydra.main(config_path="../configs", config_name="correction", version_base=None) +@autolog +def main(config: DictConfig, logger: MLFlowLogger) -> None: + sot_df = pd.read_csv(mlflow.artifacts.download_artifacts(config.source_of_truth)) + sot_df["stem"] = sot_df["slide_path"].map(lambda x: Path(x).stem) + meta_csv_df = pd.read_csv( + mlflow.artifacts.download_artifacts(config.metadata_to_correct) + ) + cols = list(meta_csv_df.columns) + meta_csv_df = meta_csv_df.merge( + sot_df[["slide_path", "carcinoma"]], on="slide_path", how="left" + ) + + meta_csv_df["carcinoma"] = meta_csv_df["carcinoma_y"] + meta_csv_df = meta_csv_df[cols] + + with TemporaryDirectory() as tmp_dir: + target = Path(tmp_dir) / "mmci_2k_group_2_with_extra_corrected.csv" + meta_csv_df.to_csv(str(target), index=False) + mlflow.log_artifact(str(target)) + + +if __name__ == "__main__": + main() diff --git a/correction/correct_tiles.py b/correction/correct_tiles.py new file mode 100644 index 00000000..47bf7c48 --- /dev/null +++ b/correction/correct_tiles.py @@ -0,0 +1,35 @@ +from pathlib import Path + +import hydra +import mlflow +import pandas as pd +from omegaconf import DictConfig +from rationai.mlkit import with_cli_args +from rationai.mlkit.autolog import autolog +from rationai.mlkit.lightning.loggers import MLFlowLogger +from rationai.tiling.writers import save_mlflow_dataset + + +@with_cli_args(["+correction=correct_tiles"]) +@hydra.main(config_path="../configs", config_name="correction", version_base=None) +@autolog +def main(config: DictConfig, logger: MLFlowLogger) -> None: + sot_df = pd.read_csv(mlflow.artifacts.download_artifacts(config.source_of_truth)) + sot_df["stem"] = sot_df["slide_path"].map(lambda x: Path(x).stem) + + tiles_path = Path(mlflow.artifacts.download_artifacts(config.tiles_to_correct)) + slides = pd.read_parquet(tiles_path / "slides.parquet") + slides["stem"] = slides["path"].map(lambda x: Path(x).stem) + result = pd.merge(slides, sot_df, on="stem", how="inner") + result["carcinoma"] = result["carcinoma_y"] + final = result[slides.columns] + tiles = pd.read_parquet(tiles_path / "tiles.parquet") + save_mlflow_dataset( + slides=final, + tiles=tiles, + dataset_name=config.data.data_name, + ) + + +if __name__ == "__main__": + main() diff --git a/exploration/mmci_2k.py b/exploration/mmci_2k.py index 74ac896c..6bf56e99 100644 --- a/exploration/mmci_2k.py +++ b/exploration/mmci_2k.py @@ -21,6 +21,7 @@ def parse_slide(slide_name: str, table: pd.DataFrame) -> tuple[pd.Series, bool] return None year, bioptic_request, slide_number = match.groups() + slide_number = slide_number.lstrip("0") r = bioptic_request.lstrip("0") index = f"{year[2:]}/{r}" record = table.loc[index] From 84a2ca10b9de3ee228a5408ba1bc1f4fa27cc0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 9 Mar 2026 15:43:36 +0000 Subject: [PATCH 002/284] feat: correct metadata --- configs/correction/correct_metadata.yaml | 1 + configs/data/mmci_sl_group_1.yaml | 2 +- configs/data/mmci_sl_group_2.yaml | 2 +- .../correction/correct_metadata/mmci_sl_group_1.yaml | 9 ++++++++- .../correction/correct_metadata/mmci_sl_group_2.yaml | 9 ++++++++- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/configs/correction/correct_metadata.yaml b/configs/correction/correct_metadata.yaml index dd28985f..6a23f0aa 100644 --- a/configs/correction/correct_metadata.yaml +++ b/configs/correction/correct_metadata.yaml @@ -1,3 +1,4 @@ # @package _global_ correction_type: "Metadata Table Re-label" +metadata_to_correct: ??? diff --git a/configs/data/mmci_sl_group_1.yaml b/configs/data/mmci_sl_group_1.yaml index 29e3e1cf..e6808982 100644 --- a/configs/data/mmci_sl_group_1.yaml +++ b/configs/data/mmci_sl_group_1.yaml @@ -5,7 +5,7 @@ pgp_embeddings_uri: "mlflow-artifacts:/65/b3cc8f1190e2469eb0e4a81fbfe6e8d7/artif virchow2_embeddings_uri: "mlflow-artifacts:/65/611adae302e94f629d46968d83d19517/artifacts/MMCI SL Group 1 224" metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_1_with_extra_corrected.csv" - +metadata_table_deprecated: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_1_with_extra.csv" tissue_masks_uri: "mlflow-artifacts:/65/7e0f909430cd48459511d0dd683e7629/artifacts/tissue_masks" qc_masks_uri: "mlflow-artifacts:/65/9b85af038a1b47d99f0bfb5207158a8d/artifacts/qc_masks" diff --git a/configs/data/mmci_sl_group_2.yaml b/configs/data/mmci_sl_group_2.yaml index 82acf3ce..a1aad7c4 100644 --- a/configs/data/mmci_sl_group_2.yaml +++ b/configs/data/mmci_sl_group_2.yaml @@ -5,7 +5,7 @@ pgp_embeddings_uri: "mlflow-artifacts:/65/c7d9ccaceb5744d1b8f7778de7d61931/artif virchow2_embeddings_uri: "mlflow-artifacts:/65/611adae302e94f629d46968d83d19517/artifacts/MMCI 2k Group 2 224" metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_2_with_extra_corrected.csv" - +metadata_table_deprecated: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_2_with_extra.csv" tissue_masks_uri: "mlflow-artifacts:/65/7e0f909430cd48459511d0dd683e7629/artifacts/tissue_masks" qc_masks_uri: "mlflow-artifacts:/65/9b85af038a1b47d99f0bfb5207158a8d/artifacts/qc_masks" diff --git a/configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml b/configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml index 6b81d9cd..e847d789 100644 --- a/configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml +++ b/configs/experiment/correction/correct_metadata/mmci_sl_group_1.yaml @@ -1 +1,8 @@ -# TBD \ No newline at end of file +# @package _global_ + +defaults: + - /data/mmci_sl_group_1@data + - _self_ + +source_of_truth: ${data.metadata_table} +metadata_to_correct: ${data.metadata_table_deprecated} diff --git a/configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml b/configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml index 6b81d9cd..88222d00 100644 --- a/configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml +++ b/configs/experiment/correction/correct_metadata/mmci_sl_group_2.yaml @@ -1 +1,8 @@ -# TBD \ No newline at end of file +# @package _global_ + +defaults: + - /data/mmci_sl_group_2@data + - _self_ + +source_of_truth: ${data.metadata_table} +metadata_to_correct: ${data.metadata_table_deprecated} From 79e20050d38b98129ea9dea8748e9ca532c1b362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 11 Mar 2026 11:42:03 +0000 Subject: [PATCH 003/284] feat: add docstring --- configs/data/mmci_sl.yaml | 3 +++ configs/data/mmci_sl_group_1.yaml | 2 +- configs/data/mmci_sl_group_2.yaml | 2 +- correction/correct_metadata.py | 7 +++++++ correction/correct_tiles.py | 7 +++++++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/configs/data/mmci_sl.yaml b/configs/data/mmci_sl.yaml index 42692127..76ce0d5b 100644 --- a/configs/data/mmci_sl.yaml +++ b/configs/data/mmci_sl.yaml @@ -1,5 +1,8 @@ metadata_table: "mlflow-artifacts:/65/5a159bf29b034e08a12e9a683f84e752/artifacts/mmci_2k_prostate.csv" +tiles_uri_224: "mlflow-artifacts:/105/682b14944f1b416193871333574eb30a/artifacts/MMCI 2k 224" +virchow2_embeddings_uri: "mlflow-artifacts:/65/1429184febea4cdcb26616c4038c6a71/artifacts/MMCI SL All" + tissue_masks_uri: "mlflow-artifacts:/65/7e0f909430cd48459511d0dd683e7629/artifacts/tissue_masks" qc_masks_uri: "mlflow-artifacts:/65/9b85af038a1b47d99f0bfb5207158a8d/artifacts/qc_masks" annotation_masks_uri: null diff --git a/configs/data/mmci_sl_group_1.yaml b/configs/data/mmci_sl_group_1.yaml index e6808982..64c2acb5 100644 --- a/configs/data/mmci_sl_group_1.yaml +++ b/configs/data/mmci_sl_group_1.yaml @@ -1,7 +1,7 @@ tiles_uri_512: "mlflow-artifacts:/65/ba747c14ca6f4a6991b18f2fd9beda10/artifacts/MMCI SL Group 1 512" tiles_uri_224: "mlflow-artifacts:/65/05cbc53468d24748938a1e645bfbe0fe/artifacts/MMCI SL Group 1 224" -pgp_embeddings_uri: "mlflow-artifacts:/65/b3cc8f1190e2469eb0e4a81fbfe6e8d7/artifacts/MMCI 2k (val. part) - 224" +pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI SL Group 1 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/611adae302e94f629d46968d83d19517/artifacts/MMCI SL Group 1 224" metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_1_with_extra_corrected.csv" diff --git a/configs/data/mmci_sl_group_2.yaml b/configs/data/mmci_sl_group_2.yaml index a1aad7c4..8c9ef150 100644 --- a/configs/data/mmci_sl_group_2.yaml +++ b/configs/data/mmci_sl_group_2.yaml @@ -1,7 +1,7 @@ tiles_uri_512: "mlflow-artifacts:/65/a31f80e863f0461d803ae0027ffed3ae/artifacts/MMCI SL Group 2 512" tiles_uri_224: "mlflow-artifacts:/65/4be5807467e74794b103dfd8a2b00e1f/artifacts/MMCI SL Group 2 224" -pgp_embeddings_uri: "mlflow-artifacts:/65/c7d9ccaceb5744d1b8f7778de7d61931/artifacts/MMCI 2k Group 2 224" +pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI 2k Group 2 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/611adae302e94f629d46968d83d19517/artifacts/MMCI 2k Group 2 224" metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_2_with_extra_corrected.csv" diff --git a/correction/correct_metadata.py b/correction/correct_metadata.py index e727aecb..c366032e 100644 --- a/correction/correct_metadata.py +++ b/correction/correct_metadata.py @@ -1,3 +1,10 @@ +"""Metadata Correction. + +We have found a bug in MMCI 2k exploration script, since this bug affects carcinoma labels, +a new data split would differ from the current one (experimentally confirmed). +For that reason, we add script to correct metadata tables. +""" + from pathlib import Path from tempfile import TemporaryDirectory diff --git a/correction/correct_tiles.py b/correction/correct_tiles.py index 47bf7c48..699d71d3 100644 --- a/correction/correct_tiles.py +++ b/correction/correct_tiles.py @@ -1,3 +1,10 @@ +"""Tiles Correction. + +We have found a bug in MMCI 2k exploration script, since this bug affects carcinoma labels, +a new data split would differ from the current one (experimentally confirmed). +For that reason, we add scripts to correct tiles datasets. +""" + from pathlib import Path import hydra From 33c574eb350eda77a9192edb07080e32b6fd6a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 16 Mar 2026 14:35:41 +0000 Subject: [PATCH 004/284] fix: lm notes --- .../correction/correct_tiles/mmci_sl_group_2_224.yaml | 2 +- correction/correct_metadata.py | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml b/configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml index e1475762..c524135b 100644 --- a/configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml +++ b/configs/experiment/correction/correct_tiles/mmci_sl_group_2_224.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /data/mmci_sl_group_1@data + - /data/mmci_sl_group_2@data - _self_ source_of_truth: ${data.metadata_table} diff --git a/correction/correct_metadata.py b/correction/correct_metadata.py index c366032e..dee506f2 100644 --- a/correction/correct_metadata.py +++ b/correction/correct_metadata.py @@ -22,10 +22,8 @@ @autolog def main(config: DictConfig, logger: MLFlowLogger) -> None: sot_df = pd.read_csv(mlflow.artifacts.download_artifacts(config.source_of_truth)) - sot_df["stem"] = sot_df["slide_path"].map(lambda x: Path(x).stem) - meta_csv_df = pd.read_csv( - mlflow.artifacts.download_artifacts(config.metadata_to_correct) - ) + meta_path = mlflow.artifacts.download_artifacts(config.metadata_to_correct) + meta_csv_df = pd.read_csv(meta_path) cols = list(meta_csv_df.columns) meta_csv_df = meta_csv_df.merge( sot_df[["slide_path", "carcinoma"]], on="slide_path", how="left" @@ -35,7 +33,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: meta_csv_df = meta_csv_df[cols] with TemporaryDirectory() as tmp_dir: - target = Path(tmp_dir) / "mmci_2k_group_2_with_extra_corrected.csv" + target = Path(tmp_dir) / f"{Path(meta_path).stem}_corrected.csv" meta_csv_df.to_csv(str(target), index=False) mlflow.log_artifact(str(target)) From 069930b6d7ce9355f133cf883d4b72e321d06890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 17 Mar 2026 10:59:29 +0000 Subject: [PATCH 005/284] feat: prepare modules --- .../datamodule/datasets/tile_dataset.py | 6 + .../modeling/full_model/__init__.py | 0 prostate_cancer/modeling/full_model/vit.py | 9 ++ prostate_cancer/prostate_cancer_model.py | 30 ++++- pyproject.toml | 1 + uv.lock | 104 ++++++++++++++++++ 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 prostate_cancer/modeling/full_model/__init__.py create mode 100644 prostate_cancer/modeling/full_model/vit.py diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_dataset.py index e16b9f83..afc27e90 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_dataset.py @@ -7,6 +7,7 @@ from albumentations.pytorch import ToTensorV2 from rationai.mlkit.data.datasets import OpenSlideTilesDataset from torch.utils.data import Dataset +from transformers import ViTImageProcessor from prostate_cancer.datamodule.datasets.base import ( FilterableDataset, @@ -68,6 +69,7 @@ def __init__( tiles: pd.DataFrame, include_label: bool, transforms: TransformType | None = None, + processor: ViTImageProcessor | None = None, ) -> None: super().__init__() @@ -81,6 +83,7 @@ def __init__( self.transforms = transforms self.include_label = include_label self.to_tensor = ToTensorV2() + self.processor = processor if len(tiles) == 0: print( @@ -103,6 +106,9 @@ def __getitem__(self, idx: int) -> LabeledSample | UnlabeledSample: tensor_image = self.to_tensor(image=image)["image"] + if self.processor is not None: + tensor_image = self.processor(tensor_image,return_tensors="pt")["pixel_values"].squeeze(0) + if self.include_label: label = torch.tensor( [self.slide_tiles.tiles.iloc[idx]["carcinoma"]] diff --git a/prostate_cancer/modeling/full_model/__init__.py b/prostate_cancer/modeling/full_model/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prostate_cancer/modeling/full_model/vit.py b/prostate_cancer/modeling/full_model/vit.py new file mode 100644 index 00000000..30c660fb --- /dev/null +++ b/prostate_cancer/modeling/full_model/vit.py @@ -0,0 +1,9 @@ +from transformers import ViTForImageClassification + + +def google_vit_full_model() -> ViTForImageClassification: + return ViTForImageClassification.from_pretrained( + "google/vit-base-patch16-224", + num_labels=1, + ignore_mismatched_sizes=True, + ) diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/prostate_cancer_model.py index a87c1ba9..9f4bb428 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/prostate_cancer_model.py @@ -21,11 +21,28 @@ class ProstateCancerModel(LightningModule): def __init__( - self, backbone: nn.Module | None, decode_head: nn.Module, lr: float + self, + backbone: nn.Module | None = None, + decode_head: nn.Module | None = None, + full_model: nn.Module | None = None, + lr: float = 1e-4, ) -> None: super().__init__() + + # enforce mutually exclusive configs + if full_model is not None and (backbone is not None or decode_head is not None): + raise ValueError( + "Provide either `full_model` OR (`backbone` + `decode_head`), not both." + ) + + if full_model is None and decode_head is None: + raise ValueError( + "`decode_head` must be provided when using a backbone." + ) + self.backbone = backbone self.decode_head = decode_head + self.full_model = full_model self.lr = lr self.criterion = nn.BCEWithLogitsLoss(reduction="mean") @@ -55,7 +72,18 @@ def __init__( ) def forward(self, x: Tensor) -> Tensor: + if self.full_model is not None: + outputs = self.full_model(x) + + # HuggingFace models return objects + if hasattr(outputs, "logits"): + return outputs.logits + + return outputs + features = self.backbone(x) if self.backbone else x + + assert self.decode_head is not None logits = self.decode_head(features) return logits diff --git a/pyproject.toml b/pyproject.toml index e2c24c1d..15b9614e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ dependencies = [ "deprecated", "pillow==11.3.0", "huggingface_hub", + "transformers", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 188e269b..6c269e85 100644 --- a/uv.lock +++ b/uv.lock @@ -1802,6 +1802,7 @@ dependencies = [ { name = "torch" }, { name = "torchvision" }, { name = "tqdm" }, + { name = "transformers" }, { name = "triton" }, ] @@ -1832,6 +1833,7 @@ requires-dist = [ { name = "torch", specifier = ">=2.9.1" }, { name = "torchvision", specifier = ">=0.22.0" }, { name = "tqdm", specifier = "==4.67.1" }, + { name = "transformers" }, { name = "triton", specifier = ">=3.3.0" }, ] @@ -2172,6 +2174,62 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] +[[package]] +name = "regex" +version = "2026.2.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/42/9061b03cf0fc4b5fa2c3984cbbaed54324377e440a5c5a29d29a72518d62/regex-2026.2.28-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fcf26c3c6d0da98fada8ae4ef0aa1c3405a431c0a77eb17306d38a89b02adcd7", size = 489574, upload-time = "2026-02-28T02:16:50.455Z" }, + { url = "https://files.pythonhosted.org/packages/77/83/0c8a5623a233015595e3da499c5a1c13720ac63c107897a6037bb97af248/regex-2026.2.28-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02473c954af35dd2defeb07e44182f5705b30ea3f351a7cbffa9177beb14da5d", size = 291426, upload-time = "2026-02-28T02:16:52.52Z" }, + { url = "https://files.pythonhosted.org/packages/9e/06/3ef1ac6910dc3295ebd71b1f9bfa737e82cfead211a18b319d45f85ddd09/regex-2026.2.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b65d33a17101569f86d9c5966a8b1d7fbf8afdda5a8aa219301b0a80f58cf7d", size = 289200, upload-time = "2026-02-28T02:16:54.08Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c9/8cc8d850b35ab5650ff6756a1cb85286e2000b66c97520b29c1587455344/regex-2026.2.28-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71dcecaa113eebcc96622c17692672c2d104b1d71ddf7adeda90da7ddeb26fc", size = 796765, upload-time = "2026-02-28T02:16:55.905Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5d/57702597627fc23278ebf36fbb497ac91c0ce7fec89ac6c81e420ca3e38c/regex-2026.2.28-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:481df4623fa4969c8b11f3433ed7d5e3dc9cec0f008356c3212b3933fb77e3d8", size = 863093, upload-time = "2026-02-28T02:16:58.094Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/f3ecad537ca2811b4d26b54ca848cf70e04fcfc138667c146a9f3157779c/regex-2026.2.28-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64e7c6ad614573e0640f271e811a408d79a9e1fe62a46adb602f598df42a818d", size = 909455, upload-time = "2026-02-28T02:17:00.918Z" }, + { url = "https://files.pythonhosted.org/packages/9e/40/bb226f203caa22c1043c1ca79b36340156eca0f6a6742b46c3bb222a3a57/regex-2026.2.28-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b08a06976ff4fb0d83077022fde3eca06c55432bb997d8c0495b9a4e9872f4", size = 802037, upload-time = "2026-02-28T02:17:02.842Z" }, + { url = "https://files.pythonhosted.org/packages/44/7c/c6d91d8911ac6803b45ca968e8e500c46934e58c0903cbc6d760ee817a0a/regex-2026.2.28-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:864cdd1a2ef5716b0ab468af40139e62ede1b3a53386b375ec0786bb6783fc05", size = 775113, upload-time = "2026-02-28T02:17:04.506Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8d/4a9368d168d47abd4158580b8c848709667b1cd293ff0c0c277279543bd0/regex-2026.2.28-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:511f7419f7afab475fd4d639d4aedfc54205bcb0800066753ef68a59f0f330b5", size = 784194, upload-time = "2026-02-28T02:17:06.888Z" }, + { url = "https://files.pythonhosted.org/packages/cc/bf/2c72ab5d8b7be462cb1651b5cc333da1d0068740342f350fcca3bca31947/regex-2026.2.28-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b42f7466e32bf15a961cf09f35fa6323cc72e64d3d2c990b10de1274a5da0a59", size = 856846, upload-time = "2026-02-28T02:17:09.11Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f4/6b65c979bb6d09f51bb2d2a7bc85de73c01ec73335d7ddd202dcb8cd1c8f/regex-2026.2.28-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8710d61737b0c0ce6836b1da7109f20d495e49b3809f30e27e9560be67a257bf", size = 763516, upload-time = "2026-02-28T02:17:11.004Z" }, + { url = "https://files.pythonhosted.org/packages/8e/32/29ea5e27400ee86d2cc2b4e80aa059df04eaf78b4f0c18576ae077aeff68/regex-2026.2.28-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4390c365fd2d45278f45afd4673cb90f7285f5701607e3ad4274df08e36140ae", size = 849278, upload-time = "2026-02-28T02:17:12.693Z" }, + { url = "https://files.pythonhosted.org/packages/1d/91/3233d03b5f865111cd517e1c95ee8b43e8b428d61fa73764a80c9bb6f537/regex-2026.2.28-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cb3b1db8ff6c7b8bf838ab05583ea15230cb2f678e569ab0e3a24d1e8320940b", size = 790068, upload-time = "2026-02-28T02:17:14.9Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/abc706c1fb03b4580a09645b206a3fc032f5a9f457bc1a8038ac555658ab/regex-2026.2.28-cp312-cp312-win32.whl", hash = "sha256:f8ed9a5d4612df9d4de15878f0bc6aa7a268afbe5af21a3fdd97fa19516e978c", size = 266416, upload-time = "2026-02-28T02:17:17.15Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/2a6f7dff190e5fa9df9fb4acf2fdf17a1aa0f7f54596cba8de608db56b3a/regex-2026.2.28-cp312-cp312-win_amd64.whl", hash = "sha256:01d65fd24206c8e1e97e2e31b286c59009636c022eb5d003f52760b0f42155d4", size = 277297, upload-time = "2026-02-28T02:17:18.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/f0/58a2484851fadf284458fdbd728f580d55c1abac059ae9f048c63b92f427/regex-2026.2.28-cp312-cp312-win_arm64.whl", hash = "sha256:c0b5ccbb8ffb433939d248707d4a8b31993cb76ab1a0187ca886bf50e96df952", size = 270408, upload-time = "2026-02-28T02:17:20.328Z" }, + { url = "https://files.pythonhosted.org/packages/87/f6/dc9ef48c61b79c8201585bf37fa70cd781977da86e466cd94e8e95d2443b/regex-2026.2.28-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6d63a07e5ec8ce7184452cb00c41c37b49e67dc4f73b2955b5b8e782ea970784", size = 489311, upload-time = "2026-02-28T02:17:22.591Z" }, + { url = "https://files.pythonhosted.org/packages/95/c8/c20390f2232d3f7956f420f4ef1852608ad57aa26c3dd78516cb9f3dc913/regex-2026.2.28-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e59bc8f30414d283ae8ee1617b13d8112e7135cb92830f0ec3688cb29152585a", size = 291285, upload-time = "2026-02-28T02:17:24.355Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a6/ba1068a631ebd71a230e7d8013fcd284b7c89c35f46f34a7da02082141b1/regex-2026.2.28-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:de0cf053139f96219ccfabb4a8dd2d217c8c82cb206c91d9f109f3f552d6b43d", size = 289051, upload-time = "2026-02-28T02:17:26.722Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1b/7cc3b7af4c244c204b7a80924bd3d85aecd9ba5bc82b485c5806ee8cda9e/regex-2026.2.28-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb4db2f17e6484904f986c5a657cec85574c76b5c5e61c7aae9ffa1bc6224f95", size = 796842, upload-time = "2026-02-28T02:17:29.064Z" }, + { url = "https://files.pythonhosted.org/packages/24/87/26bd03efc60e0d772ac1e7b60a2e6325af98d974e2358f659c507d3c76db/regex-2026.2.28-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52b017b35ac2214d0db5f4f90e303634dc44e4aba4bd6235a27f97ecbe5b0472", size = 863083, upload-time = "2026-02-28T02:17:31.363Z" }, + { url = "https://files.pythonhosted.org/packages/ae/54/aeaf4afb1aa0a65e40de52a61dc2ac5b00a83c6cb081c8a1d0dda74f3010/regex-2026.2.28-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:69fc560ccbf08a09dc9b52ab69cacfae51e0ed80dc5693078bdc97db2f91ae96", size = 909412, upload-time = "2026-02-28T02:17:33.248Z" }, + { url = "https://files.pythonhosted.org/packages/12/2f/049901def913954e640d199bbc6a7ca2902b6aeda0e5da9d17f114100ec2/regex-2026.2.28-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e61eea47230eba62a31f3e8a0e3164d0f37ef9f40529fb2c79361bc6b53d2a92", size = 802101, upload-time = "2026-02-28T02:17:35.053Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/512fb9ff7f5b15ea204bb1967ebb649059446decacccb201381f9fa6aad4/regex-2026.2.28-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4f5c0b182ad4269e7381b7c27fdb0408399881f7a92a4624fd5487f2971dfc11", size = 775260, upload-time = "2026-02-28T02:17:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/9a92935878aba19bd72706b9db5646a6f993d99b3f6ed42c02ec8beb1d61/regex-2026.2.28-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:96f6269a2882fbb0ee76967116b83679dc628e68eaea44e90884b8d53d833881", size = 784311, upload-time = "2026-02-28T02:17:39.855Z" }, + { url = "https://files.pythonhosted.org/packages/09/d3/fc51a8a738a49a6b6499626580554c9466d3ea561f2b72cfdc72e4149773/regex-2026.2.28-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b5acd4b6a95f37c3c3828e5d053a7d4edaedb85de551db0153754924cb7c83e3", size = 856876, upload-time = "2026-02-28T02:17:42.317Z" }, + { url = "https://files.pythonhosted.org/packages/08/b7/2e641f3d084b120ca4c52e8c762a78da0b32bf03ef546330db3e2635dc5f/regex-2026.2.28-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2234059cfe33d9813a3677ef7667999caea9eeaa83fef98eb6ce15c6cf9e0215", size = 763632, upload-time = "2026-02-28T02:17:45.073Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6d/0009021d97e79ee99f3d8641f0a8d001eed23479ade4c3125a5480bf3e2d/regex-2026.2.28-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c15af43c72a7fb0c97cbc66fa36a43546eddc5c06a662b64a0cbf30d6ac40944", size = 849320, upload-time = "2026-02-28T02:17:47.192Z" }, + { url = "https://files.pythonhosted.org/packages/05/7a/51cfbad5758f8edae430cb21961a9c8d04bce1dae4d2d18d4186eec7cfa1/regex-2026.2.28-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9185cc63359862a6e80fe97f696e04b0ad9a11c4ac0a4a927f979f611bfe3768", size = 790152, upload-time = "2026-02-28T02:17:49.067Z" }, + { url = "https://files.pythonhosted.org/packages/90/3d/a83e2b6b3daa142acb8c41d51de3876186307d5cb7490087031747662500/regex-2026.2.28-cp313-cp313-win32.whl", hash = "sha256:fb66e5245db9652abd7196ace599b04d9c0e4aa7c8f0e2803938377835780081", size = 266398, upload-time = "2026-02-28T02:17:50.744Z" }, + { url = "https://files.pythonhosted.org/packages/85/4f/16e9ebb1fe5425e11b9596c8d57bf8877dcb32391da0bfd33742e3290637/regex-2026.2.28-cp313-cp313-win_amd64.whl", hash = "sha256:71a911098be38c859ceb3f9a9ce43f4ed9f4c6720ad8684a066ea246b76ad9ff", size = 277282, upload-time = "2026-02-28T02:17:53.074Z" }, + { url = "https://files.pythonhosted.org/packages/07/b4/92851335332810c5a89723bf7a7e35c7209f90b7d4160024501717b28cc9/regex-2026.2.28-cp313-cp313-win_arm64.whl", hash = "sha256:39bb5727650b9a0275c6a6690f9bb3fe693a7e6cc5c3155b1240aedf8926423e", size = 270382, upload-time = "2026-02-28T02:17:54.888Z" }, + { url = "https://files.pythonhosted.org/packages/24/07/6c7e4cec1e585959e96cbc24299d97e4437a81173217af54f1804994e911/regex-2026.2.28-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:97054c55db06ab020342cc0d35d6f62a465fa7662871190175f1ad6c655c028f", size = 492541, upload-time = "2026-02-28T02:17:56.813Z" }, + { url = "https://files.pythonhosted.org/packages/7c/13/55eb22ada7f43d4f4bb3815b6132183ebc331c81bd496e2d1f3b8d862e0d/regex-2026.2.28-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d25a10811de831c2baa6aef3c0be91622f44dd8d31dd12e69f6398efb15e48b", size = 292984, upload-time = "2026-02-28T02:17:58.538Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/c301f8cb29ce9644a5ef85104c59244e6e7e90994a0f458da4d39baa8e17/regex-2026.2.28-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d6cfe798d8da41bb1862ed6e0cba14003d387c3c0c4a5d45591076ae9f0ce2f8", size = 291509, upload-time = "2026-02-28T02:18:00.208Z" }, + { url = "https://files.pythonhosted.org/packages/b5/43/aabe384ec1994b91796e903582427bc2ffaed9c4103819ed3c16d8e749f3/regex-2026.2.28-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd0ce43e71d825b7c0661f9c54d4d74bd97c56c3fd102a8985bcfea48236bacb", size = 809429, upload-time = "2026-02-28T02:18:02.328Z" }, + { url = "https://files.pythonhosted.org/packages/04/b8/8d2d987a816720c4f3109cee7c06a4b24ad0e02d4fc74919ab619e543737/regex-2026.2.28-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00945d007fd74a9084d2ab79b695b595c6b7ba3698972fadd43e23230c6979c1", size = 869422, upload-time = "2026-02-28T02:18:04.23Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ad/2c004509e763c0c3719f97c03eca26473bffb3868d54c5f280b8cd4f9e3d/regex-2026.2.28-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bec23c11cbbf09a4df32fe50d57cbdd777bc442269b6e39a1775654f1c95dee2", size = 915175, upload-time = "2026-02-28T02:18:06.791Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/fd429066da487ef555a9da73bf214894aec77fc8c66a261ee355a69871a8/regex-2026.2.28-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5cdcc17d935c8f9d3f4db5c2ebe2640c332e3822ad5d23c2f8e0228e6947943a", size = 812044, upload-time = "2026-02-28T02:18:08.736Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ca/feedb7055c62a3f7f659971bf45f0e0a87544b6b0cf462884761453f97c5/regex-2026.2.28-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a448af01e3d8031c89c5d902040b124a5e921a25c4e5e07a861ca591ce429341", size = 782056, upload-time = "2026-02-28T02:18:10.777Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/1aa959ed0d25c1dd7dd5047ea8ba482ceaef38ce363c401fd32a6b923e60/regex-2026.2.28-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:10d28e19bd4888e4abf43bd3925f3c134c52fdf7259219003588a42e24c2aa25", size = 798743, upload-time = "2026-02-28T02:18:13.025Z" }, + { url = "https://files.pythonhosted.org/packages/3b/1f/dadb9cf359004784051c897dcf4d5d79895f73a1bbb7b827abaa4814ae80/regex-2026.2.28-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:99985a2c277dcb9ccb63f937451af5d65177af1efdeb8173ac55b61095a0a05c", size = 864633, upload-time = "2026-02-28T02:18:16.84Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f1/b9a25eb24e1cf79890f09e6ec971ee5b511519f1851de3453bc04f6c902b/regex-2026.2.28-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e1e7b24cb3ae9953a560c563045d1ba56ee4749fbd05cf21ba571069bd7be81b", size = 770862, upload-time = "2026-02-28T02:18:18.892Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/c5cb10b7aa6f182f9247a30cc9527e326601f46f4df864ac6db588d11fcd/regex-2026.2.28-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d8511a01d0e4ee1992eb3ba19e09bc1866fe03f05129c3aec3fdc4cbc77aad3f", size = 854788, upload-time = "2026-02-28T02:18:21.475Z" }, + { url = "https://files.pythonhosted.org/packages/0a/50/414ba0731c4bd40b011fa4703b2cc86879ec060c64f2a906e65a56452589/regex-2026.2.28-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aaffaecffcd2479ce87aa1e74076c221700b7c804e48e98e62500ee748f0f550", size = 800184, upload-time = "2026-02-28T02:18:23.492Z" }, + { url = "https://files.pythonhosted.org/packages/69/50/0c7290987f97e7e6830b0d853f69dc4dc5852c934aae63e7fdcd76b4c383/regex-2026.2.28-cp313-cp313t-win32.whl", hash = "sha256:ef77bdde9c9eba3f7fa5b58084b29bbcc74bcf55fdbeaa67c102a35b5bd7e7cc", size = 269137, upload-time = "2026-02-28T02:18:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/68/80/ef26ff90e74ceb4051ad6efcbbb8a4be965184a57e879ebcbdef327d18fa/regex-2026.2.28-cp313-cp313t-win_amd64.whl", hash = "sha256:98adf340100cbe6fbaf8e6dc75e28f2c191b1be50ffefe292fb0e6f6eefdb0d8", size = 280682, upload-time = "2026-02-28T02:18:27.205Z" }, + { url = "https://files.pythonhosted.org/packages/69/8b/fbad9c52e83ffe8f97e3ed1aa0516e6dff6bb633a41da9e64645bc7efdc5/regex-2026.2.28-cp313-cp313t-win_arm64.whl", hash = "sha256:2fb950ac1d88e6b6a9414381f403797b236f9fa17e1eee07683af72b1634207b", size = 271735, upload-time = "2026-02-28T02:18:29.015Z" }, +] + [[package]] name = "requests" version = "2.32.5" @@ -2628,6 +2686,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/d0/179abca8b984b3deefd996f362b612c39da73b60f685921e6cd58b6125b4/timm-1.0.15-py3-none-any.whl", hash = "sha256:5a3dc460c24e322ecc7fd1f3e3eb112423ddee320cb059cc1956fbc9731748ef", size = 2361373, upload-time = "2025-02-23T05:05:53.601Z" }, ] +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, +] + [[package]] name = "torch" version = "2.10.0" @@ -2729,6 +2813,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] +[[package]] +name = "transformers" +version = "5.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/1a/70e830d53ecc96ce69cfa8de38f163712d2b43ac52fbd743f39f56025c31/transformers-5.3.0.tar.gz", hash = "sha256:009555b364029da9e2946d41f1c5de9f15e6b1df46b189b7293f33a161b9c557", size = 8830831, upload-time = "2026-03-04T17:41:46.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/88/ae8320064e32679a5429a2c9ebbc05c2bf32cefb6e076f9b07f6d685a9b4/transformers-5.3.0-py3-none-any.whl", hash = "sha256:50ac8c89c3c7033444fb3f9f53138096b997ebb70d4b5e50a2e810bf12d3d29a", size = 10661827, upload-time = "2026-03-04T17:41:42.722Z" }, +] + [[package]] name = "triton" version = "3.6.0" From d062de98e2bc0d136643a824bfc62c09b53df421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 17 Mar 2026 13:13:07 +0000 Subject: [PATCH 006/284] feat: prepare for training --- .../images/labeled_with_processor.yaml | 11 ++++++++ .../images_processor_datamodule.yaml | 26 +++++++++++++++++++ .../experiment/train/images/google_vit.yaml | 8 ++++++ .../train/images/processor_base.yaml | 17 ++++++++++++ configs/model/full_model/google_vit.yaml | 1 + configs/model/google_vit_model.yaml | 6 +++++ .../datamodule/datasets/tile_dataset.py | 3 +++ .../full_model/{vit.py => google_vit.py} | 4 ++- prostate_cancer/prostate_cancer_model.py | 5 +++- 9 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 configs/datamodule/datasets/images/labeled_with_processor.yaml create mode 100644 configs/datamodule/images_processor_datamodule.yaml create mode 100644 configs/experiment/train/images/google_vit.yaml create mode 100644 configs/experiment/train/images/processor_base.yaml create mode 100644 configs/model/full_model/google_vit.yaml create mode 100644 configs/model/google_vit_model.yaml rename prostate_cancer/modeling/full_model/{vit.py => google_vit.py} (64%) diff --git a/configs/datamodule/datasets/images/labeled_with_processor.yaml b/configs/datamodule/datasets/images/labeled_with_processor.yaml new file mode 100644 index 00000000..c49c1720 --- /dev/null +++ b/configs/datamodule/datasets/images/labeled_with_processor.yaml @@ -0,0 +1,11 @@ +_target_: prostate_cancer.datamodule.datasets.LabeledTilesDataset + +uris: ??? + +thresholds: ${thresholds} + +carcinoma_roi_t: ${thresholds.carcinoma_roi_t} + +processor: ??? + +stratified_filter: ??? diff --git a/configs/datamodule/images_processor_datamodule.yaml b/configs/datamodule/images_processor_datamodule.yaml new file mode 100644 index 00000000..bf22f9bf --- /dev/null +++ b/configs/datamodule/images_processor_datamodule.yaml @@ -0,0 +1,26 @@ +defaults: + - datasets@train: images/labeled_with_processor + - datasets@val: images/labeled_with_processor + - datasets@test: images/labeled_with_processor + - datasets@predict: images/unlabeled_with_processor + + - samplers@sampler: stratified_weighted_random + +processor: + _target_: transformers.ViTImageProcessor.from_pretrained + pretrained_model_name_or_path: google/vit-base-patch16-224 + +train: + processor: ${processor} + stratified_filter: true +val: + processor: ${processor} + stratified_filter: true +test: + processor: ${processor} + stratified_filter: false +predict: + processor: ${processor} + +batch_size: ??? +num_workers: ??? diff --git a/configs/experiment/train/images/google_vit.yaml b/configs/experiment/train/images/google_vit.yaml new file mode 100644 index 00000000..a5cce768 --- /dev/null +++ b/configs/experiment/train/images/google_vit.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/train/images/processor_base + - /model/google_vit_model@model + - _self_ + +model_name: "Goole ViT" diff --git a/configs/experiment/train/images/processor_base.yaml b/configs/experiment/train/images/processor_base.yaml new file mode 100644 index 00000000..21a6d239 --- /dev/null +++ b/configs/experiment/train/images/processor_base.yaml @@ -0,0 +1,17 @@ +# @package _global_ + +defaults: + - /experiment/train/base + - /datamodule/images_processor_datamodule@datamodule + - _self_ + +datamodule: + batch_size: 64 + num_workers: 8 + + train: + uris: + - ${train_data.tiles_uri_224} + val: + uris: + - ${val_data.tiles_uri_224} diff --git a/configs/model/full_model/google_vit.yaml b/configs/model/full_model/google_vit.yaml new file mode 100644 index 00000000..9c872233 --- /dev/null +++ b/configs/model/full_model/google_vit.yaml @@ -0,0 +1 @@ +_target_: prostate_cancer.modeling.full_model.google_vit \ No newline at end of file diff --git a/configs/model/google_vit_model.yaml b/configs/model/google_vit_model.yaml new file mode 100644 index 00000000..40adb42f --- /dev/null +++ b/configs/model/google_vit_model.yaml @@ -0,0 +1,6 @@ +defaults: + - full_model: google_vit + - _self_ + +_target_: prostate_cancer.prostate_cancer_model.ProstateCancerModel +lr: ??? diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_dataset.py index afc27e90..77f24351 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_dataset.py @@ -27,8 +27,10 @@ def __init__( carcinoma_roi_t: float | None = None, stratified_filter: bool | None = None, transforms: TransformType | None = None, + processor: ViTImageProcessor | None = None, ) -> None: self.transforms = transforms + self.processor = processor super().__init__( uris=uris, thresholds=thresholds, @@ -50,6 +52,7 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: tiles=self.filter_tiles_by_slide(slide["id"]), include_label=self.labeled, transforms=self.transforms, + processor=self.processor, ), ) for _, slide in self.slides.iterrows() diff --git a/prostate_cancer/modeling/full_model/vit.py b/prostate_cancer/modeling/full_model/google_vit.py similarity index 64% rename from prostate_cancer/modeling/full_model/vit.py rename to prostate_cancer/modeling/full_model/google_vit.py index 30c660fb..2fd5b7ef 100644 --- a/prostate_cancer/modeling/full_model/vit.py +++ b/prostate_cancer/modeling/full_model/google_vit.py @@ -1,9 +1,11 @@ from transformers import ViTForImageClassification -def google_vit_full_model() -> ViTForImageClassification: +def google_vit() -> ViTForImageClassification: return ViTForImageClassification.from_pretrained( "google/vit-base-patch16-224", num_labels=1, + id2label={0: "carcinoma"}, + label2id={"carcinoma": 0}, ignore_mismatched_sizes=True, ) diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/prostate_cancer_model.py index 9f4bb428..d1be0f9b 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/prostate_cancer_model.py @@ -72,6 +72,7 @@ def __init__( ) def forward(self, x: Tensor) -> Tensor: + # --- full model mode if self.full_model is not None: outputs = self.full_model(x) @@ -81,9 +82,11 @@ def forward(self, x: Tensor) -> Tensor: return outputs + # --- backbone is None in case of embeddings features = self.backbone(x) if self.backbone else x - assert self.decode_head is not None + # --- if not full model, decode head must be present + assert self.decode_head is not None, "Decode head must be present if not full model" logits = self.decode_head(features) return logits From 3e453f8a40ae3448488c4f610a3c2e4e346bf186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 17 Mar 2026 13:22:30 +0000 Subject: [PATCH 007/284] fix: add unlabeled processor --- .../datamodule/datasets/images/unlabeled_with_processor.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 configs/datamodule/datasets/images/unlabeled_with_processor.yaml diff --git a/configs/datamodule/datasets/images/unlabeled_with_processor.yaml b/configs/datamodule/datasets/images/unlabeled_with_processor.yaml new file mode 100644 index 00000000..0c4c2699 --- /dev/null +++ b/configs/datamodule/datasets/images/unlabeled_with_processor.yaml @@ -0,0 +1,4 @@ +_target_: prostate_cancer.datamodule.datasets.UnlabeledTilesDataset +uris: ??? +processor: ??? +thresholds: ${thresholds} From e20f94ffa35bd560e44339170eb4d78428729837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 17 Mar 2026 13:27:09 +0000 Subject: [PATCH 008/284] fix: prefix --- configs/datamodule/images_processor_datamodule.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/datamodule/images_processor_datamodule.yaml b/configs/datamodule/images_processor_datamodule.yaml index bf22f9bf..887188fa 100644 --- a/configs/datamodule/images_processor_datamodule.yaml +++ b/configs/datamodule/images_processor_datamodule.yaml @@ -11,16 +11,16 @@ processor: pretrained_model_name_or_path: google/vit-base-patch16-224 train: - processor: ${processor} + processor: ${datamodule.processor} stratified_filter: true val: - processor: ${processor} + processor: ${datamodule.processor} stratified_filter: true test: - processor: ${processor} + processor: ${datamodule.processor} stratified_filter: false predict: - processor: ${processor} + processor: ${datamodule.processor} batch_size: ??? num_workers: ??? From 483f9e1fde64b8d6e0f56b5be3052224672e2fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 17 Mar 2026 13:31:11 +0000 Subject: [PATCH 009/284] fix: import --- prostate_cancer/modeling/full_model/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prostate_cancer/modeling/full_model/__init__.py b/prostate_cancer/modeling/full_model/__init__.py index e69de29b..b2d40692 100644 --- a/prostate_cancer/modeling/full_model/__init__.py +++ b/prostate_cancer/modeling/full_model/__init__.py @@ -0,0 +1,4 @@ +from prostate_cancer.modeling.full_model.google_vit import google_vit + + +__all__ = ["google_vit"] From 25c61936bef9dbf02c537ad84fba57a09e6f4f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 17 Mar 2026 13:42:01 +0000 Subject: [PATCH 010/284] fix: typo --- configs/experiment/train/images/google_vit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/train/images/google_vit.yaml b/configs/experiment/train/images/google_vit.yaml index a5cce768..bda02fb5 100644 --- a/configs/experiment/train/images/google_vit.yaml +++ b/configs/experiment/train/images/google_vit.yaml @@ -5,4 +5,4 @@ defaults: - /model/google_vit_model@model - _self_ -model_name: "Goole ViT" +model_name: "Google ViT" From 7c5eb7992a92088183eb37c8a5899cb09c4193a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 19 Mar 2026 08:31:21 +0000 Subject: [PATCH 011/284] feat: google vit inference configs --- .../predict/images/google_vit/base.yaml | 18 ++++++++++++++++++ .../images/google_vit/heatmaps/base.yaml | 8 ++++++++ .../google_vit/heatmaps/mmci_tl_val.yaml | 6 ++++++ .../heatmaps_and_max_aggregations/base.yaml | 9 +++++++++ .../google_vit/max_aggregations/base.yaml | 9 +++++++++ .../mean_pool_max_aggregations/base.yaml | 9 +++++++++ .../test/images/google_vit/base.yaml | 16 ++++++++++++++++ .../test/images/google_vit/curves/base.yaml | 8 ++++++++ .../images/google_vit/nested_metrics/base.yaml | 8 ++++++++ .../images/google_vit/test_suite/base.yaml | 8 ++++++++ .../images/google_vit/test_suite/mmci_tl.yaml | 6 ++++++ .../google_vit/tile_histograms/base.yaml | 12 ++++++++++++ .../test/images/google_vit/tile_threshold.yaml | 14 ++++++++++++++ configs/model/checkpoints/google_vit.yaml | 6 ++++++ 14 files changed, 137 insertions(+) create mode 100644 configs/experiment/predict/images/google_vit/base.yaml create mode 100644 configs/experiment/predict/images/google_vit/heatmaps/base.yaml create mode 100644 configs/experiment/predict/images/google_vit/heatmaps/mmci_tl_val.yaml create mode 100644 configs/experiment/predict/images/google_vit/heatmaps_and_max_aggregations/base.yaml create mode 100644 configs/experiment/predict/images/google_vit/max_aggregations/base.yaml create mode 100644 configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/test/images/google_vit/base.yaml create mode 100644 configs/experiment/test/images/google_vit/curves/base.yaml create mode 100644 configs/experiment/test/images/google_vit/nested_metrics/base.yaml create mode 100644 configs/experiment/test/images/google_vit/test_suite/base.yaml create mode 100644 configs/experiment/test/images/google_vit/test_suite/mmci_tl.yaml create mode 100644 configs/experiment/test/images/google_vit/tile_histograms/base.yaml create mode 100644 configs/experiment/test/images/google_vit/tile_threshold.yaml create mode 100644 configs/model/checkpoints/google_vit.yaml diff --git a/configs/experiment/predict/images/google_vit/base.yaml b/configs/experiment/predict/images/google_vit/base.yaml new file mode 100644 index 00000000..eb092c8f --- /dev/null +++ b/configs/experiment/predict/images/google_vit/base.yaml @@ -0,0 +1,18 @@ +# @package _global_ + +defaults: + - /experiment/predict/base + - /datamodule/images_processor_datamodule@datamodule + - /model/google_vit_model@model + - /model/checkpoints/google_vit@model_data + - _self_ + + + +datamodule: + batch_size: 96 + num_workers: 8 + + predict: + uris: + - ${predict_data.tiles_uri_224} diff --git a/configs/experiment/predict/images/google_vit/heatmaps/base.yaml b/configs/experiment/predict/images/google_vit/heatmaps/base.yaml new file mode 100644 index 00000000..6b411e48 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/heatmaps/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/google_vit/base + - /callbacks/heatmap@callbacks + - _self_ + +use_case: "Heatmaps" diff --git a/configs/experiment/predict/images/google_vit/heatmaps/mmci_tl_val.yaml b/configs/experiment/predict/images/google_vit/heatmaps/mmci_tl_val.yaml new file mode 100644 index 00000000..487d9509 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/heatmaps/mmci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/google_vit/heatmaps/base + - /data/mmci_tl_val@predict_data + - _self_ diff --git a/configs/experiment/predict/images/google_vit/heatmaps_and_max_aggregations/base.yaml b/configs/experiment/predict/images/google_vit/heatmaps_and_max_aggregations/base.yaml new file mode 100644 index 00000000..1469ca53 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/heatmaps_and_max_aggregations/base.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /callbacks/heatmap_and_aggregator@callbacks + - /aggregator/max@aggregator + - /experiment/predict/images/google_vit/base + - _self_ + +use_case: "Heatmaps & Max. Aggregation" diff --git a/configs/experiment/predict/images/google_vit/max_aggregations/base.yaml b/configs/experiment/predict/images/google_vit/max_aggregations/base.yaml new file mode 100644 index 00000000..4eb854dc --- /dev/null +++ b/configs/experiment/predict/images/google_vit/max_aggregations/base.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator@callbacks + - /aggregator/max@aggregator + - /experiment/predict/images/google_vit/base + - _self_ + +use_case: "Max. Aggregation" diff --git a/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/base.yaml b/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/base.yaml new file mode 100644 index 00000000..79e512c1 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/base.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator@callbacks + - /aggregator/mean_pool_max@aggregator + - /experiment/predict/images/google_vit/base + - _self_ + +use_case: "Mean Pool Max. Aggregation" diff --git a/configs/experiment/test/images/google_vit/base.yaml b/configs/experiment/test/images/google_vit/base.yaml new file mode 100644 index 00000000..69a7e0e7 --- /dev/null +++ b/configs/experiment/test/images/google_vit/base.yaml @@ -0,0 +1,16 @@ +# @package _global_ + +defaults: + - /experiment/test/base + - /datamodule/images_processor_datamodule@datamodule + - /model/google_vit_model@model + - /model/checkpoints/google_vit@model_data + - _self_ + +datamodule: + batch_size: 96 + num_workers: 8 + + test: + uris: + - ${test_data.tiles_uri_224} diff --git a/configs/experiment/test/images/google_vit/curves/base.yaml b/configs/experiment/test/images/google_vit/curves/base.yaml new file mode 100644 index 00000000..48bec3f0 --- /dev/null +++ b/configs/experiment/test/images/google_vit/curves/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/curves@callbacks + - /experiment/test/images/google_vit/base + - _self_ + +use_case: "TL Curves" \ No newline at end of file diff --git a/configs/experiment/test/images/google_vit/nested_metrics/base.yaml b/configs/experiment/test/images/google_vit/nested_metrics/base.yaml new file mode 100644 index 00000000..cb8f7c96 --- /dev/null +++ b/configs/experiment/test/images/google_vit/nested_metrics/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/nested_metrics@callbacks + - /experiment/test/images/google_vit/base + - _self_ + +use_case: "TL Per-Slide metrics" \ No newline at end of file diff --git a/configs/experiment/test/images/google_vit/test_suite/base.yaml b/configs/experiment/test/images/google_vit/test_suite/base.yaml new file mode 100644 index 00000000..5a35d18c --- /dev/null +++ b/configs/experiment/test/images/google_vit/test_suite/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/images/google_vit/base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/images/google_vit/test_suite/mmci_tl.yaml b/configs/experiment/test/images/google_vit/test_suite/mmci_tl.yaml new file mode 100644 index 00000000..99b97d0d --- /dev/null +++ b/configs/experiment/test/images/google_vit/test_suite/mmci_tl.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/images/google_vit/test_suite/base + - /data/mmci_tl_test@test_data + - _self_ diff --git a/configs/experiment/test/images/google_vit/tile_histograms/base.yaml b/configs/experiment/test/images/google_vit/tile_histograms/base.yaml new file mode 100644 index 00000000..d6221b94 --- /dev/null +++ b/configs/experiment/test/images/google_vit/tile_histograms/base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /callbacks/tile_histograms@callbacks + - /experiment/test/images/google_vit/base + - _self_ + +datamodule: + test: + stratified_filter: true + +use_case: "TL Histograms" diff --git a/configs/experiment/test/images/google_vit/tile_threshold.yaml b/configs/experiment/test/images/google_vit/tile_threshold.yaml new file mode 100644 index 00000000..b9f83d0f --- /dev/null +++ b/configs/experiment/test/images/google_vit/tile_threshold.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/images/google_vit/base + - /data/mmci_tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.9 # Pathologist's estimate +use_case: "Tile Threshold Estimation" diff --git a/configs/model/checkpoints/google_vit.yaml b/configs/model/checkpoints/google_vit.yaml new file mode 100644 index 00000000..ad44aecb --- /dev/null +++ b/configs/model/checkpoints/google_vit.yaml @@ -0,0 +1,6 @@ +tile_threshold: 0.0 +slide_max_threshold: 0.0 +slide_mpm_threshold: 0.0 + +checkpoint: "mlflow-artifacts:/65/426434cd46d34d3a8660e5454062a5c4/artifacts/checkpoints/epoch=0-step=13889/checkpoint.ckpt" +model_name: "Google ViT" From b60682ed4eb646a8f4cc2c4cf5c3f878f146c78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 19 Mar 2026 19:27:52 +0000 Subject: [PATCH 012/284] feat: different Lr --- prostate_cancer/prostate_cancer_model.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/prostate_cancer_model.py index d1be0f9b..2ce69943 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/prostate_cancer_model.py @@ -153,4 +153,20 @@ def predict_step( return self._get_predictions(logits) def configure_optimizers(self) -> Optimizer: - return AdamW(self.parameters(), self.lr) + assert self.full_model is not None, "Expected full_model to be provided" + + backbone = self.full_model.vit + head = self.full_model.classifier + + param_groups = [ + { + "params": backbone.parameters(), + "lr": self.lr * 0.1, + }, + { + "params": head.parameters(), + "lr": self.lr, + }, + ] + + return AdamW(param_groups, weight_decay=0.01) From 5c88dc0ed53f6700cf1fb843b77953512c3d72aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 21 Mar 2026 12:25:11 +0000 Subject: [PATCH 013/284] feat: warmuup --- prostate_cancer/prostate_cancer_model.py | 30 ++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/prostate_cancer_model.py index 2ce69943..e64f8dc1 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/prostate_cancer_model.py @@ -152,7 +152,7 @@ def predict_step( logits = self(inputs) return self._get_predictions(logits) - def configure_optimizers(self) -> Optimizer: + def configure_optimizers(self): assert self.full_model is not None, "Expected full_model to be provided" backbone = self.full_model.vit @@ -169,4 +169,30 @@ def configure_optimizers(self) -> Optimizer: }, ] - return AdamW(param_groups, weight_decay=0.01) + optimizer = AdamW(param_groups, weight_decay=0.01) + + total_steps = self.trainer.estimated_stepping_batches + steps_per_epoch = total_steps // self.trainer.max_epochs + warmup_steps = steps_per_epoch // 2 + + def lr_lambda(current_step: int) -> float: + # --- warmup phase + if current_step < warmup_steps: + return float(current_step) / float(max(1, warmup_steps)) + + # --- cosine decay phase + progress = float(current_step - warmup_steps) / float( + max(1, total_steps - warmup_steps) + ) + return 0.5 * (1.0 + math.cos(math.pi * progress)) + + scheduler = LambdaLR(optimizer, lr_lambda) + + return { + "optimizer": optimizer, + "lr_scheduler": { + "scheduler": scheduler, + "interval": "step", # IMPORTANT for warmup + "frequency": 1, + }, + } From 14e4ce115699b0f0b134c0e303e56b1f614d8748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 21 Mar 2026 13:03:04 +0000 Subject: [PATCH 014/284] fix: add imports --- prostate_cancer/prostate_cancer_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/prostate_cancer_model.py index e64f8dc1..0b2edf98 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/prostate_cancer_model.py @@ -1,9 +1,11 @@ from copy import deepcopy +import math import torch from lightning import LightningModule from torch import Tensor, nn from torch.optim import AdamW +from torch.optim.lr_scheduler import LambdaLR from torch.optim.optimizer import Optimizer from torchmetrics import ( AUROC, From f0bb3e671151de7e2e21b4b9787cd7ddc1a99f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 22 Mar 2026 07:19:25 +0000 Subject: [PATCH 015/284] feat: basic augments --- .../images_processor_datamodule.yaml | 2 ++ .../datamodule/transforms/augmentations.yaml | 14 ++++++++++ prostate_cancer/prostate_cancer_model.py | 28 +------------------ 3 files changed, 17 insertions(+), 27 deletions(-) create mode 100644 configs/datamodule/transforms/augmentations.yaml diff --git a/configs/datamodule/images_processor_datamodule.yaml b/configs/datamodule/images_processor_datamodule.yaml index 887188fa..ff2447ac 100644 --- a/configs/datamodule/images_processor_datamodule.yaml +++ b/configs/datamodule/images_processor_datamodule.yaml @@ -5,12 +5,14 @@ defaults: - datasets@predict: images/unlabeled_with_processor - samplers@sampler: stratified_weighted_random + - transforms@default_transform: augmentations processor: _target_: transformers.ViTImageProcessor.from_pretrained pretrained_model_name_or_path: google/vit-base-patch16-224 train: + transforms: ${datamodule.default_transform} processor: ${datamodule.processor} stratified_filter: true val: diff --git a/configs/datamodule/transforms/augmentations.yaml b/configs/datamodule/transforms/augmentations.yaml new file mode 100644 index 00000000..950552fc --- /dev/null +++ b/configs/datamodule/transforms/augmentations.yaml @@ -0,0 +1,14 @@ +_target_: albumentations.Compose +transforms: + - _target_: albumentations.RandomRotate90 + p: 1.0 + - _target_: albumentations.HorizontalFlip + p: 0.5 + - _target_: albumentations.VerticalFlip + p: 0.5 + - _target_: albumentations.RandomGamma + p: 0.5 + - _target_: albumentations.RandomBrightnessContrast + p: 0.5 + - _target_: albumentations.HueSaturationValue + p: 0.5 diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/prostate_cancer_model.py index 0b2edf98..985ecf5e 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/prostate_cancer_model.py @@ -170,31 +170,5 @@ def configure_optimizers(self): "lr": self.lr, }, ] - - optimizer = AdamW(param_groups, weight_decay=0.01) - total_steps = self.trainer.estimated_stepping_batches - steps_per_epoch = total_steps // self.trainer.max_epochs - warmup_steps = steps_per_epoch // 2 - - def lr_lambda(current_step: int) -> float: - # --- warmup phase - if current_step < warmup_steps: - return float(current_step) / float(max(1, warmup_steps)) - - # --- cosine decay phase - progress = float(current_step - warmup_steps) / float( - max(1, total_steps - warmup_steps) - ) - return 0.5 * (1.0 + math.cos(math.pi * progress)) - - scheduler = LambdaLR(optimizer, lr_lambda) - - return { - "optimizer": optimizer, - "lr_scheduler": { - "scheduler": scheduler, - "interval": "step", # IMPORTANT for warmup - "frequency": 1, - }, - } + return AdamW(param_groups, weight_decay=0.01) From 0725828002f38fa5596262c8f5e4c9380f25ef9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 23 Mar 2026 07:19:22 +0000 Subject: [PATCH 016/284] feat: use standard decomposition --- .../{full_model => backbone}/google_vit.yaml | 0 configs/model/google_vit_model.yaml | 6 +- .../{full_model => backbone}/google_vit.py | 6 +- .../modeling/decode_head/binary_classifier.py | 48 +++++++++++----- .../modeling/full_model/__init__.py | 4 -- prostate_cancer/prostate_cancer_model.py | 57 ++----------------- 6 files changed, 48 insertions(+), 73 deletions(-) rename configs/model/{full_model => backbone}/google_vit.yaml (100%) rename prostate_cancer/modeling/{full_model => backbone}/google_vit.py (68%) delete mode 100644 prostate_cancer/modeling/full_model/__init__.py diff --git a/configs/model/full_model/google_vit.yaml b/configs/model/backbone/google_vit.yaml similarity index 100% rename from configs/model/full_model/google_vit.yaml rename to configs/model/backbone/google_vit.yaml diff --git a/configs/model/google_vit_model.yaml b/configs/model/google_vit_model.yaml index 40adb42f..1d0a8d9d 100644 --- a/configs/model/google_vit_model.yaml +++ b/configs/model/google_vit_model.yaml @@ -1,6 +1,10 @@ defaults: - - full_model: google_vit + - decode_head: binary_classifier + - backbone: google_vit - _self_ +decode_head: + in_features: 768 + _target_: prostate_cancer.prostate_cancer_model.ProstateCancerModel lr: ??? diff --git a/prostate_cancer/modeling/full_model/google_vit.py b/prostate_cancer/modeling/backbone/google_vit.py similarity index 68% rename from prostate_cancer/modeling/full_model/google_vit.py rename to prostate_cancer/modeling/backbone/google_vit.py index 2fd5b7ef..31986bc8 100644 --- a/prostate_cancer/modeling/full_model/google_vit.py +++ b/prostate_cancer/modeling/backbone/google_vit.py @@ -1,11 +1,11 @@ -from transformers import ViTForImageClassification +from transformers import ViTForImageClassification, ViTModel -def google_vit() -> ViTForImageClassification: +def google_vit() -> ViTModel: return ViTForImageClassification.from_pretrained( "google/vit-base-patch16-224", num_labels=1, id2label={0: "carcinoma"}, label2id={"carcinoma": 0}, ignore_mismatched_sizes=True, - ) + ).vit diff --git a/prostate_cancer/modeling/decode_head/binary_classifier.py b/prostate_cancer/modeling/decode_head/binary_classifier.py index 9bf145a2..ded56bf0 100644 --- a/prostate_cancer/modeling/decode_head/binary_classifier.py +++ b/prostate_cancer/modeling/decode_head/binary_classifier.py @@ -2,31 +2,51 @@ class BinaryClassifier(nn.Module): - """This is a single neuron classifier for feature vectors. + """Universal binary classifier head. - Note: - Since CNN extractors provide features in 4 dimensional tensor, we use global pooling to flatten it. - This is not neccessary for GigaPath features which are already flat. + Supports: + - CNN features: (B, C, H, W) + - Flat features: (B, C) + - Transformer features (e.g. ViT): (B, N, C) """ - def __init__(self, in_features: int) -> None: + def __init__( + self, + in_features: int, + dropout: float = 0.5, + pooling: str = "cls", # "cls" or "mean" for transformers + ) -> None: super().__init__() self.in_features = in_features + self.pooling = pooling + self.global_pool = nn.AdaptiveMaxPool2d(1) - self.dropout = nn.Dropout(p=0.5) + self.dropout = nn.Dropout(p=dropout) self.proj = nn.Linear(in_features, 1) def forward(self, x: Tensor) -> Tensor: - # CNN feature extractors - if x.ndim == 4: # (B, C, W, H) - x = self.global_pool(x) # (B, C, 1, 1) - x = x.flatten(start_dim=-3, end_dim=-1) # (B, C) - # PGP already provides 2 dimensions - elif x.ndim != 2: # (B, C) + # CNN case: (B, C, H, W) + if x.ndim == 4: + x = self.global_pool(x) # (B, C, 1, 1) + x = x.flatten(1) # (B, C) + + # Transformer case: (B, N, C) + elif x.ndim == 3: + if self.pooling == "cls": + x = x[:, 0] # CLS token + elif self.pooling == "mean": + x = x.mean(dim=1) # mean pooling + else: + raise ValueError(f"Unknown pooling: {self.pooling}") + + # Already flat: (B, C) + elif x.ndim != 2: raise ValueError( - f"Expected input of shape (B, C) or (B, C, W, H), got {x.shape}" + f"Expected input of shape (B, C), (B, N, C), or (B, C, H, W), got {x.shape}" ) + # Final classification x = self.dropout(x) x = self.proj(x) - return x # Return logits, not probability + + return x diff --git a/prostate_cancer/modeling/full_model/__init__.py b/prostate_cancer/modeling/full_model/__init__.py deleted file mode 100644 index b2d40692..00000000 --- a/prostate_cancer/modeling/full_model/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from prostate_cancer.modeling.full_model.google_vit import google_vit - - -__all__ = ["google_vit"] diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/prostate_cancer_model.py index 985ecf5e..f1cbaf73 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/prostate_cancer_model.py @@ -1,11 +1,9 @@ from copy import deepcopy -import math import torch from lightning import LightningModule from torch import Tensor, nn from torch.optim import AdamW -from torch.optim.lr_scheduler import LambdaLR from torch.optim.optimizer import Optimizer from torchmetrics import ( AUROC, @@ -23,28 +21,11 @@ class ProstateCancerModel(LightningModule): def __init__( - self, - backbone: nn.Module | None = None, - decode_head: nn.Module | None = None, - full_model: nn.Module | None = None, - lr: float = 1e-4, + self, backbone: nn.Module | None, decode_head: nn.Module, lr: float ) -> None: super().__init__() - - # enforce mutually exclusive configs - if full_model is not None and (backbone is not None or decode_head is not None): - raise ValueError( - "Provide either `full_model` OR (`backbone` + `decode_head`), not both." - ) - - if full_model is None and decode_head is None: - raise ValueError( - "`decode_head` must be provided when using a backbone." - ) - self.backbone = backbone self.decode_head = decode_head - self.full_model = full_model self.lr = lr self.criterion = nn.BCEWithLogitsLoss(reduction="mean") @@ -74,21 +55,11 @@ def __init__( ) def forward(self, x: Tensor) -> Tensor: - # --- full model mode - if self.full_model is not None: - outputs = self.full_model(x) - - # HuggingFace models return objects - if hasattr(outputs, "logits"): - return outputs.logits - - return outputs - - # --- backbone is None in case of embeddings features = self.backbone(x) if self.backbone else x - # --- if not full model, decode head must be present - assert self.decode_head is not None, "Decode head must be present if not full model" + if hasattr(features, "last_hidden_state"): + features = features.last_hidden_state + logits = self.decode_head(features) return logits @@ -154,21 +125,5 @@ def predict_step( logits = self(inputs) return self._get_predictions(logits) - def configure_optimizers(self): - assert self.full_model is not None, "Expected full_model to be provided" - - backbone = self.full_model.vit - head = self.full_model.classifier - - param_groups = [ - { - "params": backbone.parameters(), - "lr": self.lr * 0.1, - }, - { - "params": head.parameters(), - "lr": self.lr, - }, - ] - - return AdamW(param_groups, weight_decay=0.01) + def configure_optimizers(self) -> Optimizer: + return AdamW(self.parameters(), self.lr) From 1c35e2233f586de2c89227e9309f90c4dca53cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 23 Mar 2026 07:25:32 +0000 Subject: [PATCH 017/284] fix: import --- configs/model/backbone/google_vit.yaml | 2 +- prostate_cancer/modeling/backbone/__init__.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/configs/model/backbone/google_vit.yaml b/configs/model/backbone/google_vit.yaml index 9c872233..3e9b3e96 100644 --- a/configs/model/backbone/google_vit.yaml +++ b/configs/model/backbone/google_vit.yaml @@ -1 +1 @@ -_target_: prostate_cancer.modeling.full_model.google_vit \ No newline at end of file +_target_: prostate_cancer.modeling.backbone.google_vit \ No newline at end of file diff --git a/prostate_cancer/modeling/backbone/__init__.py b/prostate_cancer/modeling/backbone/__init__.py index fc4831a5..f8074130 100644 --- a/prostate_cancer/modeling/backbone/__init__.py +++ b/prostate_cancer/modeling/backbone/__init__.py @@ -1,5 +1,6 @@ from prostate_cancer.modeling.backbone.resnet50 import resnet50 from prostate_cancer.modeling.backbone.vgg16 import vgg16 +from prostate_cancer.modeling.backbone.google_vit import google_vit -__all__ = ["resnet50", "vgg16"] +__all__ = ["resnet50", "vgg16", "google_vit"] From fb654f40c1cccf23c27470491c06c9e6b7cac80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 23 Mar 2026 07:56:31 +0000 Subject: [PATCH 018/284] feat: module hierarchy --- configs/model/google_vit_model.yaml | 2 +- configs/model/pgp_model.yaml | 2 +- configs/model/resnet50_model.yaml | 2 +- configs/model/vgg16_model.yaml | 2 +- configs/model/virchow2_model.yaml | 2 +- .../{prostate_cancer_model.py => base_model.py} | 14 ++------------ prostate_cancer/cnn_model.py | 17 +++++++++++++++++ prostate_cancer/embedding_model.py | 14 ++++++++++++++ prostate_cancer/vit_model.py | 17 +++++++++++++++++ 9 files changed, 55 insertions(+), 17 deletions(-) rename prostate_cancer/{prostate_cancer_model.py => base_model.py} (88%) create mode 100644 prostate_cancer/cnn_model.py create mode 100644 prostate_cancer/embedding_model.py create mode 100644 prostate_cancer/vit_model.py diff --git a/configs/model/google_vit_model.yaml b/configs/model/google_vit_model.yaml index 1d0a8d9d..8d083792 100644 --- a/configs/model/google_vit_model.yaml +++ b/configs/model/google_vit_model.yaml @@ -6,5 +6,5 @@ defaults: decode_head: in_features: 768 -_target_: prostate_cancer.prostate_cancer_model.ProstateCancerModel +_target_: prostate_cancer.vit_model.ViTProstateModel lr: ??? diff --git a/configs/model/pgp_model.yaml b/configs/model/pgp_model.yaml index 3a50e847..29dc5f25 100644 --- a/configs/model/pgp_model.yaml +++ b/configs/model/pgp_model.yaml @@ -1,4 +1,4 @@ -_target_: prostate_cancer.prostate_cancer_model.ProstateCancerModel +_target_: prostate_cancer.embedding_model.EmbeddingProstateModel backbone: null decode_head: _target_: prostate_cancer.modeling.decode_head.BinaryClassifier diff --git a/configs/model/resnet50_model.yaml b/configs/model/resnet50_model.yaml index 28e9c46a..1005ad21 100644 --- a/configs/model/resnet50_model.yaml +++ b/configs/model/resnet50_model.yaml @@ -6,5 +6,5 @@ defaults: decode_head: in_features: 2048 -_target_: prostate_cancer.prostate_cancer_model.ProstateCancerModel +_target_: prostate_cancer.cnn_model.CNNProstateModel lr: ??? diff --git a/configs/model/vgg16_model.yaml b/configs/model/vgg16_model.yaml index accc7c95..fb49a330 100644 --- a/configs/model/vgg16_model.yaml +++ b/configs/model/vgg16_model.yaml @@ -6,5 +6,5 @@ defaults: decode_head: in_features: 512 -_target_: prostate_cancer.prostate_cancer_model.ProstateCancerModel +_target_: prostate_cancer.cnn_model.CNNProstateModel lr: ??? diff --git a/configs/model/virchow2_model.yaml b/configs/model/virchow2_model.yaml index 94048ea9..c4437214 100644 --- a/configs/model/virchow2_model.yaml +++ b/configs/model/virchow2_model.yaml @@ -1,4 +1,4 @@ -_target_: prostate_cancer.prostate_cancer_model.ProstateCancerModel +_target_: prostate_cancer.embedding_model.EmbeddingProstateModel backbone: null decode_head: _target_: prostate_cancer.modeling.decode_head.BinaryClassifier diff --git a/prostate_cancer/prostate_cancer_model.py b/prostate_cancer/base_model.py similarity index 88% rename from prostate_cancer/prostate_cancer_model.py rename to prostate_cancer/base_model.py index f1cbaf73..26ac8c35 100644 --- a/prostate_cancer/prostate_cancer_model.py +++ b/prostate_cancer/base_model.py @@ -20,12 +20,8 @@ class ProstateCancerModel(LightningModule): - def __init__( - self, backbone: nn.Module | None, decode_head: nn.Module, lr: float - ) -> None: + def __init__(self, lr: float) -> None: super().__init__() - self.backbone = backbone - self.decode_head = decode_head self.lr = lr self.criterion = nn.BCEWithLogitsLoss(reduction="mean") @@ -55,13 +51,7 @@ def __init__( ) def forward(self, x: Tensor) -> Tensor: - features = self.backbone(x) if self.backbone else x - - if hasattr(features, "last_hidden_state"): - features = features.last_hidden_state - - logits = self.decode_head(features) - return logits + raise NotImplementedError() def _get_predictions(self, logits: Tensor) -> Tensor: return torch.sigmoid(logits) diff --git a/prostate_cancer/cnn_model.py b/prostate_cancer/cnn_model.py new file mode 100644 index 00000000..2791ce6d --- /dev/null +++ b/prostate_cancer/cnn_model.py @@ -0,0 +1,17 @@ +from prostate_cancer.base_model import ProstateCancerModel + +from torch import Tensor +from torch import nn + + +class CNNProstateModel(ProstateCancerModel): + + def __init__(self, backbone: nn.Module, decode_head: nn.Module, lr: float) -> None: + super().__init__(lr=lr) + self.backbone = backbone + self.decode_head = decode_head + + def forward(self, x: Tensor) -> Tensor: + features = self.backbone(x) + logits = self.decode_head(features) + return logits diff --git a/prostate_cancer/embedding_model.py b/prostate_cancer/embedding_model.py new file mode 100644 index 00000000..8492b3f7 --- /dev/null +++ b/prostate_cancer/embedding_model.py @@ -0,0 +1,14 @@ +from prostate_cancer.base_model import ProstateCancerModel + +from torch import Tensor +from torch import nn + +class EmbeddingProstateModel(ProstateCancerModel): + + def __init__(self, decode_head: nn.Module, lr: float) -> None: + super().__init__(lr=lr) + self.decode_head = decode_head + + def forward(self, x: Tensor) -> Tensor: + logits = self.decode_head(x) + return logits diff --git a/prostate_cancer/vit_model.py b/prostate_cancer/vit_model.py new file mode 100644 index 00000000..17e5eb42 --- /dev/null +++ b/prostate_cancer/vit_model.py @@ -0,0 +1,17 @@ +from prostate_cancer.base_model import ProstateCancerModel + +from torch import Tensor +from torch import nn +from transformers import ViTModel + +class ViTProstateModel(ProstateCancerModel): + + def __init__(self, backbone: ViTModel, decode_head: nn.Module, lr: float) -> None: + super().__init__(lr=lr) + self.backbone = backbone + self.decode_head = decode_head + + def forward(self, x: Tensor) -> Tensor: + features = self.backbone(x).last_hidden_state + logits = self.decode_head(features) + return logits From 0d0782c653a848ad124f069ea304bc3fd112b1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 23 Mar 2026 08:14:29 +0000 Subject: [PATCH 019/284] feat: classifier hierarchy --- .../model/decode_head/binary_classifier.yaml | 2 - configs/model/decode_head/cnn_classifier.yaml | 2 + .../decode_head/embedding_classifier.yaml | 2 + configs/model/decode_head/vit_classifier.yaml | 2 + configs/model/google_vit_model.yaml | 2 +- configs/model/pgp_model.yaml | 2 +- configs/model/resnet50_model.yaml | 2 +- configs/model/vgg16_model.yaml | 2 +- configs/model/virchow2_model.yaml | 2 +- .../modeling/decode_head/__init__.py | 5 ++- .../modeling/decode_head/binary_classifier.py | 38 +------------------ .../modeling/decode_head/cnn_classifier.py | 18 +++++++++ .../decode_head/embedding_classifier.py | 10 +++++ .../modeling/decode_head/vit_classifier.py | 22 +++++++++++ 14 files changed, 66 insertions(+), 45 deletions(-) delete mode 100644 configs/model/decode_head/binary_classifier.yaml create mode 100644 configs/model/decode_head/cnn_classifier.yaml create mode 100644 configs/model/decode_head/embedding_classifier.yaml create mode 100644 configs/model/decode_head/vit_classifier.yaml create mode 100644 prostate_cancer/modeling/decode_head/cnn_classifier.py create mode 100644 prostate_cancer/modeling/decode_head/embedding_classifier.py create mode 100644 prostate_cancer/modeling/decode_head/vit_classifier.py diff --git a/configs/model/decode_head/binary_classifier.yaml b/configs/model/decode_head/binary_classifier.yaml deleted file mode 100644 index 5bf4a0b2..00000000 --- a/configs/model/decode_head/binary_classifier.yaml +++ /dev/null @@ -1,2 +0,0 @@ -_target_: prostate_cancer.modeling.decode_head.BinaryClassifier -in_features: ??? diff --git a/configs/model/decode_head/cnn_classifier.yaml b/configs/model/decode_head/cnn_classifier.yaml new file mode 100644 index 00000000..7d66347d --- /dev/null +++ b/configs/model/decode_head/cnn_classifier.yaml @@ -0,0 +1,2 @@ +_target_: prostate_cancer.modeling.decode_head.BinaryCNNClassifier +in_features: ??? diff --git a/configs/model/decode_head/embedding_classifier.yaml b/configs/model/decode_head/embedding_classifier.yaml new file mode 100644 index 00000000..8af19f44 --- /dev/null +++ b/configs/model/decode_head/embedding_classifier.yaml @@ -0,0 +1,2 @@ +_target_: prostate_cancer.modeling.decode_head.BinaryEmbeddingClassifier +in_features: ??? diff --git a/configs/model/decode_head/vit_classifier.yaml b/configs/model/decode_head/vit_classifier.yaml new file mode 100644 index 00000000..6c5b5180 --- /dev/null +++ b/configs/model/decode_head/vit_classifier.yaml @@ -0,0 +1,2 @@ +_target_: prostate_cancer.modeling.decode_head.BinaryViTClassifier +in_features: ??? diff --git a/configs/model/google_vit_model.yaml b/configs/model/google_vit_model.yaml index 8d083792..287515ed 100644 --- a/configs/model/google_vit_model.yaml +++ b/configs/model/google_vit_model.yaml @@ -1,5 +1,5 @@ defaults: - - decode_head: binary_classifier + - decode_head: vit_classifier - backbone: google_vit - _self_ diff --git a/configs/model/pgp_model.yaml b/configs/model/pgp_model.yaml index 29dc5f25..c63c6e71 100644 --- a/configs/model/pgp_model.yaml +++ b/configs/model/pgp_model.yaml @@ -1,6 +1,6 @@ _target_: prostate_cancer.embedding_model.EmbeddingProstateModel backbone: null decode_head: - _target_: prostate_cancer.modeling.decode_head.BinaryClassifier + _target_: prostate_cancer.modeling.decode_head.BinaryEmbeddingClassifier in_features: 1536 lr: ??? diff --git a/configs/model/resnet50_model.yaml b/configs/model/resnet50_model.yaml index 1005ad21..d79d492e 100644 --- a/configs/model/resnet50_model.yaml +++ b/configs/model/resnet50_model.yaml @@ -1,5 +1,5 @@ defaults: - - decode_head: binary_classifier + - decode_head: cnn_classifier - backbone: resnet50 - _self_ diff --git a/configs/model/vgg16_model.yaml b/configs/model/vgg16_model.yaml index fb49a330..1fe92edd 100644 --- a/configs/model/vgg16_model.yaml +++ b/configs/model/vgg16_model.yaml @@ -1,5 +1,5 @@ defaults: - - decode_head: binary_classifier + - decode_head: cnn_classifier - backbone: vgg16 - _self_ diff --git a/configs/model/virchow2_model.yaml b/configs/model/virchow2_model.yaml index c4437214..372bec9e 100644 --- a/configs/model/virchow2_model.yaml +++ b/configs/model/virchow2_model.yaml @@ -1,6 +1,6 @@ _target_: prostate_cancer.embedding_model.EmbeddingProstateModel backbone: null decode_head: - _target_: prostate_cancer.modeling.decode_head.BinaryClassifier + _target_: prostate_cancer.modeling.decode_head.BinaryEmbeddingClassifier in_features: 2560 lr: ??? diff --git a/prostate_cancer/modeling/decode_head/__init__.py b/prostate_cancer/modeling/decode_head/__init__.py index 802bdbf9..a29d3faa 100644 --- a/prostate_cancer/modeling/decode_head/__init__.py +++ b/prostate_cancer/modeling/decode_head/__init__.py @@ -1,4 +1,7 @@ from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier +from prostate_cancer.modeling.decode_head.cnn_classifier import BinaryCNNClassifier +from prostate_cancer.modeling.decode_head.vit_classifier import BinaryViTClassifier +from prostate_cancer.modeling.decode_head.embedding_classifier import BinaryEmbeddingClassifier -__all__ = ["BinaryClassifier"] +__all__ = ["BinaryClassifier", "BinaryCNNClassifier", "BinaryViTClassifier", "BinaryEmbeddingClassifier"] diff --git a/prostate_cancer/modeling/decode_head/binary_classifier.py b/prostate_cancer/modeling/decode_head/binary_classifier.py index ded56bf0..b7c9e507 100644 --- a/prostate_cancer/modeling/decode_head/binary_classifier.py +++ b/prostate_cancer/modeling/decode_head/binary_classifier.py @@ -2,51 +2,15 @@ class BinaryClassifier(nn.Module): - """Universal binary classifier head. - - Supports: - - CNN features: (B, C, H, W) - - Flat features: (B, C) - - Transformer features (e.g. ViT): (B, N, C) - """ - def __init__( self, in_features: int, dropout: float = 0.5, - pooling: str = "cls", # "cls" or "mean" for transformers ) -> None: super().__init__() self.in_features = in_features - self.pooling = pooling - - self.global_pool = nn.AdaptiveMaxPool2d(1) self.dropout = nn.Dropout(p=dropout) self.proj = nn.Linear(in_features, 1) def forward(self, x: Tensor) -> Tensor: - # CNN case: (B, C, H, W) - if x.ndim == 4: - x = self.global_pool(x) # (B, C, 1, 1) - x = x.flatten(1) # (B, C) - - # Transformer case: (B, N, C) - elif x.ndim == 3: - if self.pooling == "cls": - x = x[:, 0] # CLS token - elif self.pooling == "mean": - x = x.mean(dim=1) # mean pooling - else: - raise ValueError(f"Unknown pooling: {self.pooling}") - - # Already flat: (B, C) - elif x.ndim != 2: - raise ValueError( - f"Expected input of shape (B, C), (B, N, C), or (B, C, H, W), got {x.shape}" - ) - - # Final classification - x = self.dropout(x) - x = self.proj(x) - - return x + raise NotImplementedError() diff --git a/prostate_cancer/modeling/decode_head/cnn_classifier.py b/prostate_cancer/modeling/decode_head/cnn_classifier.py new file mode 100644 index 00000000..5c918433 --- /dev/null +++ b/prostate_cancer/modeling/decode_head/cnn_classifier.py @@ -0,0 +1,18 @@ +from torch import Tensor, nn + +from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier + +class BinaryCNNClassifier(BinaryClassifier): + def __init__(self, in_features: int, dropout: float = 0.5) -> None: + super().__init__(in_features=in_features, dropout=dropout) + self.global_pool = nn.AdaptiveMaxPool2d(1) + + def forward(self, x: Tensor) -> Tensor: + assert x.ndim == 4 + + x = self.global_pool(x) # (B, C, 1, 1) + x = x.flatten(1) # (B, C) + x = self.dropout(x) + x = self.proj(x) + + return x diff --git a/prostate_cancer/modeling/decode_head/embedding_classifier.py b/prostate_cancer/modeling/decode_head/embedding_classifier.py new file mode 100644 index 00000000..abe74b4a --- /dev/null +++ b/prostate_cancer/modeling/decode_head/embedding_classifier.py @@ -0,0 +1,10 @@ +from torch import Tensor + +from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier + +class BinaryEmbeddingClassifier(BinaryClassifier): + def forward(self, x: Tensor) -> Tensor: + assert x.ndim == 2 + x = self.dropout(x) + x = self.proj(x) + return x diff --git a/prostate_cancer/modeling/decode_head/vit_classifier.py b/prostate_cancer/modeling/decode_head/vit_classifier.py new file mode 100644 index 00000000..f0f94a67 --- /dev/null +++ b/prostate_cancer/modeling/decode_head/vit_classifier.py @@ -0,0 +1,22 @@ +from torch import Tensor + +from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier + +class BinaryViTClassifier(BinaryClassifier): + def __init__(self, in_features: int, pooling: str, dropout: float = 0.5) -> None: + super().__init__(in_features=in_features, dropout=dropout) + self.pooling = pooling + + def forward(self, x: Tensor) -> Tensor: + assert x.ndim == 3 + + if self.pooling == "cls": + x = x[:, 0] + elif self.pooling == "mean": + x = x.mean(dim=1) + else: + raise ValueError(f"Unknown pooling: {self.pooling}") + + x = self.dropout(x) + x = self.proj(x) + return x From 2b8d1334234ce7c05b3d8fb2e754cca540c4e5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 23 Mar 2026 08:16:30 +0000 Subject: [PATCH 020/284] fix: add pooling --- configs/model/google_vit_model.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/model/google_vit_model.yaml b/configs/model/google_vit_model.yaml index 287515ed..e346e7d5 100644 --- a/configs/model/google_vit_model.yaml +++ b/configs/model/google_vit_model.yaml @@ -5,6 +5,7 @@ defaults: decode_head: in_features: 768 + pooling: cls _target_: prostate_cancer.vit_model.ViTProstateModel lr: ??? From 0468fa8d68be46f298dbb91a963fbd18ec8078fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 23 Mar 2026 08:27:05 +0000 Subject: [PATCH 021/284] feat: clean dataset configs --- configs/datamodule/datasets/images/labeled.yaml | 1 + .../datasets/images/labeled_with_processor.yaml | 11 ----------- configs/datamodule/datasets/images/unlabeled.yaml | 3 ++- .../datasets/images/unlabeled_with_processor.yaml | 4 ---- configs/datamodule/images_datamodule.yaml | 4 ++++ configs/datamodule/images_processor_datamodule.yaml | 13 +++++-------- configs/datamodule/processor/google_vit.yaml | 2 ++ 7 files changed, 14 insertions(+), 24 deletions(-) delete mode 100644 configs/datamodule/datasets/images/labeled_with_processor.yaml delete mode 100644 configs/datamodule/datasets/images/unlabeled_with_processor.yaml create mode 100644 configs/datamodule/processor/google_vit.yaml diff --git a/configs/datamodule/datasets/images/labeled.yaml b/configs/datamodule/datasets/images/labeled.yaml index 7899f9b8..e6cda831 100644 --- a/configs/datamodule/datasets/images/labeled.yaml +++ b/configs/datamodule/datasets/images/labeled.yaml @@ -3,3 +3,4 @@ uris: ??? thresholds: ${thresholds} carcinoma_roi_t: ${thresholds.carcinoma_roi_t} stratified_filter: ??? +processor: ??? diff --git a/configs/datamodule/datasets/images/labeled_with_processor.yaml b/configs/datamodule/datasets/images/labeled_with_processor.yaml deleted file mode 100644 index c49c1720..00000000 --- a/configs/datamodule/datasets/images/labeled_with_processor.yaml +++ /dev/null @@ -1,11 +0,0 @@ -_target_: prostate_cancer.datamodule.datasets.LabeledTilesDataset - -uris: ??? - -thresholds: ${thresholds} - -carcinoma_roi_t: ${thresholds.carcinoma_roi_t} - -processor: ??? - -stratified_filter: ??? diff --git a/configs/datamodule/datasets/images/unlabeled.yaml b/configs/datamodule/datasets/images/unlabeled.yaml index 3667d566..4e790d13 100644 --- a/configs/datamodule/datasets/images/unlabeled.yaml +++ b/configs/datamodule/datasets/images/unlabeled.yaml @@ -1,3 +1,4 @@ _target_: prostate_cancer.datamodule.datasets.UnlabeledTilesDataset uris: ??? -thresholds: ${thresholds} \ No newline at end of file +thresholds: ${thresholds} +processor: ??? \ No newline at end of file diff --git a/configs/datamodule/datasets/images/unlabeled_with_processor.yaml b/configs/datamodule/datasets/images/unlabeled_with_processor.yaml deleted file mode 100644 index 0c4c2699..00000000 --- a/configs/datamodule/datasets/images/unlabeled_with_processor.yaml +++ /dev/null @@ -1,4 +0,0 @@ -_target_: prostate_cancer.datamodule.datasets.UnlabeledTilesDataset -uris: ??? -processor: ??? -thresholds: ${thresholds} diff --git a/configs/datamodule/images_datamodule.yaml b/configs/datamodule/images_datamodule.yaml index 73938e50..ecbe5654 100644 --- a/configs/datamodule/images_datamodule.yaml +++ b/configs/datamodule/images_datamodule.yaml @@ -10,14 +10,18 @@ defaults: train: transforms: ??? # specified during the training (stain / non-stain augmentations) stratified_filter: true + processor: null val: transforms: ${datamodule.default_transform} stratified_filter: true + processor: null test: transforms: ${datamodule.default_transform} stratified_filter: false + processor: null predict: transforms: ${datamodule.default_transform} + processor: null batch_size: ??? num_workers: ??? diff --git a/configs/datamodule/images_processor_datamodule.yaml b/configs/datamodule/images_processor_datamodule.yaml index ff2447ac..22c93253 100644 --- a/configs/datamodule/images_processor_datamodule.yaml +++ b/configs/datamodule/images_processor_datamodule.yaml @@ -1,15 +1,12 @@ defaults: - - datasets@train: images/labeled_with_processor - - datasets@val: images/labeled_with_processor - - datasets@test: images/labeled_with_processor - - datasets@predict: images/unlabeled_with_processor + - datasets@train: images/labeled + - datasets@val: images/labeled + - datasets@test: images/labeled + - datasets@predict: images/unlabeled - samplers@sampler: stratified_weighted_random - transforms@default_transform: augmentations - -processor: - _target_: transformers.ViTImageProcessor.from_pretrained - pretrained_model_name_or_path: google/vit-base-patch16-224 + - processor: google_vit train: transforms: ${datamodule.default_transform} diff --git a/configs/datamodule/processor/google_vit.yaml b/configs/datamodule/processor/google_vit.yaml new file mode 100644 index 00000000..dbb7a3f4 --- /dev/null +++ b/configs/datamodule/processor/google_vit.yaml @@ -0,0 +1,2 @@ +_target_: transformers.ViTImageProcessor.from_pretrained +pretrained_model_name_or_path: google/vit-base-patch16-224 \ No newline at end of file From 0aec4259e9695411670b4a128575cb6c47c76cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 24 Mar 2026 07:40:44 +0000 Subject: [PATCH 022/284] feat: new staining augmentation --- .../datamodule/transforms/augmentations.yaml | 24 ++++++++++++ .../normalize_w_stain_augments.yaml | 39 ++++++++++++------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/configs/datamodule/transforms/augmentations.yaml b/configs/datamodule/transforms/augmentations.yaml index 950552fc..4c43ebec 100644 --- a/configs/datamodule/transforms/augmentations.yaml +++ b/configs/datamodule/transforms/augmentations.yaml @@ -12,3 +12,27 @@ transforms: p: 0.5 - _target_: albumentations.HueSaturationValue p: 0.5 + + - _target_: rationai.staining.AugmentStainingTransform + + conversion: + _target_: rationai.staining.ColorConversion.from_stain_vectors + stain_vector_1: [0.479, 0.823, 0.305] + stain_vector_2: [0.156, 0.949, 0.273] + stain_vector_3: [-0.189, -0.242, 0.952] + + noise_transform: + _target_: albumentations.Compose + transforms: + - _target_: albumentations.MultiplicativeNoise + multiplier: [0.5, 1.5] + per_channel: true + elementwise: false + p: 1.0 + + - _target_: albumentations.AdditiveNoise + noise_type: uniform + noise_params: + ranges: + - [-0.005, 0.005] + p: 1.0 diff --git a/configs/datamodule/transforms/normalize_w_stain_augments.yaml b/configs/datamodule/transforms/normalize_w_stain_augments.yaml index e852cc41..995db648 100644 --- a/configs/datamodule/transforms/normalize_w_stain_augments.yaml +++ b/configs/datamodule/transforms/normalize_w_stain_augments.yaml @@ -12,22 +12,31 @@ transforms: p: 0.5 - _target_: albumentations.HueSaturationValue p: 0.5 - - _target_: rationai.staining.transforms.ColorTransform + + - _target_: rationai.staining.AugmentStainingTransform + conversion: - _target_: hydra.utils.get_object - path: rationai.staining.ColorConversion.RGB2HER_MMCI_PROSTATE - - _target_: albumentations.MultiplicativeNoise - multiplier: [0.5, 1.5] - per_channel: true - elementwise: false - - _target_: albumentations.AdditiveNoise - noise_type: "uniform" - noise_params: - ranges: [[-0.005, 0.005]] - - _target_: rationai.staining.transforms.ColorTransform - conversion: - _target_: hydra.utils.get_object - path: rationai.staining.ColorConversion.HER_MMCI_PROSTATE2RGB + _target_: rationai.staining.ColorConversion.from_stain_vectors + stain_vector_1: [0.479, 0.823, 0.305] + stain_vector_2: [0.156, 0.949, 0.273] + stain_vector_3: [-0.189, -0.242, 0.952] + + noise_transform: + _target_: albumentations.Compose + transforms: + - _target_: albumentations.MultiplicativeNoise + multiplier: [0.5, 1.5] + per_channel: true + elementwise: false + p: 1.0 + + - _target_: albumentations.AdditiveNoise + noise_type: uniform + noise_params: + ranges: + - [-0.005, 0.005] + p: 1.0 + - _target_: albumentations.Normalize mean: ${mean} std: ${std} From 0998544fb9efa823292a955b557fe7d338a5ef05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 24 Mar 2026 07:51:34 +0000 Subject: [PATCH 023/284] feat: fix param name --- configs/datamodule/transforms/augmentations.yaml | 6 +++--- .../datamodule/transforms/normalize_w_stain_augments.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/datamodule/transforms/augmentations.yaml b/configs/datamodule/transforms/augmentations.yaml index 4c43ebec..bbcd47ba 100644 --- a/configs/datamodule/transforms/augmentations.yaml +++ b/configs/datamodule/transforms/augmentations.yaml @@ -17,9 +17,9 @@ transforms: conversion: _target_: rationai.staining.ColorConversion.from_stain_vectors - stain_vector_1: [0.479, 0.823, 0.305] - stain_vector_2: [0.156, 0.949, 0.273] - stain_vector_3: [-0.189, -0.242, 0.952] + stain1: [0.479, 0.823, 0.305] + stain2: [0.156, 0.949, 0.273] + stain3: [-0.189, -0.242, 0.952] noise_transform: _target_: albumentations.Compose diff --git a/configs/datamodule/transforms/normalize_w_stain_augments.yaml b/configs/datamodule/transforms/normalize_w_stain_augments.yaml index 995db648..28d6afc7 100644 --- a/configs/datamodule/transforms/normalize_w_stain_augments.yaml +++ b/configs/datamodule/transforms/normalize_w_stain_augments.yaml @@ -17,9 +17,9 @@ transforms: conversion: _target_: rationai.staining.ColorConversion.from_stain_vectors - stain_vector_1: [0.479, 0.823, 0.305] - stain_vector_2: [0.156, 0.949, 0.273] - stain_vector_3: [-0.189, -0.242, 0.952] + stain1: [0.479, 0.823, 0.305] + stain2: [0.156, 0.949, 0.273] + stain3: [-0.189, -0.242, 0.952] noise_transform: _target_: albumentations.Compose From 07fe542e7af175c831214733203b0d243b848a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 25 Mar 2026 08:53:41 +0000 Subject: [PATCH 024/284] fix: do not include backbone in embedding models --- configs/model/pgp_model.yaml | 1 - configs/model/virchow2_model.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/configs/model/pgp_model.yaml b/configs/model/pgp_model.yaml index c63c6e71..19d00a3c 100644 --- a/configs/model/pgp_model.yaml +++ b/configs/model/pgp_model.yaml @@ -1,5 +1,4 @@ _target_: prostate_cancer.embedding_model.EmbeddingProstateModel -backbone: null decode_head: _target_: prostate_cancer.modeling.decode_head.BinaryEmbeddingClassifier in_features: 1536 diff --git a/configs/model/virchow2_model.yaml b/configs/model/virchow2_model.yaml index 372bec9e..c395b2ce 100644 --- a/configs/model/virchow2_model.yaml +++ b/configs/model/virchow2_model.yaml @@ -1,5 +1,4 @@ _target_: prostate_cancer.embedding_model.EmbeddingProstateModel -backbone: null decode_head: _target_: prostate_cancer.modeling.decode_head.BinaryEmbeddingClassifier in_features: 2560 From 7bd17d0ebe45c3fe5c463898e923e646ce2aa3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 25 Mar 2026 08:57:30 +0000 Subject: [PATCH 025/284] fix: importing old model --- prostate_cancer/callbacks/cam_callback.py | 5 ++--- .../callbacks/cancer_prediction_table_callback.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/prostate_cancer/callbacks/cam_callback.py b/prostate_cancer/callbacks/cam_callback.py index edf57f2b..37d85eb7 100644 --- a/prostate_cancer/callbacks/cam_callback.py +++ b/prostate_cancer/callbacks/cam_callback.py @@ -12,7 +12,6 @@ from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from torchvision.transforms import Resize -from prostate_cancer.prostate_cancer_model import ProstateCancerModel from prostate_cancer.typing import LabeledSampleBatch @@ -83,8 +82,8 @@ def on_test_batch_end( def on_test_start( self, trainer: lightning.Trainer, pl_module: lightning.LightningModule ) -> None: - if not isinstance(pl_module, ProstateCancerModel): - raise ValueError("Model should be of type ProstateCancerModel.") + if not hasattr(self.model, "decode_head"): + raise ValueError("Model should have decode head.") self.model = pl_module self.decode_head = cast("BinaryClassifier", self.model.decode_head) diff --git a/prostate_cancer/callbacks/cancer_prediction_table_callback.py b/prostate_cancer/callbacks/cancer_prediction_table_callback.py index 7832c0f9..ac3d5bda 100644 --- a/prostate_cancer/callbacks/cancer_prediction_table_callback.py +++ b/prostate_cancer/callbacks/cancer_prediction_table_callback.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: - from prostate_cancer.data import DataModule + from prostate_cancer.datamodule import DataModule class CancerPredictionTableCallback(MultiloaderLifecycle): From 3c5431aa1e1b45c6c040813b38cc3e4452cbf26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 25 Mar 2026 15:47:58 +0000 Subject: [PATCH 026/284] feat: use just backbone directly --- prostate_cancer/modeling/backbone/google_vit.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/prostate_cancer/modeling/backbone/google_vit.py b/prostate_cancer/modeling/backbone/google_vit.py index 31986bc8..74ac0c61 100644 --- a/prostate_cancer/modeling/backbone/google_vit.py +++ b/prostate_cancer/modeling/backbone/google_vit.py @@ -1,11 +1,4 @@ -from transformers import ViTForImageClassification, ViTModel - +from transformers import ViTModel def google_vit() -> ViTModel: - return ViTForImageClassification.from_pretrained( - "google/vit-base-patch16-224", - num_labels=1, - id2label={0: "carcinoma"}, - label2id={"carcinoma": 0}, - ignore_mismatched_sizes=True, - ).vit + return ViTModel.from_pretrained("google/vit-base-patch16-224") From 31db90df43dec061174a5707fc21504b63dae4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 27 Mar 2026 08:57:43 +0000 Subject: [PATCH 027/284] feat: add new checkpoint --- configs/model/checkpoints/google_vit.yaml | 2 +- pyproject.toml | 2 +- uv.lock | 91 +++++++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/configs/model/checkpoints/google_vit.yaml b/configs/model/checkpoints/google_vit.yaml index ad44aecb..9e34c409 100644 --- a/configs/model/checkpoints/google_vit.yaml +++ b/configs/model/checkpoints/google_vit.yaml @@ -2,5 +2,5 @@ tile_threshold: 0.0 slide_max_threshold: 0.0 slide_mpm_threshold: 0.0 -checkpoint: "mlflow-artifacts:/65/426434cd46d34d3a8660e5454062a5c4/artifacts/checkpoints/epoch=0-step=13889/checkpoint.ckpt" +checkpoint: "mlflow-artifacts:/65/312222e62e3f451ab8cbe21d9cb1a120/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" model_name: "Google ViT" diff --git a/pyproject.toml b/pyproject.toml index 15b9614e..bbd0bce1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,4 +39,4 @@ dependencies = [ ] [dependency-groups] -dev = ["mypy", "ruff"] +dev = ["mypy", "ruff", "pre-commit"] diff --git a/uv.lock b/uv.lock index 6c269e85..c8b7ad59 100644 --- a/uv.lock +++ b/uv.lock @@ -233,6 +233,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, ] +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + [[package]] name = "charset-normalizer" version = "3.4.5" @@ -443,6 +452,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + [[package]] name = "docker" version = "7.1.0" @@ -810,6 +828,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c6/50/e0edd38dcd63fb26a8547f13d28f7a008bc4a3fd4eb4ff030673f22ad41a/hydra_core-1.3.2-py3-none-any.whl", hash = "sha256:fa0238a9e31df3373b35b0bfb672c34cc92718d21f81311d8996a16de1141d8b", size = 154547, upload-time = "2023-02-23T18:33:40.801Z" }, ] +[[package]] +name = "identify" +version = "2.6.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, +] + [[package]] name = "idna" version = "3.11" @@ -1354,6 +1381,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + [[package]] name = "numpy" version = "2.2.5" @@ -1724,6 +1760,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, ] +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, +] + [[package]] name = "propcache" version = "0.4.1" @@ -1809,6 +1870,7 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "mypy" }, + { name = "pre-commit" }, { name = "ruff" }, ] @@ -1840,6 +1902,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "mypy" }, + { name = "pre-commit" }, { name = "ruff" }, ] @@ -2003,6 +2066,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-discovery" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/88/815e53084c5079a59df912825a279f41dd2e0df82281770eadc732f5352c/python_discovery-1.2.1.tar.gz", hash = "sha256:180c4d114bff1c32462537eac5d6a332b768242b76b69c0259c7d14b1b680c9e", size = 58457, upload-time = "2026-03-26T22:30:44.496Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl", hash = "sha256:b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502", size = 31674, upload-time = "2026-03-26T22:30:43.396Z" }, +] + [[package]] name = "pytorch-lightning" version = "2.6.1" @@ -2913,6 +2989,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, ] +[[package]] +name = "virtualenv" +version = "21.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, +] + [[package]] name = "waitress" version = "3.0.2" From 9bd3d02f23a3c4020ce0da3ed33705a9c0eafd10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 27 Mar 2026 08:58:08 +0000 Subject: [PATCH 028/284] feat: ruff --- prostate_cancer/cnn_model.py | 6 ++---- prostate_cancer/datamodule/datasets/tile_dataset.py | 4 +++- prostate_cancer/embedding_model.py | 5 ++--- prostate_cancer/modeling/backbone/__init__.py | 4 ++-- prostate_cancer/modeling/backbone/google_vit.py | 1 + prostate_cancer/modeling/decode_head/__init__.py | 11 +++++++++-- .../modeling/decode_head/cnn_classifier.py | 5 +++-- .../modeling/decode_head/embedding_classifier.py | 1 + .../modeling/decode_head/vit_classifier.py | 1 + prostate_cancer/vit_model.py | 7 +++---- 10 files changed, 27 insertions(+), 18 deletions(-) diff --git a/prostate_cancer/cnn_model.py b/prostate_cancer/cnn_model.py index 2791ce6d..b7089e99 100644 --- a/prostate_cancer/cnn_model.py +++ b/prostate_cancer/cnn_model.py @@ -1,11 +1,9 @@ -from prostate_cancer.base_model import ProstateCancerModel +from torch import Tensor, nn -from torch import Tensor -from torch import nn +from prostate_cancer.base_model import ProstateCancerModel class CNNProstateModel(ProstateCancerModel): - def __init__(self, backbone: nn.Module, decode_head: nn.Module, lr: float) -> None: super().__init__(lr=lr) self.backbone = backbone diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_dataset.py index 77f24351..2ccec468 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_dataset.py @@ -110,7 +110,9 @@ def __getitem__(self, idx: int) -> LabeledSample | UnlabeledSample: tensor_image = self.to_tensor(image=image)["image"] if self.processor is not None: - tensor_image = self.processor(tensor_image,return_tensors="pt")["pixel_values"].squeeze(0) + tensor_image = self.processor(tensor_image, return_tensors="pt")[ + "pixel_values" + ].squeeze(0) if self.include_label: label = torch.tensor( diff --git a/prostate_cancer/embedding_model.py b/prostate_cancer/embedding_model.py index 8492b3f7..c70368c8 100644 --- a/prostate_cancer/embedding_model.py +++ b/prostate_cancer/embedding_model.py @@ -1,10 +1,9 @@ +from torch import Tensor, nn + from prostate_cancer.base_model import ProstateCancerModel -from torch import Tensor -from torch import nn class EmbeddingProstateModel(ProstateCancerModel): - def __init__(self, decode_head: nn.Module, lr: float) -> None: super().__init__(lr=lr) self.decode_head = decode_head diff --git a/prostate_cancer/modeling/backbone/__init__.py b/prostate_cancer/modeling/backbone/__init__.py index f8074130..1525619d 100644 --- a/prostate_cancer/modeling/backbone/__init__.py +++ b/prostate_cancer/modeling/backbone/__init__.py @@ -1,6 +1,6 @@ +from prostate_cancer.modeling.backbone.google_vit import google_vit from prostate_cancer.modeling.backbone.resnet50 import resnet50 from prostate_cancer.modeling.backbone.vgg16 import vgg16 -from prostate_cancer.modeling.backbone.google_vit import google_vit -__all__ = ["resnet50", "vgg16", "google_vit"] +__all__ = ["google_vit", "resnet50", "vgg16"] diff --git a/prostate_cancer/modeling/backbone/google_vit.py b/prostate_cancer/modeling/backbone/google_vit.py index 74ac0c61..179d598a 100644 --- a/prostate_cancer/modeling/backbone/google_vit.py +++ b/prostate_cancer/modeling/backbone/google_vit.py @@ -1,4 +1,5 @@ from transformers import ViTModel + def google_vit() -> ViTModel: return ViTModel.from_pretrained("google/vit-base-patch16-224") diff --git a/prostate_cancer/modeling/decode_head/__init__.py b/prostate_cancer/modeling/decode_head/__init__.py index a29d3faa..a964ca41 100644 --- a/prostate_cancer/modeling/decode_head/__init__.py +++ b/prostate_cancer/modeling/decode_head/__init__.py @@ -1,7 +1,14 @@ from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier from prostate_cancer.modeling.decode_head.cnn_classifier import BinaryCNNClassifier +from prostate_cancer.modeling.decode_head.embedding_classifier import ( + BinaryEmbeddingClassifier, +) from prostate_cancer.modeling.decode_head.vit_classifier import BinaryViTClassifier -from prostate_cancer.modeling.decode_head.embedding_classifier import BinaryEmbeddingClassifier -__all__ = ["BinaryClassifier", "BinaryCNNClassifier", "BinaryViTClassifier", "BinaryEmbeddingClassifier"] +__all__ = [ + "BinaryCNNClassifier", + "BinaryClassifier", + "BinaryEmbeddingClassifier", + "BinaryViTClassifier", +] diff --git a/prostate_cancer/modeling/decode_head/cnn_classifier.py b/prostate_cancer/modeling/decode_head/cnn_classifier.py index 5c918433..dbcb4027 100644 --- a/prostate_cancer/modeling/decode_head/cnn_classifier.py +++ b/prostate_cancer/modeling/decode_head/cnn_classifier.py @@ -2,6 +2,7 @@ from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier + class BinaryCNNClassifier(BinaryClassifier): def __init__(self, in_features: int, dropout: float = 0.5) -> None: super().__init__(in_features=in_features, dropout=dropout) @@ -10,8 +11,8 @@ def __init__(self, in_features: int, dropout: float = 0.5) -> None: def forward(self, x: Tensor) -> Tensor: assert x.ndim == 4 - x = self.global_pool(x) # (B, C, 1, 1) - x = x.flatten(1) # (B, C) + x = self.global_pool(x) # (B, C, 1, 1) + x = x.flatten(1) # (B, C) x = self.dropout(x) x = self.proj(x) diff --git a/prostate_cancer/modeling/decode_head/embedding_classifier.py b/prostate_cancer/modeling/decode_head/embedding_classifier.py index abe74b4a..484e8da1 100644 --- a/prostate_cancer/modeling/decode_head/embedding_classifier.py +++ b/prostate_cancer/modeling/decode_head/embedding_classifier.py @@ -2,6 +2,7 @@ from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier + class BinaryEmbeddingClassifier(BinaryClassifier): def forward(self, x: Tensor) -> Tensor: assert x.ndim == 2 diff --git a/prostate_cancer/modeling/decode_head/vit_classifier.py b/prostate_cancer/modeling/decode_head/vit_classifier.py index f0f94a67..c6454875 100644 --- a/prostate_cancer/modeling/decode_head/vit_classifier.py +++ b/prostate_cancer/modeling/decode_head/vit_classifier.py @@ -2,6 +2,7 @@ from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier + class BinaryViTClassifier(BinaryClassifier): def __init__(self, in_features: int, pooling: str, dropout: float = 0.5) -> None: super().__init__(in_features=in_features, dropout=dropout) diff --git a/prostate_cancer/vit_model.py b/prostate_cancer/vit_model.py index 17e5eb42..c0a762e2 100644 --- a/prostate_cancer/vit_model.py +++ b/prostate_cancer/vit_model.py @@ -1,11 +1,10 @@ +from torch import Tensor, nn +from transformers import ViTModel + from prostate_cancer.base_model import ProstateCancerModel -from torch import Tensor -from torch import nn -from transformers import ViTModel class ViTProstateModel(ProstateCancerModel): - def __init__(self, backbone: ViTModel, decode_head: nn.Module, lr: float) -> None: super().__init__(lr=lr) self.backbone = backbone From fda9da7565f23d2c0a8df522a99077cad3125ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 27 Mar 2026 09:52:48 +0000 Subject: [PATCH 029/284] fix: mypy --- prostate_cancer/callbacks/cam_callback.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/prostate_cancer/callbacks/cam_callback.py b/prostate_cancer/callbacks/cam_callback.py index 37d85eb7..428384c6 100644 --- a/prostate_cancer/callbacks/cam_callback.py +++ b/prostate_cancer/callbacks/cam_callback.py @@ -13,7 +13,7 @@ from torchvision.transforms import Resize from prostate_cancer.typing import LabeledSampleBatch - +from prostate_cancer.cnn_model import CNNProstateModel if TYPE_CHECKING: from prostate_cancer.datamodule.data_module import DataModule @@ -25,6 +25,16 @@ def __init__(self, resize_shape: tuple[int, int]) -> None: super().__init__() self.resize = Resize(resize_shape) + def on_test_start( + self, trainer: lightning.Trainer, pl_module: lightning.LightningModule + ) -> None: + if not isinstance(pl_module, CNNProstateModel): + raise ValueError("Model must be a CNNProstateModel to generate CAMs.") + + self.model = pl_module + self.decode_head = cast("BinaryClassifier", self.model.decode_head) + self.linear_in_features = self.decode_head.proj.in_features + def on_test_dataloader_start( self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int ) -> None: @@ -79,18 +89,7 @@ def on_test_batch_end( data = self._explain(inputs) self.mask_builder.update(data=data, xs=metadata["x"], ys=metadata["y"]) - def on_test_start( - self, trainer: lightning.Trainer, pl_module: lightning.LightningModule - ) -> None: - if not hasattr(self.model, "decode_head"): - raise ValueError("Model should have decode head.") - - self.model = pl_module - self.decode_head = cast("BinaryClassifier", self.model.decode_head) - self.linear_in_features = self.decode_head.proj.in_features - def _explain(self, inputs: torch.Tensor) -> torch.Tensor: - assert self.model.backbone is not None feature_maps = self.model.backbone(inputs) # (B, C, H, W) _, c, _, _ = feature_maps.shape From 3647ea0c8a49cf434be4af25cf24280ab9ff0836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 27 Mar 2026 09:57:50 +0000 Subject: [PATCH 030/284] fix: ruff --- prostate_cancer/callbacks/cam_callback.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prostate_cancer/callbacks/cam_callback.py b/prostate_cancer/callbacks/cam_callback.py index 428384c6..6b59236f 100644 --- a/prostate_cancer/callbacks/cam_callback.py +++ b/prostate_cancer/callbacks/cam_callback.py @@ -12,8 +12,9 @@ from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from torchvision.transforms import Resize -from prostate_cancer.typing import LabeledSampleBatch from prostate_cancer.cnn_model import CNNProstateModel +from prostate_cancer.typing import LabeledSampleBatch + if TYPE_CHECKING: from prostate_cancer.datamodule.data_module import DataModule From 9ef606a02b901045873214118860f47c76af3b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 27 Mar 2026 12:59:09 +0000 Subject: [PATCH 031/284] fix: comments --- prostate_cancer/datamodule/datasets/tile_dataset.py | 8 ++++---- prostate_cancer/modeling/decode_head/cnn_classifier.py | 3 ++- .../modeling/decode_head/embedding_classifier.py | 4 +++- prostate_cancer/modeling/decode_head/vit_classifier.py | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_dataset.py index 2ccec468..a995af18 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_dataset.py @@ -107,10 +107,10 @@ def __getitem__(self, idx: int) -> LabeledSample | UnlabeledSample: if self.transforms is not None: image = self.transforms(image=image)["image"] - tensor_image = self.to_tensor(image=image)["image"] - - if self.processor is not None: - tensor_image = self.processor(tensor_image, return_tensors="pt")[ + if self.processor is None: + tensor_image = self.to_tensor(image=image)["image"] + else: + tensor_image = self.processor(image, return_tensors="pt")[ "pixel_values" ].squeeze(0) diff --git a/prostate_cancer/modeling/decode_head/cnn_classifier.py b/prostate_cancer/modeling/decode_head/cnn_classifier.py index dbcb4027..e16048ea 100644 --- a/prostate_cancer/modeling/decode_head/cnn_classifier.py +++ b/prostate_cancer/modeling/decode_head/cnn_classifier.py @@ -9,7 +9,8 @@ def __init__(self, in_features: int, dropout: float = 0.5) -> None: self.global_pool = nn.AdaptiveMaxPool2d(1) def forward(self, x: Tensor) -> Tensor: - assert x.ndim == 4 + if x.ndim != 4: + raise ValueError(f"Expected 4D tensor, got {x.ndim}D") x = self.global_pool(x) # (B, C, 1, 1) x = x.flatten(1) # (B, C) diff --git a/prostate_cancer/modeling/decode_head/embedding_classifier.py b/prostate_cancer/modeling/decode_head/embedding_classifier.py index 484e8da1..d31821c1 100644 --- a/prostate_cancer/modeling/decode_head/embedding_classifier.py +++ b/prostate_cancer/modeling/decode_head/embedding_classifier.py @@ -5,7 +5,9 @@ class BinaryEmbeddingClassifier(BinaryClassifier): def forward(self, x: Tensor) -> Tensor: - assert x.ndim == 2 + if x.ndim != 2: + raise ValueError(f"Expected 2D tensor, got {x.ndim}D") + x = self.dropout(x) x = self.proj(x) return x diff --git a/prostate_cancer/modeling/decode_head/vit_classifier.py b/prostate_cancer/modeling/decode_head/vit_classifier.py index c6454875..3b11889a 100644 --- a/prostate_cancer/modeling/decode_head/vit_classifier.py +++ b/prostate_cancer/modeling/decode_head/vit_classifier.py @@ -9,7 +9,8 @@ def __init__(self, in_features: int, pooling: str, dropout: float = 0.5) -> None self.pooling = pooling def forward(self, x: Tensor) -> Tensor: - assert x.ndim == 3 + if x.ndim != 3: + raise ValueError(f"Expected 3D tensor, got {x.ndim}D") if self.pooling == "cls": x = x[:, 0] From 2726712c4ad36067e11becb44d162a2495b92c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 31 Mar 2026 08:21:48 +0000 Subject: [PATCH 032/284] feat: new checkpoint --- configs/model/checkpoints/google_vit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/model/checkpoints/google_vit.yaml b/configs/model/checkpoints/google_vit.yaml index 9e34c409..beb9a452 100644 --- a/configs/model/checkpoints/google_vit.yaml +++ b/configs/model/checkpoints/google_vit.yaml @@ -2,5 +2,5 @@ tile_threshold: 0.0 slide_max_threshold: 0.0 slide_mpm_threshold: 0.0 -checkpoint: "mlflow-artifacts:/65/312222e62e3f451ab8cbe21d9cb1a120/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" +checkpoint: "mlflow-artifacts:/65/d56efaa9e46546938e40156d8574327e/artifacts/checkpoints/epoch=0-step=41667/checkpoint.ckpt" model_name: "Google ViT" From 4ba973ee250d1deb286ffec1ae6c70a558f258b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 1 Apr 2026 08:50:58 +0000 Subject: [PATCH 033/284] feat: set estimated tl threshold --- configs/model/checkpoints/google_vit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/model/checkpoints/google_vit.yaml b/configs/model/checkpoints/google_vit.yaml index beb9a452..f11f9503 100644 --- a/configs/model/checkpoints/google_vit.yaml +++ b/configs/model/checkpoints/google_vit.yaml @@ -1,4 +1,4 @@ -tile_threshold: 0.0 +tile_threshold: 0.91 slide_max_threshold: 0.0 slide_mpm_threshold: 0.0 From 04ea6d4edcf8ef8465695b1689abd157b8f7386f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 21 Apr 2026 16:14:27 +0000 Subject: [PATCH 034/284] feat: MIL module --- prostate_cancer/__main__.py | 4 +- prostate_cancer/attention_mil_model.py | 109 ++++++++++++++++++ prostate_cancer/base_model.py | 10 +- .../callbacks/aggregator_callback.py | 6 +- prostate_cancer/callbacks/cam_callback.py | 6 +- .../carcinoma_prediction_table_callback.py | 6 +- prostate_cancer/callbacks/curves_callback.py | 4 +- .../callbacks/estimation_callback.py | 6 +- prostate_cancer/callbacks/heatmap_callback.py | 10 +- .../callbacks/nested_metrics_callback.py | 4 +- .../callbacks/num_positive_callback.py | 6 +- .../callbacks/tile_histograms_callback.py | 4 +- prostate_cancer/datamodule/__init__.py | 4 +- prostate_cancer/datamodule/data_module.py | 26 ++--- .../datamodule/datasets/embedding_dataset.py | 20 ++-- .../datamodule/datasets/tile_dataset.py | 12 +- prostate_cancer/typing.py | 32 ++++- 17 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 prostate_cancer/attention_mil_model.py diff --git a/prostate_cancer/__main__.py b/prostate_cancer/__main__.py index edcbca85..948a2135 100644 --- a/prostate_cancer/__main__.py +++ b/prostate_cancer/__main__.py @@ -8,7 +8,7 @@ from rationai.mlkit import Trainer, autolog from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger -from prostate_cancer.datamodule import DataModule +from prostate_cancer.datamodule import TileDataModule from prostate_cancer.log_title import log_checkpoint_title @@ -31,7 +31,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: data = hydra.utils.instantiate( config.datamodule, _recursive_=False, # to avoid instantiating all the datasets - _target_=DataModule, + _target_=TileDataModule, ) model = hydra.utils.instantiate( config.model diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py new file mode 100644 index 00000000..4b6a64c6 --- /dev/null +++ b/prostate_cancer/attention_mil_model.py @@ -0,0 +1,109 @@ +from copy import deepcopy + +import torch +from lightning import LightningModule +from torch import Tensor, nn +from torch.optim.adamw import AdamW +from torch.optim.optimizer import Optimizer +from torchmetrics import MetricCollection, Metric +from torchmetrics.classification import ( + AUROC, Recall, Precision, Accuracy, NegativePredictiveValue, Specificity +) + +from prostate_cancer.typing import LabeledSlideSampleBatch, UnlabeledSlideSampleBatch + + +class ProstateCancerAttentionMIL(LightningModule): + def __init__(self, foundation: str, lr: float) -> None: + super().__init__() + match foundation: + case "pgp": + input_dim = 1536 + case "virchow2": + input_dim = 2560 + case _: + raise ValueError(f"Unknown foundation model: {foundation}") + + self.encoder = nn.Identity() + self.attention = nn.Sequential( + nn.Linear(input_dim, 512), + nn.Tanh(), + nn.Linear(512, 1), + ) + + self.classifier = nn.Linear(input_dim, 1) + self.criterion = nn.BCELoss() + self.lr = lr + + metrics: dict[str, Metric | MetricCollection] = { + "AUC": AUROC("binary"), + "accuracy": Accuracy("binary"), + "precision": Precision("binary"), + "recall": Recall("binary"), + "specificity": Specificity("binary"), + "negative_predictive_value": NegativePredictiveValue("binary") + } + + self.train_metrics = MetricCollection(deepcopy(metrics), prefix="train/") + self.val_metrics = MetricCollection(deepcopy(metrics), prefix="validation/") + self.test_metrics = MetricCollection(deepcopy(metrics), prefix="test/") + + def forward(self, x: Tensor) -> Tensor: + # x has shape (batch_size, num_tiles_padded, embedding_dim) + x = self.encoder(x) + attn = self.attention(x) + attention_weights = torch.softmax(attn.sigmoid(), dim=0) + mask = (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() + attention_weights = attention_weights * mask + attention_weights = attention_weights / attention_weights.sum( + dim=1, keepdim=True + ) + x = self.classifier(x) + x = torch.sum(attention_weights * x, dim=1) + x = x.sigmoid() + + return x.squeeze(-1) + + def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: + bags, labels, _ = batch + + outputs = self(bags) + loss = self.criterion(outputs, labels) + self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) + + self.train_metrics.update(outputs, labels) + self.log_dict( + self.train_metrics, on_epoch=True, on_step=False, batch_size=len(bags) + ) + + return loss + + def validation_step(self, batch: LabeledSlideSampleBatch) -> None: + bags, labels, _ = batch + + outputs = self(bags) + loss = self.criterion(outputs, labels) + self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) + + self.val_metrics.update(outputs, labels) + self.log_dict( + self.val_metrics, on_epoch=True, on_step=False, batch_size=len(bags) + ) + + def test_step(self, batch: LabeledSlideSampleBatch) -> None: + bags, labels, _ = batch + + outputs = self(bags) + + self.test_metrics.update(outputs, labels) + self.log_dict( + self.test_metrics, on_epoch=True, on_step=False, batch_size=len(bags) + ) + + return outputs + + def predict_step(self, batch: UnlabeledSlideSampleBatch) -> Tensor: + return self(batch[0]) + + def configure_optimizers(self) -> Optimizer: + return AdamW(self.parameters(), lr=self.lr) diff --git a/prostate_cancer/base_model.py b/prostate_cancer/base_model.py index 26ac8c35..ecf31b81 100644 --- a/prostate_cancer/base_model.py +++ b/prostate_cancer/base_model.py @@ -16,7 +16,7 @@ Specificity, ) -from prostate_cancer.typing import LabeledSampleBatch, UnlabeledSampleBatch +from prostate_cancer.typing import LabeledTileSampleBatch, UnlabeledTileSampleBatch class ProstateCancerModel(LightningModule): @@ -56,7 +56,7 @@ def forward(self, x: Tensor) -> Tensor: def _get_predictions(self, logits: Tensor) -> Tensor: return torch.sigmoid(logits) - def training_step(self, batch: LabeledSampleBatch) -> Tensor: + def training_step(self, batch: LabeledTileSampleBatch) -> Tensor: inputs, targets, _ = batch logits = self(inputs) predictions = self._get_predictions(logits) @@ -75,7 +75,7 @@ def training_step(self, batch: LabeledSampleBatch) -> Tensor: return loss - def validation_step(self, batch: LabeledSampleBatch) -> None: + def validation_step(self, batch: LabeledTileSampleBatch) -> None: inputs, targets, _ = batch logits = self(inputs) predictions = self._get_predictions(logits) @@ -93,7 +93,7 @@ def validation_step(self, batch: LabeledSampleBatch) -> None: self.log_dict(self.val_metrics, batch_size=len(inputs), on_epoch=True) def test_step( - self, batch: LabeledSampleBatch, batch_idx: int, dataloader_idx: int = 0 + self, batch: LabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0 ) -> Tensor: inputs, targets, _ = batch logits = self(inputs) @@ -109,7 +109,7 @@ def on_test_epoch_end(self) -> None: self.test_metrics.reset() def predict_step( - self, batch: UnlabeledSampleBatch, batch_idx: int, dataloader_idx: int = 0 + self, batch: UnlabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0 ) -> Tensor: inputs, _ = batch logits = self(inputs) diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index fa38eea4..99851b26 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -9,11 +9,11 @@ from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from rationai.mlkit.metrics.aggregators import Aggregator -from prostate_cancer.typing import UnlabeledSampleBatch +from prostate_cancer.typing import UnlabeledTileSampleBatch if TYPE_CHECKING: - from prostate_cancer.datamodule import DataModule + from prostate_cancer.datamodule import TileDataModule class AggregatorCallback(MultiloaderLifecycle): @@ -36,7 +36,7 @@ def on_predict_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: torch.Tensor, - batch: UnlabeledSampleBatch, + batch: UnlabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/cam_callback.py b/prostate_cancer/callbacks/cam_callback.py index 6b59236f..9940fbea 100644 --- a/prostate_cancer/callbacks/cam_callback.py +++ b/prostate_cancer/callbacks/cam_callback.py @@ -13,11 +13,11 @@ from torchvision.transforms import Resize from prostate_cancer.cnn_model import CNNProstateModel -from prostate_cancer.typing import LabeledSampleBatch +from prostate_cancer.typing import LabeledTileSampleBatch if TYPE_CHECKING: - from prostate_cancer.datamodule.data_module import DataModule + from prostate_cancer.datamodule.data_module import TileDataModule from prostate_cancer.modeling.decode_head import BinaryClassifier @@ -82,7 +82,7 @@ def on_test_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: Any, - batch: LabeledSampleBatch, + batch: LabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py index 332ea135..c0ef637c 100644 --- a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py +++ b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py @@ -7,11 +7,11 @@ import torch from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle -from prostate_cancer.typing import UnlabeledSampleBatch +from prostate_cancer.typing import UnlabeledTileSampleBatch if TYPE_CHECKING: - from prostate_cancer.datamodule import DataModule + from prostate_cancer.datamodule import TileDataModule class CarcinomaPredictionTableCallback(MultiloaderLifecycle): @@ -36,7 +36,7 @@ def on_predict_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: torch.Tensor, - batch: UnlabeledSampleBatch, + batch: UnlabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/curves_callback.py b/prostate_cancer/callbacks/curves_callback.py index 753c76e7..d5d58649 100644 --- a/prostate_cancer/callbacks/curves_callback.py +++ b/prostate_cancer/callbacks/curves_callback.py @@ -8,7 +8,7 @@ from sklearn.metrics import auc, precision_recall_curve, roc_curve from postprocessing.slide_level_curves import _plot_curve -from prostate_cancer.typing import LabeledSampleBatch +from prostate_cancer.typing import LabeledTileSampleBatch class CurvesCallback(Callback): @@ -30,7 +30,7 @@ def on_test_batch_end( trainer: Trainer, pl_module: LightningModule, outputs: Any, - batch: LabeledSampleBatch, + batch: LabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/estimation_callback.py b/prostate_cancer/callbacks/estimation_callback.py index d0aadddb..bf815c1e 100644 --- a/prostate_cancer/callbacks/estimation_callback.py +++ b/prostate_cancer/callbacks/estimation_callback.py @@ -10,11 +10,11 @@ from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from rationai.mlkit.metrics.aggregators import Aggregator -from prostate_cancer.typing import UnlabeledSampleBatch +from prostate_cancer.typing import UnlabeledTileSampleBatch if TYPE_CHECKING: - from prostate_cancer.datamodule import DataModule + from prostate_cancer.datamodule import TileDataModule class EstimationCallback(MultiloaderLifecycle): @@ -55,7 +55,7 @@ def on_predict_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: torch.Tensor, - batch: UnlabeledSampleBatch, + batch: UnlabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/heatmap_callback.py b/prostate_cancer/callbacks/heatmap_callback.py index f6566ef3..9b176e4c 100644 --- a/prostate_cancer/callbacks/heatmap_callback.py +++ b/prostate_cancer/callbacks/heatmap_callback.py @@ -7,11 +7,11 @@ from rationai.masks.mask_builders import ScalarMaskBuilder from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle -from prostate_cancer.typing import LabeledSampleBatch, UnlabeledSampleBatch +from prostate_cancer.typing import LabeledTileSampleBatch, UnlabeledTileSampleBatch if TYPE_CHECKING: - from prostate_cancer.datamodule import DataModule + from prostate_cancer.datamodule import TileDataModule class HeatmapCallback(MultiloaderLifecycle): @@ -63,7 +63,7 @@ def on_predict_dataloader_start( def _on_batch_end( self, outputs: Any, - batch: UnlabeledSampleBatch | LabeledSampleBatch, + batch: UnlabeledTileSampleBatch | LabeledTileSampleBatch, ) -> None: if len(batch) == 3: # Test step @@ -80,7 +80,7 @@ def on_test_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: Any, - batch: LabeledSampleBatch, + batch: LabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: @@ -91,7 +91,7 @@ def on_predict_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: Any, - batch: UnlabeledSampleBatch, + batch: UnlabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/nested_metrics_callback.py b/prostate_cancer/callbacks/nested_metrics_callback.py index a0a5303b..cddb7c48 100644 --- a/prostate_cancer/callbacks/nested_metrics_callback.py +++ b/prostate_cancer/callbacks/nested_metrics_callback.py @@ -13,7 +13,7 @@ Specificity, ) -from prostate_cancer.typing import LabeledSampleBatch +from prostate_cancer.typing import LabeledTileSampleBatch class NestedMetricsCallback(Callback): @@ -39,7 +39,7 @@ def on_test_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: Any, - batch: LabeledSampleBatch, + batch: LabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/num_positive_callback.py b/prostate_cancer/callbacks/num_positive_callback.py index a289b285..5085ed30 100644 --- a/prostate_cancer/callbacks/num_positive_callback.py +++ b/prostate_cancer/callbacks/num_positive_callback.py @@ -6,11 +6,11 @@ from rationai.mlkit.lightning.loggers import MLFlowLogger from prostate_cancer.datamodule.datasets.base import get_slide_name -from prostate_cancer.typing import UnlabeledSampleBatch +from prostate_cancer.typing import UnlabeledTileSampleBatch if TYPE_CHECKING: - from prostate_cancer.datamodule import DataModule + from prostate_cancer.datamodule import TileDataModule class NumPositiveCallback(MultiloaderLifecycle): @@ -28,7 +28,7 @@ def on_predict_batch_end( trainer: pl.Trainer, pl_module: pl.LightningModule, outputs: Any, - batch: UnlabeledSampleBatch, + batch: UnlabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/callbacks/tile_histograms_callback.py b/prostate_cancer/callbacks/tile_histograms_callback.py index ad6dd1fc..ca304609 100644 --- a/prostate_cancer/callbacks/tile_histograms_callback.py +++ b/prostate_cancer/callbacks/tile_histograms_callback.py @@ -8,7 +8,7 @@ from numpy.typing import NDArray from rationai.mlkit.lightning.loggers import MLFlowLogger -from prostate_cancer.typing import LabeledSampleBatch +from prostate_cancer.typing import LabeledTileSampleBatch class TileHistogramsCallback(Callback): @@ -23,7 +23,7 @@ def on_test_batch_end( trainer: Trainer, pl_module: LightningModule, outputs: Any, - batch: LabeledSampleBatch, + batch: LabeledTileSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/datamodule/__init__.py b/prostate_cancer/datamodule/__init__.py index 478c4806..299bff3b 100644 --- a/prostate_cancer/datamodule/__init__.py +++ b/prostate_cancer/datamodule/__init__.py @@ -1,4 +1,4 @@ -from prostate_cancer.datamodule.data_module import DataModule +from prostate_cancer.datamodule.data_module import TileDataModule -__all__ = ["DataModule"] +__all__ = ["TileDataModule"] diff --git a/prostate_cancer/datamodule/data_module.py b/prostate_cancer/datamodule/data_module.py index a7fe5f94..57f23d2f 100644 --- a/prostate_cancer/datamodule/data_module.py +++ b/prostate_cancer/datamodule/data_module.py @@ -7,13 +7,13 @@ from rationai.mlkit.data.datasets import MetaTiledSlides from torch.utils.data import DataLoader -from prostate_cancer.typing import LabeledSample, LabeledSampleBatch, UnlabeledSample +from prostate_cancer.typing import LabeledTileSample, LabeledTileSampleBatch, UnlabeledTileSample PartialConf: TypeAlias = DictConfig -class DataModule(LightningDataModule): +class TileDataModule(LightningDataModule): def __init__( self, batch_size: int, @@ -31,29 +31,29 @@ def setup(self, stage: str) -> None: match stage: case "fit": self.train = cast( - "MetaTiledSlides[LabeledSample]", + "MetaTiledSlides[LabeledTileSample]", instantiate(self.datasets["train"]), ) self.val = cast( - "MetaTiledSlides[LabeledSample]", instantiate(self.datasets["val"]) + "MetaTiledSlides[LabeledTileSample]", instantiate(self.datasets["val"]) ) case "val": self.val = cast( - "MetaTiledSlides[LabeledSample]", instantiate(self.datasets["val"]) + "MetaTiledSlides[LabeledTileSample]", instantiate(self.datasets["val"]) ) case "test": self.test = cast( - "MetaTiledSlides[LabeledSample]", instantiate(self.datasets["test"]) + "MetaTiledSlides[LabeledTileSample]", instantiate(self.datasets["test"]) ) case "predict": self.predict = cast( - "MetaTiledSlides[UnlabeledSample]", + "MetaTiledSlides[UnlabeledTileSample]", instantiate(self.datasets["predict"]), ) def _load_sampler( - self, dataset: MetaTiledSlides[LabeledSample] - ) -> Iterable[LabeledSampleBatch] | None: + self, dataset: MetaTiledSlides[LabeledTileSample] + ) -> Iterable[LabeledTileSampleBatch] | None: if self.sampler_partial is not None: return instantiate(self.sampler_partial)( dataset=dataset, target_col="carcinoma" @@ -61,7 +61,7 @@ def _load_sampler( return None - def train_dataloader(self) -> Iterable[LabeledSampleBatch]: + def train_dataloader(self) -> Iterable[LabeledTileSampleBatch]: sampler = self._load_sampler(self.train) shuffle = ( True if sampler is None else None @@ -76,7 +76,7 @@ def train_dataloader(self) -> Iterable[LabeledSampleBatch]: drop_last=True, ) - def val_dataloader(self) -> Iterable[LabeledSampleBatch]: + def val_dataloader(self) -> Iterable[LabeledTileSampleBatch]: return DataLoader( self.val, batch_size=self.batch_size, @@ -84,7 +84,7 @@ def val_dataloader(self) -> Iterable[LabeledSampleBatch]: persistent_workers=self.num_workers > 0, ) - def test_dataloader(self) -> list[Iterable[LabeledSampleBatch]]: + def test_dataloader(self) -> list[Iterable[LabeledTileSampleBatch]]: return [ DataLoader( dataset, batch_size=self.batch_size, num_workers=self.num_workers @@ -92,7 +92,7 @@ def test_dataloader(self) -> list[Iterable[LabeledSampleBatch]]: for dataset in self.test.datasets ] - def predict_dataloader(self) -> list[Iterable[LabeledSampleBatch]]: + def predict_dataloader(self) -> list[Iterable[LabeledTileSampleBatch]]: return [ DataLoader( dataset, batch_size=self.batch_size, num_workers=self.num_workers diff --git a/prostate_cancer/datamodule/datasets/embedding_dataset.py b/prostate_cancer/datamodule/datasets/embedding_dataset.py index 19bf39b3..980268d7 100644 --- a/prostate_cancer/datamodule/datasets/embedding_dataset.py +++ b/prostate_cancer/datamodule/datasets/embedding_dataset.py @@ -12,10 +12,10 @@ FilterableDataset, get_slide_name, ) -from prostate_cancer.typing import LabeledSample, Metadata, UnlabeledSample +from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample -SlideMetadata: TypeAlias = pd.Series +SlideDfMetadata: TypeAlias = pd.Series T = TypeVar("T", covariant=True) @@ -74,7 +74,7 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: return datasets def _filter_tiles_embeddings_by_slide( - self, slide: SlideMetadata + self, slide: SlideDfMetadata ) -> tuple[pd.DataFrame, torch.Tensor]: slide_tiles = ( self.tiles[self.tiles["slide_id"] == slide["id"]] @@ -102,15 +102,13 @@ def _filter_tiles_embeddings_by_slide( return slide_tiles, slide_embeddings -class LabeledEmbeddingsDataset(EmbeddingsDataset[LabeledSample]): ... +class LabeledEmbeddingsDataset(EmbeddingsDataset[LabeledTileSample]): ... -class UnlabeledEmbeddingsDataset(EmbeddingsDataset[UnlabeledSample]): ... +class UnlabeledEmbeddingsDataset(EmbeddingsDataset[UnlabeledTileSample]): ... -class _TileEmbeddingsSlide(Dataset[LabeledSample | UnlabeledSample]): - """This dataset class provides gigapath features for given slide (and optionally includes label).""" - +class _TileEmbeddingsSlide(Dataset[LabeledTileSample | UnlabeledTileSample]): def __init__( self, slide_metadata: pd.Series, @@ -123,15 +121,15 @@ def __init__( self.slide_metadata = slide_metadata self.tiles = tiles self.embeddings = embeddings + assert len(self.tiles) == len(self.embeddings), "Tiles and embeddings not aligned" def __len__(self) -> int: - assert len(self.tiles) == len(self.embeddings) return len(self.tiles) - def __getitem__(self, idx: int) -> LabeledSample | UnlabeledSample: + def __getitem__(self, idx: int) -> LabeledTileSample | UnlabeledTileSample: vector = self.embeddings[idx] tile = self.tiles.iloc[idx] - metadata = Metadata( + metadata = TileMetadata( slide=get_slide_name(self.slide_metadata), x=tile["x"], y=tile["y"] ) diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_dataset.py index a995af18..d77fe4be 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_dataset.py @@ -13,7 +13,7 @@ FilterableDataset, get_slide_name, ) -from prostate_cancer.typing import LabeledSample, Metadata, UnlabeledSample +from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample T = TypeVar("T", covariant=True) @@ -59,13 +59,13 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: ) -class LabeledTilesDataset(TilesDataset[LabeledSample]): ... +class LabeledTilesDataset(TilesDataset[LabeledTileSample]): ... -class UnlabeledTilesDataset(TilesDataset[UnlabeledSample]): ... +class UnlabeledTilesDataset(TilesDataset[UnlabeledTileSample]): ... -class SlideTiles(Dataset[LabeledSample | UnlabeledSample]): +class SlideTiles(Dataset[LabeledTileSample | UnlabeledTileSample]): def __init__( self, slide_metadata: pd.Series, @@ -96,9 +96,9 @@ def __init__( def __len__(self) -> int: return len(self.slide_tiles) - def __getitem__(self, idx: int) -> LabeledSample | UnlabeledSample: + def __getitem__(self, idx: int) -> LabeledTileSample | UnlabeledTileSample: image = self.slide_tiles[idx] - metadata = Metadata( + metadata = TileMetadata( slide=self.slide_tiles.slide_path.stem, x=self.slide_tiles.tiles.iloc[idx]["x"], y=self.slide_tiles.tiles.iloc[idx]["y"], diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 380f9887..f6efdcf0 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -1,22 +1,42 @@ from typing import TypeAlias, TypedDict +from pathlib import Path from torch import Tensor -class Metadata(TypedDict): +class TileMetadata(TypedDict): slide: str x: int y: int -class MetadataBatch(TypedDict): +class TileMetadataBatch(TypedDict): slide: list[str] x: Tensor y: Tensor -LabeledSample = tuple[Tensor, Tensor, Metadata] -UnlabeledSample = tuple[Tensor, Metadata] +LabeledTileSample: TypeAlias = tuple[Tensor, Tensor, TileMetadata] +UnlabeledTileSample: TypeAlias = tuple[Tensor, TileMetadata] -LabeledSampleBatch: TypeAlias = tuple[Tensor, Tensor, MetadataBatch] -UnlabeledSampleBatch: TypeAlias = tuple[Tensor, MetadataBatch] +LabeledTileSampleBatch: TypeAlias = tuple[Tensor, Tensor, TileMetadataBatch] +UnlabeledTileSampleBatch: TypeAlias = tuple[Tensor, TileMetadataBatch] + + +class SlideMetadata(TypedDict): + slide_id: str + slide_name: str + slide_path: Path + + +class SlideMetadataBatch(TypedDict): + slide_id: list[str] + slide_name: list[str] + slide_path: list[Path] + + +LabeledSlideSample = tuple[Tensor, Tensor, TileMetadata] +UnlabeledSlideSample = tuple[Tensor, TileMetadata] + +LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, TileMetadataBatch] +UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, TileMetadataBatch] From c927cf83b9d3827757e09b3f2be2bd8e43d48b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 09:08:16 +0000 Subject: [PATCH 035/284] feat: mil forward --- prostate_cancer/attention_mil_model.py | 54 +- .../callbacks/aggregator_callback.py | 2 +- prostate_cancer/callbacks/cam_callback.py | 4 +- .../carcinoma_prediction_table_callback.py | 2 +- .../callbacks/estimation_callback.py | 2 +- prostate_cancer/callbacks/heatmap_callback.py | 2 +- .../callbacks/num_positive_callback.py | 2 +- prostate_cancer/datamodule/data_module.py | 15 +- .../datamodule/datasets/embedding_dataset.py | 4 +- prostate_cancer/typing.py | 2 +- uv.lock | 1136 +++++++++-------- 11 files changed, 656 insertions(+), 569 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 4b6a64c6..c50131aa 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -1,3 +1,5 @@ +"""Original was created by Adam Kukučka in Ulcerative Colitis project.""" + from copy import deepcopy import torch @@ -5,9 +7,14 @@ from torch import Tensor, nn from torch.optim.adamw import AdamW from torch.optim.optimizer import Optimizer -from torchmetrics import MetricCollection, Metric +from torchmetrics import Metric, MetricCollection from torchmetrics.classification import ( - AUROC, Recall, Precision, Accuracy, NegativePredictiveValue, Specificity + AUROC, + Accuracy, + NegativePredictiveValue, + Precision, + Recall, + Specificity, ) from prostate_cancer.typing import LabeledSlideSampleBatch, UnlabeledSlideSampleBatch @@ -41,7 +48,7 @@ def __init__(self, foundation: str, lr: float) -> None: "precision": Precision("binary"), "recall": Recall("binary"), "specificity": Specificity("binary"), - "negative_predictive_value": NegativePredictiveValue("binary") + "negative_predictive_value": NegativePredictiveValue("binary"), } self.train_metrics = MetricCollection(deepcopy(metrics), prefix="train/") @@ -50,19 +57,40 @@ def __init__(self, foundation: str, lr: float) -> None: def forward(self, x: Tensor) -> Tensor: # x has shape (batch_size, num_tiles_padded, embedding_dim) - x = self.encoder(x) - attn = self.attention(x) - attention_weights = torch.softmax(attn.sigmoid(), dim=0) - mask = (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() - attention_weights = attention_weights * mask + + # Just identity + x = self.encoder(x) # (batch_size, num_tiles_padded, embedding_dim) + + # TL weights (which tiles to attend to) + raw_attn = self.attention(x) # (batch_size, num_tiles_padded, 1) + + # sigmoid is applied to avoid unimodal spiky distribution + attention_weights = torch.softmax( + raw_attn.sigmoid(), dim=1 + ) # (batch_size, num_tiles_padded, 1) + + # Do not attend to padded tiles + mask = ( + (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() + ) # (batch_size, num_tiles_padded, 1) + + attention_weights = ( + attention_weights * mask + ) # (batch_size, num_tiles_padded, 1) + + # proper distribution for the non-padded tiles attention_weights = attention_weights / attention_weights.sum( dim=1, keepdim=True - ) - x = self.classifier(x) - x = torch.sum(attention_weights * x, dim=1) - x = x.sigmoid() + ) # (batch_size, num_tiles_padded, 1) + + # TL predictions + x = self.classifier(x) # (batch_size, num_tiles_padded, 1) + + # weight TL predictions with attention + x = torch.sum(attention_weights * x, dim=1) # (batch_size, 1) + x = x.sigmoid() # (batch_size, 1) - return x.squeeze(-1) + return x.squeeze(-1) # (batch_size,) def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: bags, labels, _ = batch diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index 99851b26..902215e4 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -28,7 +28,7 @@ def on_predict_dataloader_start( raise ValueError("Trainer should have datamodule attribute") # aggregator cannot be reset, thus, its original state is copied for each slide self.aggregator = deepcopy(self.aggregator_original) - datamodule = cast("DataModule", trainer.datamodule) + datamodule = cast("TileDataModule", trainer.datamodule) self.slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) def on_predict_batch_end( diff --git a/prostate_cancer/callbacks/cam_callback.py b/prostate_cancer/callbacks/cam_callback.py index 9940fbea..c6bae6a6 100644 --- a/prostate_cancer/callbacks/cam_callback.py +++ b/prostate_cancer/callbacks/cam_callback.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: - from prostate_cancer.datamodule.data_module import TileDataModule + from prostate_cancer.datamodule import TileDataModule from prostate_cancer.modeling.decode_head import BinaryClassifier @@ -42,7 +42,7 @@ def on_test_dataloader_start( if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") - datamodule = cast("DataModule", trainer.datamodule) + datamodule = cast("TileDataModule", trainer.datamodule) slide = cast("pd.Series", datamodule.test.slides.iloc[dataloader_idx]) self.save_dir = "cam_explanations" diff --git a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py index c0ef637c..ce674d73 100644 --- a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py +++ b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py @@ -27,7 +27,7 @@ def on_predict_dataloader_start( if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") - datamodule = cast("DataModule", trainer.datamodule) + datamodule = cast("TileDataModule", trainer.datamodule) self.slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) self.table: list[dict[str, Any]] = [] diff --git a/prostate_cancer/callbacks/estimation_callback.py b/prostate_cancer/callbacks/estimation_callback.py index bf815c1e..824d2c76 100644 --- a/prostate_cancer/callbacks/estimation_callback.py +++ b/prostate_cancer/callbacks/estimation_callback.py @@ -47,7 +47,7 @@ def on_predict_dataloader_start( ) ) - datamodule = cast("DataModule", trainer.datamodule) + datamodule = cast("TileDataModule", trainer.datamodule) self.slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) def on_predict_batch_end( diff --git a/prostate_cancer/callbacks/heatmap_callback.py b/prostate_cancer/callbacks/heatmap_callback.py index 9b176e4c..e27eebf9 100644 --- a/prostate_cancer/callbacks/heatmap_callback.py +++ b/prostate_cancer/callbacks/heatmap_callback.py @@ -29,7 +29,7 @@ def _on_dataloader_start( if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") - datamodule = cast("DataModule", trainer.datamodule) + datamodule = cast("TileDataModule", trainer.datamodule) slide = cast("pd.Series", getattr(datamodule, mode).slides.iloc[dataloader_idx]) # Create temporary directory diff --git a/prostate_cancer/callbacks/num_positive_callback.py b/prostate_cancer/callbacks/num_positive_callback.py index 5085ed30..305de94c 100644 --- a/prostate_cancer/callbacks/num_positive_callback.py +++ b/prostate_cancer/callbacks/num_positive_callback.py @@ -40,7 +40,7 @@ def on_predict_dataloader_end( if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") - datamodule = cast("DataModule", trainer.datamodule) + datamodule = cast("TileDataModule", trainer.datamodule) slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) table = {"slide": get_slide_name(slide), "num_positive": self.num_positive} diff --git a/prostate_cancer/datamodule/data_module.py b/prostate_cancer/datamodule/data_module.py index 57f23d2f..f5cc1801 100644 --- a/prostate_cancer/datamodule/data_module.py +++ b/prostate_cancer/datamodule/data_module.py @@ -7,7 +7,11 @@ from rationai.mlkit.data.datasets import MetaTiledSlides from torch.utils.data import DataLoader -from prostate_cancer.typing import LabeledTileSample, LabeledTileSampleBatch, UnlabeledTileSample +from prostate_cancer.typing import ( + LabeledTileSample, + LabeledTileSampleBatch, + UnlabeledTileSample, +) PartialConf: TypeAlias = DictConfig @@ -35,15 +39,18 @@ def setup(self, stage: str) -> None: instantiate(self.datasets["train"]), ) self.val = cast( - "MetaTiledSlides[LabeledTileSample]", instantiate(self.datasets["val"]) + "MetaTiledSlides[LabeledTileSample]", + instantiate(self.datasets["val"]), ) case "val": self.val = cast( - "MetaTiledSlides[LabeledTileSample]", instantiate(self.datasets["val"]) + "MetaTiledSlides[LabeledTileSample]", + instantiate(self.datasets["val"]), ) case "test": self.test = cast( - "MetaTiledSlides[LabeledTileSample]", instantiate(self.datasets["test"]) + "MetaTiledSlides[LabeledTileSample]", + instantiate(self.datasets["test"]), ) case "predict": self.predict = cast( diff --git a/prostate_cancer/datamodule/datasets/embedding_dataset.py b/prostate_cancer/datamodule/datasets/embedding_dataset.py index 980268d7..73ee6f34 100644 --- a/prostate_cancer/datamodule/datasets/embedding_dataset.py +++ b/prostate_cancer/datamodule/datasets/embedding_dataset.py @@ -121,7 +121,9 @@ def __init__( self.slide_metadata = slide_metadata self.tiles = tiles self.embeddings = embeddings - assert len(self.tiles) == len(self.embeddings), "Tiles and embeddings not aligned" + assert len(self.tiles) == len(self.embeddings), ( + "Tiles and embeddings not aligned" + ) def __len__(self) -> int: return len(self.tiles) diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index f6efdcf0..67f2609b 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -1,5 +1,5 @@ -from typing import TypeAlias, TypedDict from pathlib import Path +from typing import TypeAlias, TypedDict from torch import Tensor diff --git a/uv.lock b/uv.lock index b4a30e19..9e7b591d 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.3" +version = "3.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -28,42 +28,42 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, ] [[package]] @@ -151,24 +151,24 @@ sdist = { url = "https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d [[package]] name = "anyio" -version = "4.12.1" +version = "4.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, ] [[package]] name = "attrs" -version = "25.4.0" +version = "26.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] [[package]] @@ -191,11 +191,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.4.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, ] [[package]] @@ -244,55 +244,55 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/b6/9ee9c1a608916ca5feae81a344dffbaa53b26b90be58cc2159e3332d44ec/charset_normalizer-3.4.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed97c282ee4f994ef814042423a529df9497e3c666dca19be1d4cd1129dc7ade", size = 280976, upload-time = "2026-03-06T06:01:15.276Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d8/a54f7c0b96f1df3563e9190f04daf981e365a9b397eedfdfb5dbef7e5c6c/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0294916d6ccf2d069727d65973c3a1ca477d68708db25fd758dd28b0827cff54", size = 189356, upload-time = "2026-03-06T06:01:16.511Z" }, - { url = "https://files.pythonhosted.org/packages/42/69/2bf7f76ce1446759a5787cb87d38f6a61eb47dbbdf035cfebf6347292a65/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc57a0baa3eeedd99fafaef7511b5a6ef4581494e8168ee086031744e2679467", size = 206369, upload-time = "2026-03-06T06:01:17.853Z" }, - { url = "https://files.pythonhosted.org/packages/10/9c/949d1a46dab56b959d9a87272482195f1840b515a3380e39986989a893ae/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ed1a9a204f317ef879b32f9af507d47e49cd5e7f8e8d5d96358c98373314fc60", size = 203285, upload-time = "2026-03-06T06:01:19.473Z" }, - { url = "https://files.pythonhosted.org/packages/67/5c/ae30362a88b4da237d71ea214a8c7eb915db3eec941adda511729ac25fa2/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad83b8f9379176c841f8865884f3514d905bcd2a9a3b210eaa446e7d2223e4d", size = 196274, upload-time = "2026-03-06T06:01:20.728Z" }, - { url = "https://files.pythonhosted.org/packages/b2/07/c9f2cb0e46cb6d64fdcc4f95953747b843bb2181bda678dc4e699b8f0f9a/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:a118e2e0b5ae6b0120d5efa5f866e58f2bb826067a646431da4d6a2bdae7950e", size = 184715, upload-time = "2026-03-06T06:01:22.194Z" }, - { url = "https://files.pythonhosted.org/packages/36/64/6b0ca95c44fddf692cd06d642b28f63009d0ce325fad6e9b2b4d0ef86a52/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:754f96058e61a5e22e91483f823e07df16416ce76afa4ebf306f8e1d1296d43f", size = 193426, upload-time = "2026-03-06T06:01:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/50/bc/a730690d726403743795ca3f5bb2baf67838c5fea78236098f324b965e40/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0c300cefd9b0970381a46394902cd18eaf2aa00163f999590ace991989dcd0fc", size = 191780, upload-time = "2026-03-06T06:01:25.053Z" }, - { url = "https://files.pythonhosted.org/packages/97/4f/6c0bc9af68222b22951552d73df4532b5be6447cee32d58e7e8c74ecbb7b/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c108f8619e504140569ee7de3f97d234f0fbae338a7f9f360455071ef9855a95", size = 185805, upload-time = "2026-03-06T06:01:26.294Z" }, - { url = "https://files.pythonhosted.org/packages/dd/b9/a523fb9b0ee90814b503452b2600e4cbc118cd68714d57041564886e7325/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d1028de43596a315e2720a9849ee79007ab742c06ad8b45a50db8cdb7ed4a82a", size = 208342, upload-time = "2026-03-06T06:01:27.55Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/c59e761dee4464050713e50e27b58266cc8e209e518c0b378c1580c959ba/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:19092dde50335accf365cce21998a1c6dd8eafd42c7b226eb54b2747cdce2fac", size = 193661, upload-time = "2026-03-06T06:01:29.051Z" }, - { url = "https://files.pythonhosted.org/packages/1c/43/729fa30aad69783f755c5ad8649da17ee095311ca42024742701e202dc59/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4354e401eb6dab9aed3c7b4030514328a6c748d05e1c3e19175008ca7de84fb1", size = 204819, upload-time = "2026-03-06T06:01:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/87/33/d9b442ce5a91b96fc0840455a9e49a611bbadae6122778d0a6a79683dd31/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a68766a3c58fde7f9aaa22b3786276f62ab2f594efb02d0a1421b6282e852e98", size = 198080, upload-time = "2026-03-06T06:01:31.478Z" }, - { url = "https://files.pythonhosted.org/packages/56/5a/b8b5a23134978ee9885cee2d6995f4c27cc41f9baded0a9685eabc5338f0/charset_normalizer-3.4.5-cp312-cp312-win32.whl", hash = "sha256:1827734a5b308b65ac54e86a618de66f935a4f63a8a462ff1e19a6788d6c2262", size = 132630, upload-time = "2026-03-06T06:01:33.056Z" }, - { url = "https://files.pythonhosted.org/packages/70/53/e44a4c07e8904500aec95865dc3f6464dc3586a039ef0df606eb3ac38e35/charset_normalizer-3.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:728c6a963dfab66ef865f49286e45239384249672cd598576765acc2a640a636", size = 142856, upload-time = "2026-03-06T06:01:34.489Z" }, - { url = "https://files.pythonhosted.org/packages/ea/aa/c5628f7cad591b1cf45790b7a61483c3e36cf41349c98af7813c483fd6e8/charset_normalizer-3.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:75dfd1afe0b1647449e852f4fb428195a7ed0588947218f7ba929f6538487f02", size = 132982, upload-time = "2026-03-06T06:01:35.641Z" }, - { url = "https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac59c15e3f1465f722607800c68713f9fbc2f672b9eb649fe831da4019ae9b23", size = 280788, upload-time = "2026-03-06T06:01:37.126Z" }, - { url = "https://files.pythonhosted.org/packages/0e/09/6003e7ffeb90cc0560da893e3208396a44c210c5ee42efff539639def59b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:165c7b21d19365464e8f70e5ce5e12524c58b48c78c1f5a57524603c1ab003f8", size = 188890, upload-time = "2026-03-06T06:01:38.73Z" }, - { url = "https://files.pythonhosted.org/packages/42/1e/02706edf19e390680daa694d17e2b8eab4b5f7ac285e2a51168b4b22ee6b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:28269983f25a4da0425743d0d257a2d6921ea7d9b83599d4039486ec5b9f911d", size = 206136, upload-time = "2026-03-06T06:01:40.016Z" }, - { url = "https://files.pythonhosted.org/packages/c7/87/942c3def1b37baf3cf786bad01249190f3ca3d5e63a84f831e704977de1f/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d27ce22ec453564770d29d03a9506d449efbb9fa13c00842262b2f6801c48cce", size = 202551, upload-time = "2026-03-06T06:01:41.522Z" }, - { url = "https://files.pythonhosted.org/packages/94/0a/af49691938dfe175d71b8a929bd7e4ace2809c0c5134e28bc535660d5262/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0625665e4ebdddb553ab185de5db7054393af8879fb0c87bd5690d14379d6819", size = 195572, upload-time = "2026-03-06T06:01:43.208Z" }, - { url = "https://files.pythonhosted.org/packages/20/ea/dfb1792a8050a8e694cfbde1570ff97ff74e48afd874152d38163d1df9ae/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:c23eb3263356d94858655b3e63f85ac5d50970c6e8febcdde7830209139cc37d", size = 184438, upload-time = "2026-03-06T06:01:44.755Z" }, - { url = "https://files.pythonhosted.org/packages/72/12/c281e2067466e3ddd0595bfaea58a6946765ace5c72dfa3edc2f5f118026/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e6302ca4ae283deb0af68d2fbf467474b8b6aedcd3dab4db187e07f94c109763", size = 193035, upload-time = "2026-03-06T06:01:46.051Z" }, - { url = "https://files.pythonhosted.org/packages/ba/4f/3792c056e7708e10464bad0438a44708886fb8f92e3c3d29ec5e2d964d42/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e51ae7d81c825761d941962450f50d041db028b7278e7b08930b4541b3e45cb9", size = 191340, upload-time = "2026-03-06T06:01:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/e7/86/80ddba897127b5c7a9bccc481b0cd36c8fefa485d113262f0fe4332f0bf4/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:597d10dec876923e5c59e48dbd366e852eacb2b806029491d307daea6b917d7c", size = 185464, upload-time = "2026-03-06T06:01:48.764Z" }, - { url = "https://files.pythonhosted.org/packages/4d/00/b5eff85ba198faacab83e0e4b6f0648155f072278e3b392a82478f8b988b/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5cffde4032a197bd3b42fd0b9509ec60fb70918d6970e4cc773f20fc9180ca67", size = 208014, upload-time = "2026-03-06T06:01:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/c8/11/d36f70be01597fd30850dde8a1269ebc8efadd23ba5785808454f2389bde/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2da4eedcb6338e2321e831a0165759c0c620e37f8cd044a263ff67493be8ffb3", size = 193297, upload-time = "2026-03-06T06:01:51.933Z" }, - { url = "https://files.pythonhosted.org/packages/1a/1d/259eb0a53d4910536c7c2abb9cb25f4153548efb42800c6a9456764649c0/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:65a126fb4b070d05340a84fc709dd9e7c75d9b063b610ece8a60197a291d0adf", size = 204321, upload-time = "2026-03-06T06:01:53.887Z" }, - { url = "https://files.pythonhosted.org/packages/84/31/faa6c5b9d3688715e1ed1bb9d124c384fe2fc1633a409e503ffe1c6398c1/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7a80a9242963416bd81f99349d5f3fce1843c303bd404f204918b6d75a75fd6", size = 197509, upload-time = "2026-03-06T06:01:56.439Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a5/c7d9dd1503ffc08950b3260f5d39ec2366dd08254f0900ecbcf3a6197c7c/charset_normalizer-3.4.5-cp313-cp313-win32.whl", hash = "sha256:f1d725b754e967e648046f00c4facc42d414840f5ccc670c5670f59f83693e4f", size = 132284, upload-time = "2026-03-06T06:01:57.812Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0f/57072b253af40c8aa6636e6de7d75985624c1eb392815b2f934199340a89/charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:e37bd100d2c5d3ba35db9c7c5ba5a9228cbcffe5c4778dc824b164e5257813d7", size = 142630, upload-time = "2026-03-06T06:01:59.062Z" }, - { url = "https://files.pythonhosted.org/packages/31/41/1c4b7cc9f13bd9d369ce3bc993e13d374ce25fa38a2663644283ecf422c1/charset_normalizer-3.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:93b3b2cc5cf1b8743660ce77a4f45f3f6d1172068207c1defc779a36eea6bb36", size = 133254, upload-time = "2026-03-06T06:02:00.281Z" }, - { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" }, +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] [[package]] name = "click" -version = "8.3.1" +version = "8.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] [[package]] @@ -359,62 +359,106 @@ wheels = [ [[package]] name = "cryptography" -version = "46.0.5" +version = "46.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, + { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, + { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, + { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, ] [[package]] name = "cuda-bindings" -version = "12.9.4" +version = "13.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cuda-pathfinder", marker = "sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, ] [[package]] name = "cuda-pathfinder" -version = "1.4.2" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/d6/ac63065d33dd700fee7ebd7d287332401b54e31b9346e142f871e1f0b116/cuda_pathfinder-1.5.3-py3-none-any.whl", hash = "sha256:dff021123aedbb4117cc7ec81717bbfe198fb4e8b5f1ee57e0e084fec5c8577d", size = 49991, upload-time = "2026-04-14T20:09:27.037Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/de/8ca2b613042550dcf9ef50c596c8b1f602afda92cf9032ac28a73f6ee410/cuda_pathfinder-1.4.2-py3-none-any.whl", hash = "sha256:eb354abc20278f8609dc5b666a24648655bef5613c6dfe78a238a6fd95566754", size = 44779, upload-time = "2026-03-10T21:57:30.974Z" }, + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" }, ] [[package]] @@ -428,16 +472,16 @@ wheels = [ [[package]] name = "databricks-sdk" -version = "0.99.0" +version = "0.105.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/28/d53ce4c21ab197cacdeb65bcdcba3f8098a1b6b88052b72b60348f6b8091/databricks_sdk-0.99.0.tar.gz", hash = "sha256:13ae35b064277074a79fcd5265260e92b352b5eb4e950438dcee895951bd86fd", size = 878669, upload-time = "2026-03-12T08:16:12.597Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/4a/14c0608545b9a0a1103d6a964b226241c336ff8aa99d4226f9741af60f17/databricks_sdk-0.105.0.tar.gz", hash = "sha256:c52bf21c5e5814bc3d741a98fc772f4b6f1f9cfa08e90bf7580aa1592bc92e92", size = 917927, upload-time = "2026-04-23T08:53:44.249Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/3a/8593cae357aac76142da1940ad7a965ea50f2767bb958820839be675811b/databricks_sdk-0.99.0-py3-none-any.whl", hash = "sha256:c0a6740bf21d430daa85461e193197ce1d0ac12209ce20e3cb9b67b32fe2bcfd", size = 829540, upload-time = "2026-03-12T08:16:11.155Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/27651e85dab9897c65c4acf7abcd924e85f8726d282804fc7686f1387520/databricks_sdk-0.105.0-py3-none-any.whl", hash = "sha256:f1624e434588e2174052bc3646670f17581f3bb269b9438f9121e9df4a59eebd", size = 866235, upload-time = "2026-04-23T08:53:42.01Z" }, ] [[package]] @@ -486,7 +530,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.135.1" +version = "0.136.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -495,18 +539,18 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/45/c130091c2dfa061bbfe3150f2a5091ef1adf149f2a8d2ae769ecaf6e99a2/fastapi-0.136.1.tar.gz", hash = "sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f", size = 397448, upload-time = "2026-04-23T16:49:44.046Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ff/2e4eca3ade2c22fe1dea7043b8ee9dabe47753349eb1b56a202de8af6349/fastapi-0.136.1-py3-none-any.whl", hash = "sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f", size = 117683, upload-time = "2026-04-23T16:49:42.437Z" }, ] [[package]] name = "filelock" -version = "3.25.2" +version = "3.29.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, ] [[package]] @@ -610,11 +654,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2026.2.0" +version = "2026.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" }, ] [package.optional-dependencies] @@ -636,27 +680,27 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.46" +version = "3.1.47" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/bd/50db468e9b1310529a19fce651b3b0e753b5c07954d486cba31bbee9a5d5/gitpython-3.1.47.tar.gz", hash = "sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd", size = 216978, upload-time = "2026-04-22T02:44:44.059Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c5/a1bc0996af85757903cf2bf444a7824e68e0035ce63fb41d6f76f9def68b/gitpython-3.1.47-py3-none-any.whl", hash = "sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905", size = 209547, upload-time = "2026-04-22T02:44:41.271Z" }, ] [[package]] name = "google-auth" -version = "2.49.1" +version = "2.49.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/80/6a696a07d3d3b0a92488933532f03dbefa4a24ab80fb231395b9a2a1be77/google_auth-2.49.1.tar.gz", hash = "sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64", size = 333825, upload-time = "2026-03-12T19:30:58.135Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/fc/e925290a1ad95c975c459e2df070fac2b90954e13a0370ac505dff78cb99/google_auth-2.49.2.tar.gz", hash = "sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409", size = 333958, upload-time = "2026-04-10T00:41:21.888Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/eb/c6c2478d8a8d633460be40e2a8a6f8f429171997a35a96f81d3b680dec83/google_auth-2.49.1-py3-none-any.whl", hash = "sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7", size = 240737, upload-time = "2026-03-12T19:30:53.159Z" }, + { url = "https://files.pythonhosted.org/packages/73/76/d241a5c927433420507215df6cac1b1fa4ac0ba7a794df42a84326c68da8/google_auth-2.49.2-py3-none-any.whl", hash = "sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5", size = 240638, upload-time = "2026-04-10T00:41:14.501Z" }, ] [[package]] @@ -697,28 +741,30 @@ wheels = [ [[package]] name = "greenlet" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267, upload-time = "2026-02-20T20:54:15.531Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, - { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, - { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, - { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, - { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, - { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/cc802e067d02af8b60b6771cea7d57e21ef5e6659912814babb42b864713/greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f", size = 231081, upload-time = "2026-02-20T20:17:28.121Z" }, - { url = "https://files.pythonhosted.org/packages/58/2e/fe7f36ff1982d6b10a60d5e0740c759259a7d6d2e1dc41da6d96de32fff6/greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643", size = 230331, upload-time = "2026-02-20T20:17:23.34Z" }, - { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, - { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, - { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, - { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, - { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, - { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, - { url = "https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124", size = 230961, upload-time = "2026-02-20T20:16:58.461Z" }, - { url = "https://files.pythonhosted.org/packages/62/6b/a89f8456dcb06becff288f563618e9f20deed8dd29beea14f9a168aef64b/greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327", size = 230221, upload-time = "2026-02-20T20:17:37.152Z" }, +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/94/a5935717b307d7c71fe877b52b884c6af707d2d2090db118a03fbd799369/greenlet-3.4.0.tar.gz", hash = "sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff", size = 195913, upload-time = "2026-04-08T17:08:00.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/8b/3669ad3b3f247a791b2b4aceb3aa5a31f5f6817bf547e4e1ff712338145a/greenlet-3.4.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a", size = 286902, upload-time = "2026-04-08T15:52:12.138Z" }, + { url = "https://files.pythonhosted.org/packages/38/3e/3c0e19b82900873e2d8469b590a6c4b3dfd2b316d0591f1c26b38a4879a5/greenlet-3.4.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97", size = 606099, upload-time = "2026-04-08T16:24:38.408Z" }, + { url = "https://files.pythonhosted.org/packages/b5/33/99fef65e7754fc76a4ed14794074c38c9ed3394a5bd129d7f61b705f3168/greenlet-3.4.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996", size = 618837, upload-time = "2026-04-08T16:30:58.298Z" }, + { url = "https://files.pythonhosted.org/packages/44/57/eae2cac10421feae6c0987e3dc106c6d86262b1cb379e171b017aba893a6/greenlet-3.4.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d", size = 624901, upload-time = "2026-04-08T16:40:38.981Z" }, + { url = "https://files.pythonhosted.org/packages/36/f7/229f3aed6948faa20e0616a0b8568da22e365ede6a54d7d369058b128afd/greenlet-3.4.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc", size = 615062, upload-time = "2026-04-08T15:56:33.766Z" }, + { url = "https://files.pythonhosted.org/packages/6a/8a/0e73c9b94f31d1cc257fe79a0eff621674141cdae7d6d00f40de378a1e42/greenlet-3.4.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077", size = 423927, upload-time = "2026-04-08T16:43:05.293Z" }, + { url = "https://files.pythonhosted.org/packages/08/97/d988180011aa40135c46cd0d0cf01dd97f7162bae14139b4a3ef54889ba5/greenlet-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de", size = 1573511, upload-time = "2026-04-08T16:26:20.058Z" }, + { url = "https://files.pythonhosted.org/packages/d4/0f/a5a26fe152fb3d12e6a474181f6e9848283504d0afd095f353d85726374b/greenlet-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08", size = 1640396, upload-time = "2026-04-08T15:57:30.88Z" }, + { url = "https://files.pythonhosted.org/packages/42/cf/bb2c32d9a100e36ee9f6e38fad6b1e082b8184010cb06259b49e1266ca01/greenlet-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2", size = 238892, upload-time = "2026-04-08T17:03:10.094Z" }, + { url = "https://files.pythonhosted.org/packages/b7/47/6c41314bac56e71436ce551c7fbe3cc830ed857e6aa9708dbb9c65142eb6/greenlet-3.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e", size = 235599, upload-time = "2026-04-08T15:52:54.3Z" }, + { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, + { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, + { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, + { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, + { url = "https://files.pythonhosted.org/packages/9b/96/795619651d39c7fbd809a522f881aa6f0ead504cc8201c3a5b789dfaef99/greenlet-3.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a", size = 235498, upload-time = "2026-04-08T17:05:00.584Z" }, ] [[package]] @@ -744,26 +790,26 @@ wheels = [ [[package]] name = "hf-xet" -version = "1.4.2" +version = "1.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/08/23c84a26716382c89151b5b447b4beb19e3345f3a93d3b73009a71a57ad3/hf_xet-1.4.2.tar.gz", hash = "sha256:b7457b6b482d9e0743bd116363239b1fa904a5e65deede350fbc0c4ea67c71ea", size = 672357, upload-time = "2026-03-13T06:58:51.077Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/92/ec9ad04d0b5728dca387a45af7bc98fbb0d73b2118759f5f6038b61a57e8/hf_xet-1.4.3.tar.gz", hash = "sha256:8ddedb73c8c08928c793df2f3401ec26f95be7f7e516a7bee2fbb546f6676113", size = 670477, upload-time = "2026-03-31T22:40:07.874Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/06/e8cf74c3c48e5485c7acc5a990d0d8516cdfb5fdf80f799174f1287cc1b5/hf_xet-1.4.2-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ac8202ae1e664b2c15cdfc7298cbb25e80301ae596d602ef7870099a126fcad4", size = 3796125, upload-time = "2026-03-13T06:58:33.177Z" }, - { url = "https://files.pythonhosted.org/packages/66/d4/b73ebab01cbf60777323b7de9ef05550790451eb5172a220d6b9845385ec/hf_xet-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6d2f8ee39fa9fba9af929f8c0d0482f8ee6e209179ad14a909b6ad78ffcb7c81", size = 3555985, upload-time = "2026-03-13T06:58:31.797Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e7/ded6d1bd041c3f2bca9e913a0091adfe32371988e047dd3a68a2463c15a2/hf_xet-1.4.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4642a6cf249c09da8c1f87fe50b24b2a3450b235bf8adb55700b52f0ea6e2eb6", size = 4212085, upload-time = "2026-03-13T06:58:24.323Z" }, - { url = "https://files.pythonhosted.org/packages/97/c1/a0a44d1f98934f7bdf17f7a915b934f9fca44bb826628c553589900f6df8/hf_xet-1.4.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:769431385e746c92dc05492dde6f687d304584b89c33d79def8367ace06cb555", size = 3988266, upload-time = "2026-03-13T06:58:22.887Z" }, - { url = "https://files.pythonhosted.org/packages/7a/82/be713b439060e7d1f1d93543c8053d4ef2fe7e6922c5b31642eaa26f3c4b/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c9dd1c1bc4cc56168f81939b0e05b4c36dd2d28c13dc1364b17af89aa0082496", size = 4188513, upload-time = "2026-03-13T06:58:40.858Z" }, - { url = "https://files.pythonhosted.org/packages/21/a6/cbd4188b22abd80ebd0edbb2b3e87f2633e958983519980815fb8314eae5/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fca58a2ae4e6f6755cc971ac6fcdf777ea9284d7e540e350bb000813b9a3008d", size = 4428287, upload-time = "2026-03-13T06:58:42.601Z" }, - { url = "https://files.pythonhosted.org/packages/b2/4e/84e45b25e2e3e903ed3db68d7eafa96dae9a1d1f6d0e7fc85120347a852f/hf_xet-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:163aab46854ccae0ab6a786f8edecbbfbaa38fcaa0184db6feceebf7000c93c0", size = 3665574, upload-time = "2026-03-13T06:58:53.881Z" }, - { url = "https://files.pythonhosted.org/packages/ee/71/c5ac2b9a7ae39c14e91973035286e73911c31980fe44e7b1d03730c00adc/hf_xet-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:09b138422ecbe50fd0c84d4da5ff537d27d487d3607183cd10e3e53f05188e82", size = 3528760, upload-time = "2026-03-13T06:58:52.187Z" }, - { url = "https://files.pythonhosted.org/packages/b4/86/b40b83a2ff03ef05c4478d2672b1fc2b9683ff870e2b25f4f3af240f2e7b/hf_xet-1.4.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:71f02d6e4cdd07f344f6844845d78518cc7186bd2bc52d37c3b73dc26a3b0bc5", size = 3800339, upload-time = "2026-03-13T06:58:36.245Z" }, - { url = "https://files.pythonhosted.org/packages/64/2e/af4475c32b4378b0e92a587adb1aa3ec53e3450fd3e5fe0372a874531c00/hf_xet-1.4.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e9b38d876e94d4bdcf650778d6ebbaa791dd28de08db9736c43faff06ede1b5a", size = 3559664, upload-time = "2026-03-13T06:58:34.787Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4c/781267da3188db679e601de18112021a5cb16506fe86b246e22c5401a9c4/hf_xet-1.4.2-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:77e8c180b7ef12d8a96739a4e1e558847002afe9ea63b6f6358b2271a8bdda1c", size = 4217422, upload-time = "2026-03-13T06:58:27.472Z" }, - { url = "https://files.pythonhosted.org/packages/68/47/d6cf4a39ecf6c7705f887a46f6ef5c8455b44ad9eb0d391aa7e8a2ff7fea/hf_xet-1.4.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3b3c6a882016b94b6c210957502ff7877802d0dbda8ad142c8595db8b944271", size = 3992847, upload-time = "2026-03-13T06:58:25.989Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ef/e80815061abff54697239803948abc665c6b1d237102c174f4f7a9a5ffc5/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d9a634cc929cfbaf2e1a50c0e532ae8c78fa98618426769480c58501e8c8ac2", size = 4193843, upload-time = "2026-03-13T06:58:44.59Z" }, - { url = "https://files.pythonhosted.org/packages/54/75/07f6aa680575d9646c4167db6407c41340cbe2357f5654c4e72a1b01ca14/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b0932eb8b10317ea78b7da6bab172b17be03bbcd7809383d8d5abd6a2233e04", size = 4432751, upload-time = "2026-03-13T06:58:46.533Z" }, - { url = "https://files.pythonhosted.org/packages/cd/71/193eabd7e7d4b903c4aa983a215509c6114915a5a237525ec562baddb868/hf_xet-1.4.2-cp37-abi3-win_amd64.whl", hash = "sha256:ad185719fb2e8ac26f88c8100562dbf9dbdcc3d9d2add00faa94b5f106aea53f", size = 3671149, upload-time = "2026-03-13T06:58:57.07Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7e/ccf239da366b37ba7f0b36095450efae4a64980bdc7ec2f51354205fdf39/hf_xet-1.4.2-cp37-abi3-win_arm64.whl", hash = "sha256:32c012286b581f783653e718c1862aea5b9eb140631685bb0c5e7012c8719a87", size = 3533426, upload-time = "2026-03-13T06:58:55.46Z" }, + { url = "https://files.pythonhosted.org/packages/72/43/724d307b34e353da0abd476e02f72f735cdd2bc86082dee1b32ea0bfee1d/hf_xet-1.4.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7551659ba4f1e1074e9623996f28c3873682530aee0a846b7f2f066239228144", size = 3800935, upload-time = "2026-03-31T22:39:49.618Z" }, + { url = "https://files.pythonhosted.org/packages/2b/d2/8bee5996b699262edb87dbb54118d287c0e1b2fc78af7cdc41857ba5e3c4/hf_xet-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bee693ada985e7045997f05f081d0e12c4c08bd7626dc397f8a7c487e6c04f7f", size = 3558942, upload-time = "2026-03-31T22:39:47.938Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a1/e993d09cbe251196fb60812b09a58901c468127b7259d2bf0f68bf6088eb/hf_xet-1.4.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21644b404bb0100fe3857892f752c4d09642586fd988e61501c95bbf44b393a3", size = 4207657, upload-time = "2026-03-31T22:39:39.69Z" }, + { url = "https://files.pythonhosted.org/packages/64/44/9eb6d21e5c34c63e5e399803a6932fa983cabdf47c0ecbcfe7ea97684b8c/hf_xet-1.4.3-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:987f09cfe418237812896a6736b81b1af02a3a6dcb4b4944425c4c4fca7a7cf8", size = 3986765, upload-time = "2026-03-31T22:39:37.936Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7b/8ad6f16fdb82f5f7284a34b5ec48645bd575bdcd2f6f0d1644775909c486/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:60cf7fc43a99da0a853345cf86d23738c03983ee5249613a6305d3e57a5dca74", size = 4188162, upload-time = "2026-03-31T22:39:58.382Z" }, + { url = "https://files.pythonhosted.org/packages/1b/c4/39d6e136cbeea9ca5a23aad4b33024319222adbdc059ebcda5fc7d9d5ff4/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2815a49a7a59f3e2edf0cf113ae88e8cb2ca2a221bf353fb60c609584f4884d4", size = 4424525, upload-time = "2026-03-31T22:40:00.225Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/adc32dae6bdbc367853118b9878139ac869419a4ae7ba07185dc31251b76/hf_xet-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:42ee323265f1e6a81b0e11094564fb7f7e0ec75b5105ffd91ae63f403a11931b", size = 3671610, upload-time = "2026-03-31T22:40:10.42Z" }, + { url = "https://files.pythonhosted.org/packages/e2/19/25d897dcc3f81953e0c2cde9ec186c7a0fee413eb0c9a7a9130d87d94d3a/hf_xet-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:27c976ba60079fb8217f485b9c5c7fcd21c90b0367753805f87cb9f3cdc4418a", size = 3528529, upload-time = "2026-03-31T22:40:09.106Z" }, + { url = "https://files.pythonhosted.org/packages/ac/9f/9c23e4a447b8f83120798f9279d0297a4d1360bdbf59ef49ebec78fe2545/hf_xet-1.4.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d0da85329eaf196e03e90b84c2d0aca53bd4573d097a75f99609e80775f98025", size = 3805048, upload-time = "2026-03-31T22:39:53.105Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f8/7aacb8e5f4a7899d39c787b5984e912e6c18b11be136ef13947d7a66d265/hf_xet-1.4.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e23717ce4186b265f69afa66e6f0069fe7efbf331546f5c313d00e123dc84583", size = 3562178, upload-time = "2026-03-31T22:39:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/df/9a/a24b26dc8a65f0ecc0fe5be981a19e61e7ca963b85e062c083f3a9100529/hf_xet-1.4.3-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc360b70c815bf340ed56c7b8c63aacf11762a4b099b2fe2c9bd6d6068668c08", size = 4212320, upload-time = "2026-03-31T22:39:42.922Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/46d493db155d2ee2801b71fb1b0fd67696359047fdd8caee2c914cc50c79/hf_xet-1.4.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39f2d2e9654cd9b4319885733993807aab6de9dfbd34c42f0b78338d6617421f", size = 3991546, upload-time = "2026-03-31T22:39:41.335Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f5/067363e1c96c6b17256910830d1b54099d06287e10f4ec6ec4e7e08371fc/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:49ad8a8cead2b56051aa84d7fce3e1335efe68df3cf6c058f22a65513885baac", size = 4193200, upload-time = "2026-03-31T22:40:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/42/4b/53951592882d9c23080c7644542fda34a3813104e9e11fa1a7d82d419cb8/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7716d62015477a70ea272d2d68cd7cad140f61c52ee452e133e139abfe2c17ba", size = 4429392, upload-time = "2026-03-31T22:40:03.492Z" }, + { url = "https://files.pythonhosted.org/packages/8a/21/75a6c175b4e79662ad8e62f46a40ce341d8d6b206b06b4320d07d55b188c/hf_xet-1.4.3-cp37-abi3-win_amd64.whl", hash = "sha256:6b591fcad34e272a5b02607485e4f2a1334aebf1bc6d16ce8eb1eb8978ac2021", size = 3677359, upload-time = "2026-03-31T22:40:13.619Z" }, + { url = "https://files.pythonhosted.org/packages/8a/7c/44314ecd0e89f8b2b51c9d9e5e7a60a9c1c82024ac471d415860557d3cd8/hf_xet-1.4.3-cp37-abi3-win_arm64.whl", hash = "sha256:7c2c7e20bcfcc946dc67187c203463f5e932e395845d098cc2a93f5b67ca0b47", size = 3533664, upload-time = "2026-03-31T22:40:12.152Z" }, ] [[package]] @@ -796,7 +842,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.7.1" +version = "1.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -809,9 +855,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b4/a8/94ccc0aec97b996a3a68f3e1fa06a4bd7185dd02bf22bfba794a0ade8440/huggingface_hub-1.7.1.tar.gz", hash = "sha256:be38fe66e9b03c027ad755cb9e4b87ff0303c98acf515b5d579690beb0bf3048", size = 722097, upload-time = "2026-03-13T09:36:07.758Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/89/e7aa12d8a6b9259bed10671abb25ae6fa437c0f88a86ecbf59617bae7759/huggingface_hub-1.11.0.tar.gz", hash = "sha256:15fb3713c7f9cdff7b808a94fd91664f661ab142796bb48c9cd9493e8d166278", size = 761749, upload-time = "2026-04-16T13:07:39.73Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/75/ca21955d6117a394a482c7862ce96216239d0e3a53133ae8510727a8bcfa/huggingface_hub-1.7.1-py3-none-any.whl", hash = "sha256:38c6cce7419bbde8caac26a45ed22b0cea24152a8961565d70ec21f88752bfaa", size = 616308, upload-time = "2026-03-13T09:36:06.062Z" }, + { url = "https://files.pythonhosted.org/packages/37/02/4f3f8997d1ea7fe0146b343e5e14bd065fa87af790d07e5576d31b31cc18/huggingface_hub-1.11.0-py3-none-any.whl", hash = "sha256:42a6de0afbfeb5e022222d36398f029679db4eb4778801aafda32257ae9131ab", size = 645499, upload-time = "2026-04-16T13:07:37.716Z" }, ] [[package]] @@ -830,20 +876,20 @@ wheels = [ [[package]] name = "identify" -version = "2.6.18" +version = "2.6.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, ] [[package]] name = "idna" -version = "3.11" +version = "3.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" }, ] [[package]] @@ -998,36 +1044,36 @@ wheels = [ [[package]] name = "librt" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" }, - { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, - { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, - { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, - { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, - { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, - { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" }, - { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, - { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, - { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" }, - { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" }, - { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" }, - { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, - { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" }, - { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" }, - { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" }, +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/6b/3d5c13fb3e3c4f43206c8f9dfed13778c2ed4f000bacaa0b7ce3c402a265/librt-0.9.0.tar.gz", hash = "sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d", size = 184368, upload-time = "2026-04-09T16:06:26.173Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/90/89ddba8e1c20b0922783cd93ed8e64f34dc05ab59c38a9c7e313632e20ff/librt-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4", size = 68332, upload-time = "2026-04-09T16:05:00.09Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/7aa4da1fb08bdeeb540cb07bfc8207cb32c5c41642f2594dbd0098a0662d/librt-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d", size = 70581, upload-time = "2026-04-09T16:05:01.213Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/73a2187e1031041e93b7e3a25aae37aa6f13b838c550f7e0f06f66766212/librt-0.9.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f", size = 203984, upload-time = "2026-04-09T16:05:02.542Z" }, + { url = "https://files.pythonhosted.org/packages/5e/3d/23460d571e9cbddb405b017681df04c142fb1b04cbfce77c54b08e28b108/librt-0.9.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27", size = 215762, upload-time = "2026-04-09T16:05:04.127Z" }, + { url = "https://files.pythonhosted.org/packages/de/1e/42dc7f8ab63e65b20640d058e63e97fd3e482c1edbda3570d813b4d0b927/librt-0.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2", size = 230288, upload-time = "2026-04-09T16:05:05.883Z" }, + { url = "https://files.pythonhosted.org/packages/dc/08/ca812b6d8259ad9ece703397f8ad5c03af5b5fedfce64279693d3ce4087c/librt-0.9.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b", size = 224103, upload-time = "2026-04-09T16:05:07.148Z" }, + { url = "https://files.pythonhosted.org/packages/b6/3f/620490fb2fa66ffd44e7f900254bc110ebec8dac6c1b7514d64662570e6f/librt-0.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265", size = 232122, upload-time = "2026-04-09T16:05:08.386Z" }, + { url = "https://files.pythonhosted.org/packages/e9/83/12864700a1b6a8be458cf5d05db209b0d8e94ae281e7ec261dbe616597b4/librt-0.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084", size = 225045, upload-time = "2026-04-09T16:05:09.707Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1b/845d339c29dc7dbc87a2e992a1ba8d28d25d0e0372f9a0a2ecebde298186/librt-0.9.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8", size = 227372, upload-time = "2026-04-09T16:05:10.942Z" }, + { url = "https://files.pythonhosted.org/packages/8d/fe/277985610269d926a64c606f761d58d3db67b956dbbf40024921e95e7fcb/librt-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f", size = 248224, upload-time = "2026-04-09T16:05:12.254Z" }, + { url = "https://files.pythonhosted.org/packages/92/1b/ee486d244b8de6b8b5dbaefabe6bfdd4a72e08f6353edf7d16d27114da8d/librt-0.9.0-cp312-cp312-win32.whl", hash = "sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f", size = 55986, upload-time = "2026-04-09T16:05:13.529Z" }, + { url = "https://files.pythonhosted.org/packages/89/7a/ba1737012308c17dc6d5516143b5dce9a2c7ba3474afd54e11f44a4d1ef3/librt-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745", size = 63260, upload-time = "2026-04-09T16:05:14.68Z" }, + { url = "https://files.pythonhosted.org/packages/36/e4/01752c113da15127f18f7bf11142f5640038f062407a611c059d0036c6aa/librt-0.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9", size = 53694, upload-time = "2026-04-09T16:05:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d7/1b3e26fffde1452d82f5666164858a81c26ebe808e7ae8c9c88628981540/librt-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e", size = 68367, upload-time = "2026-04-09T16:05:17.243Z" }, + { url = "https://files.pythonhosted.org/packages/a5/5b/c61b043ad2e091fbe1f2d35d14795e545d0b56b03edaa390fa1dcee3d160/librt-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22", size = 70595, upload-time = "2026-04-09T16:05:18.471Z" }, + { url = "https://files.pythonhosted.org/packages/a3/22/2448471196d8a73370aa2f23445455dc42712c21404081fcd7a03b9e0749/librt-0.9.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a", size = 204354, upload-time = "2026-04-09T16:05:19.593Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5e/39fc4b153c78cfd2c8a2dcb32700f2d41d2312aa1050513183be4540930d/librt-0.9.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5", size = 216238, upload-time = "2026-04-09T16:05:20.868Z" }, + { url = "https://files.pythonhosted.org/packages/d7/42/bc2d02d0fa7badfa63aa8d6dcd8793a9f7ef5a94396801684a51ed8d8287/librt-0.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11", size = 230589, upload-time = "2026-04-09T16:05:22.305Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7b/e2d95cc513866373692aa5edf98080d5602dd07cabfb9e5d2f70df2f25f7/librt-0.9.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858", size = 224610, upload-time = "2026-04-09T16:05:23.647Z" }, + { url = "https://files.pythonhosted.org/packages/31/d5/6cec4607e998eaba57564d06a1295c21b0a0c8de76e4e74d699e627bd98c/librt-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e", size = 232558, upload-time = "2026-04-09T16:05:25.025Z" }, + { url = "https://files.pythonhosted.org/packages/95/8c/27f1d8d3aaf079d3eb26439bf0b32f1482340c3552e324f7db9dca858671/librt-0.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0", size = 225521, upload-time = "2026-04-09T16:05:26.311Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d8/1e0d43b1c329b416017619469b3c3801a25a6a4ef4a1c68332aeaa6f72ca/librt-0.9.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2", size = 227789, upload-time = "2026-04-09T16:05:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/2c/b4/d3d842e88610fcd4c8eec7067b0c23ef2d7d3bff31496eded6a83b0f99be/librt-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d", size = 248616, upload-time = "2026-04-09T16:05:29.181Z" }, + { url = "https://files.pythonhosted.org/packages/ec/28/527df8ad0d1eb6c8bdfa82fc190f1f7c4cca5a1b6d7b36aeabf95b52d74d/librt-0.9.0-cp313-cp313-win32.whl", hash = "sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd", size = 56039, upload-time = "2026-04-09T16:05:30.709Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a7/413652ad0d92273ee5e30c000fc494b361171177c83e57c060ecd3c21538/librt-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519", size = 63264, upload-time = "2026-04-09T16:05:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0a/92c244309b774e290ddb15e93363846ae7aa753d9586b8aad511c5e6145b/librt-0.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5", size = 53728, upload-time = "2026-04-09T16:05:33.31Z" }, ] [[package]] @@ -1065,14 +1111,14 @@ wheels = [ [[package]] name = "mako" -version = "1.3.10" +version = "1.3.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/8a/805404d0c0b9f3d7a326475ca008db57aea9c5c9f2e1e39ed0faa335571c/mako-1.3.11.tar.gz", hash = "sha256:071eb4ab4c5010443152255d77db7faa6ce5916f35226eb02dc34479b6858069", size = 399811, upload-time = "2026-04-14T20:19:51.493Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, + { url = "https://files.pythonhosted.org/packages/68/a5/19d7aaa7e433713ffe881df33705925a196afb9532efc8475d26593921a6/mako-1.3.11-py3-none-any.whl", hash = "sha256:e372c6e333cf004aa736a15f425087ec977e1fcbd2966aae7f17c8dc1da27a77", size = 78503, upload-time = "2026-04-14T20:19:53.233Z" }, ] [[package]] @@ -1338,7 +1384,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.19.1" +version = "1.20.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, @@ -1346,21 +1392,23 @@ dependencies = [ { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, + { url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" }, + { url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" }, + { url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" }, + { url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" }, + { url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" }, + { url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" }, + { url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" }, ] [[package]] @@ -1429,137 +1477,152 @@ wheels = [ ] [[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" +name = "nvidia-cublas" +version = "13.1.0.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226, upload-time = "2025-10-09T08:59:04.818Z" }, + { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236, upload-time = "2025-10-09T08:59:32.536Z" }, ] [[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" +name = "nvidia-cuda-cupti" +version = "13.0.85" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, ] [[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" +name = "nvidia-cuda-nvrtc" +version = "13.0.88" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, ] [[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" +name = "nvidia-cuda-runtime" +version = "13.0.96" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, ] [[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" +name = "nvidia-cudnn-cu13" +version = "9.19.0.56" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform != 'win32'" }, + { name = "nvidia-cublas", marker = "sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, + { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, + { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321, upload-time = "2026-02-03T20:44:52.837Z" }, ] [[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" +name = "nvidia-cufft" +version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, ] [[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" +name = "nvidia-cufile" +version = "1.15.1.6" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, ] [[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" +name = "nvidia-curand" +version = "10.4.0.35" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, ] [[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" +name = "nvidia-cusolver" +version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform != 'win32'" }, - { name = "nvidia-cusparse-cu12", marker = "sys_platform != 'win32'" }, - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'win32'" }, + { name = "nvidia-cublas", marker = "sys_platform != 'win32'" }, + { name = "nvidia-cusparse", marker = "sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, ] [[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" +name = "nvidia-cusparse" +version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, ] [[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" +name = "nvidia-cusparselt-cu13" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, + { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277, upload-time = "2025-08-13T19:22:40.982Z" }, + { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119, upload-time = "2025-08-13T19:23:41.967Z" }, ] [[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" +name = "nvidia-nccl-cu13" +version = "2.28.9" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, + { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677, upload-time = "2025-11-18T05:49:03.45Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177, upload-time = "2025-11-18T05:49:17.677Z" }, ] [[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" +name = "nvidia-nvjitlink" +version = "13.0.88" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, ] [[package]] -name = "nvidia-nvshmem-cu12" +name = "nvidia-nvshmem-cu13" version = "3.4.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, ] [[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" +name = "nvidia-nvtx" +version = "13.0.85" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, ] [[package]] @@ -1626,42 +1689,42 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.40.0" +version = "1.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/1d/4049a9e8698361cc1a1aa03a6c59e4fa4c71e0c0f94a30f988a6876a2ae6/opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f", size = 70851, upload-time = "2026-03-04T14:17:21.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/8e/3778a7e87801d994869a9396b9fc2a289e5f9be91ff54a27d41eace494b0/opentelemetry_api-1.41.0.tar.gz", hash = "sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09", size = 71416, upload-time = "2026-04-09T14:38:34.544Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, + { url = "https://files.pythonhosted.org/packages/58/ee/99ab786653b3bda9c37ade7e24a7b607a1b1f696063172768417539d876d/opentelemetry_api-1.41.0-py3-none-any.whl", hash = "sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f", size = 69007, upload-time = "2026-04-09T14:38:11.833Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.40.0" +version = "1.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/fd/3c3125b20ba18ce2155ba9ea74acb0ae5d25f8cd39cfd37455601b7955cc/opentelemetry_sdk-1.40.0.tar.gz", hash = "sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2", size = 184252, upload-time = "2026-03-04T14:17:31.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/0e/a586df1186f9f56b5a0879d52653effc40357b8e88fc50fe300038c3c08b/opentelemetry_sdk-1.41.0.tar.gz", hash = "sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd", size = 230181, upload-time = "2026-04-09T14:38:47.225Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/c5/6a852903d8bfac758c6dc6e9a68b015d3c33f2f1be5e9591e0f4b69c7e0a/opentelemetry_sdk-1.40.0-py3-none-any.whl", hash = "sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1", size = 141951, upload-time = "2026-03-04T14:17:17.961Z" }, + { url = "https://files.pythonhosted.org/packages/2c/13/a7825118208cb32e6a4edcd0a99f925cbef81e77b3b0aedfd9125583c543/opentelemetry_sdk-1.41.0-py3-none-any.whl", hash = "sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd", size = 180214, upload-time = "2026-04-09T14:38:30.657Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.61b0" +version = "0.62b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/c0/4ae7973f3c2cfd2b6e321f1675626f0dab0a97027cc7a297474c9c8f3d04/opentelemetry_semantic_conventions-0.61b0.tar.gz", hash = "sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a", size = 145755, upload-time = "2026-03-04T14:17:32.664Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/b0/c14f723e86c049b7bf8ff431160d982519b97a7be2857ed2247377397a24/opentelemetry_semantic_conventions-0.62b0.tar.gz", hash = "sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097", size = 145753, upload-time = "2026-04-09T14:38:48.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/37/cc6a55e448deaa9b27377d087da8615a3416d8ad523d5960b78dbeadd02a/opentelemetry_semantic_conventions-0.61b0-py3-none-any.whl", hash = "sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2", size = 231621, upload-time = "2026-03-04T14:17:19.33Z" }, + { url = "https://files.pythonhosted.org/packages/58/6c/5e86fa1759a525ef91c2d8b79d668574760ff3f900d114297765eb8786cb/opentelemetry_semantic_conventions-0.62b0-py3-none-any.whl", hash = "sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489", size = 231619, upload-time = "2026-04-09T14:38:32.394Z" }, ] [[package]] @@ -1709,11 +1772,11 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.4" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/17/9c3094b822982b9f1ea666d8580ce59000f61f87c1663556fb72031ad9ec/pathspec-1.1.0.tar.gz", hash = "sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080", size = 133918, upload-time = "2026-04-23T01:46:22.298Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/8eed0486f074e9f1ca7f8ce5ad663e65f12fdab344028d658fa1b03d35e0/pathspec-1.1.0-py3-none-any.whl", hash = "sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42", size = 56264, upload-time = "2026-04-23T01:46:20.606Z" }, ] [[package]] @@ -1762,16 +1825,16 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.9.4" +version = "4.9.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, ] [[package]] name = "pre-commit" -version = "4.5.1" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -1780,9 +1843,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] [[package]] @@ -1952,11 +2015,11 @@ wheels = [ [[package]] name = "pyasn1" -version = "0.6.2" +version = "0.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, ] [[package]] @@ -1982,7 +2045,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.5" +version = "2.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1990,61 +2053,63 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" }, ] [[package]] name = "pydantic-core" -version = "2.41.5" +version = "2.46.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" }, + { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" }, + { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" }, + { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" }, + { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" }, + { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" }, + { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" }, + { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" }, + { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" }, + { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" }, + { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" }, + { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" }, + { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" }, + { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" }, + { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" }, + { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" }, + { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" }, + { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" }, + { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" }, + { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" }, + { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" }, + { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" }, + { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" }, + { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" }, ] [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] [[package]] @@ -2070,15 +2135,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.2.1" +version = "1.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/88/815e53084c5079a59df912825a279f41dd2e0df82281770eadc732f5352c/python_discovery-1.2.1.tar.gz", hash = "sha256:180c4d114bff1c32462537eac5d6a332b768242b76b69c0259c7d14b1b680c9e", size = 58457, upload-time = "2026-03-26T22:30:44.496Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl", hash = "sha256:b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502", size = 31674, upload-time = "2026-03-26T22:30:43.396Z" }, + { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, ] [[package]] @@ -2162,7 +2227,7 @@ wheels = [ [[package]] name = "rationai-masks" version = "1.1.1" -source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git#b85e26bca50eddcf916128e2ee4f31b28ea23d28" } +source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git#a1bfa348b525133f15dc57418cccdb2d08dc1f0d" } dependencies = [ { name = "numpy" }, { name = "openslide-python" }, @@ -2216,7 +2281,7 @@ dependencies = [ [[package]] name = "ray" -version = "2.54.0" +version = "2.55.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -2229,13 +2294,13 @@ dependencies = [ { name = "requests" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/16/45eefb51eb1767342a6dbf41af0b432279e422e56160705fcd1098a7ec53/ray-2.54.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:cf5c33b4b13850ec24a5bd5f9d9e0a8161f8e586bfd297e52913d170dec447fe", size = 70084880, upload-time = "2026-02-18T04:05:22.007Z" }, - { url = "https://files.pythonhosted.org/packages/60/ad/e07aca3637e9c3ec4857ec4366208099cf8488ece8061a9925ba29b66382/ray-2.54.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:795ae21d6b764245d3f521bc5833446d58569e7dfde9c5777417eb285d87450f", size = 72107346, upload-time = "2026-02-18T04:05:27.999Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b9/cc5ea8460c3dc602e6b7198277a7c59ba2b8929374ab22efa8df9f3deac8/ray-2.54.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:a972afd5aa3dda99d0b2f369b5f62e5dd95865ab7d37bf2e0a0e0d2cfbd9b325", size = 72967230, upload-time = "2026-02-18T04:05:33.771Z" }, - { url = "https://files.pythonhosted.org/packages/de/d7/744de3b1bb881701330ddcbb2f6efaccd65915d564ece899a3838f9fb105/ray-2.54.0-cp312-cp312-win_amd64.whl", hash = "sha256:2ee074ede491d0aacfa339c003f5d7a15826e1e2a72ce873234ccbc0446e19b3", size = 27427353, upload-time = "2026-02-18T04:05:38.853Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f2/5c0161d10445e703b7d01413ab54ec1cc5e27032555279d296df89b9c4ee/ray-2.54.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5ad77961fea16c697a0fb0e51216dd39c0bec28868cde54ac668edd58d12b8ae", size = 70030991, upload-time = "2026-02-18T04:05:43.966Z" }, - { url = "https://files.pythonhosted.org/packages/fd/8c/4a4a38eaec6e9614076a96967f58540f4f8d4aa0c793f43150c5df23cb9a/ray-2.54.0-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:8952c23a8aa94f10728c2d16e0dc3732d09aa0e6254801757ff494984a214f45", size = 72013826, upload-time = "2026-02-18T04:05:49.866Z" }, - { url = "https://files.pythonhosted.org/packages/42/ac/e7ec2a406bd755f61c7090460fa5ab3f09b00c3c2d8db6d0b559f78a30eb/ray-2.54.0-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:ab89e6089abb6e46fb98fdd96d399b31a852d79127cd8ac00746c61d93defa2c", size = 72880209, upload-time = "2026-02-18T04:05:55.498Z" }, + { url = "https://files.pythonhosted.org/packages/ac/3a/4d34f471a68b958b7f94c974c19ad6836a61a2dc16393df4294169a2e4b0/ray-2.55.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:137f9006eee28caab8260803cca314f37bbda3fc94fdfa31c770b5d019626ad8", size = 65822379, upload-time = "2026-04-22T20:09:58.064Z" }, + { url = "https://files.pythonhosted.org/packages/f1/13/0db535102d0256b350ca116d8987588aca1a1f9ebb4638e1e1ff88bbcef8/ray-2.55.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:26541f69bb55607ef8335baac75b2ed12ff2ce02d56313219b29eda003039221", size = 72910802, upload-time = "2026-04-22T20:10:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f8/fffadf3f4285eebd460e4d7f2ed1c0cd641ed89613c3f49eb881ee9fa7e2/ray-2.55.1-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:263705f6bab29e7622a94f82da25fd7f9cead76cdf89a07aab28f79cdf8f9d95", size = 73765203, upload-time = "2026-04-22T20:10:10.495Z" }, + { url = "https://files.pythonhosted.org/packages/10/f7/5acb86fc9625a0e6bbc40e1c7d42c60770e78585439a921c32738b6d675a/ray-2.55.1-cp312-cp312-win_amd64.whl", hash = "sha256:9ad56704c8bd7e92130162f9c58e4ef473609515637673d5a36e761f95335206", size = 27865547, upload-time = "2026-04-22T20:10:15.364Z" }, + { url = "https://files.pythonhosted.org/packages/d5/95/898699cc1a6a5f304ea95376d079843b5c05f4c8c1ec7e55a5cc7ffcea50/ray-2.55.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:f9844a9272ef2e6eb5771025866072cf4234cf4c7cc1a31e235b7de7111864be", size = 65766823, upload-time = "2026-04-22T20:10:20.786Z" }, + { url = "https://files.pythonhosted.org/packages/c9/13/87deecc090c672e45a0cf6f5eef511de448b93f37ef18fd10eb8e8557a0d/ray-2.55.1-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:b415d590e062f248907e0fe42994943f11726b7178fcf4b1cf5546721fb1a5f8", size = 72818676, upload-time = "2026-04-22T20:10:26.705Z" }, + { url = "https://files.pythonhosted.org/packages/71/d7/fc95d3b8824c62105c64aa1b59c59600b581f608d78a2af753e010936dc9/ray-2.55.1-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:1380e043eb57cde69b7e9199c6f2558ceeb8f0fc41c97d1d5e50ea042115f302", size = 73678908, upload-time = "2026-04-22T20:10:32.795Z" }, ] [[package]] @@ -2254,63 +2319,63 @@ wheels = [ [[package]] name = "regex" -version = "2026.2.28" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/42/9061b03cf0fc4b5fa2c3984cbbaed54324377e440a5c5a29d29a72518d62/regex-2026.2.28-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fcf26c3c6d0da98fada8ae4ef0aa1c3405a431c0a77eb17306d38a89b02adcd7", size = 489574, upload-time = "2026-02-28T02:16:50.455Z" }, - { url = "https://files.pythonhosted.org/packages/77/83/0c8a5623a233015595e3da499c5a1c13720ac63c107897a6037bb97af248/regex-2026.2.28-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02473c954af35dd2defeb07e44182f5705b30ea3f351a7cbffa9177beb14da5d", size = 291426, upload-time = "2026-02-28T02:16:52.52Z" }, - { url = "https://files.pythonhosted.org/packages/9e/06/3ef1ac6910dc3295ebd71b1f9bfa737e82cfead211a18b319d45f85ddd09/regex-2026.2.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b65d33a17101569f86d9c5966a8b1d7fbf8afdda5a8aa219301b0a80f58cf7d", size = 289200, upload-time = "2026-02-28T02:16:54.08Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c9/8cc8d850b35ab5650ff6756a1cb85286e2000b66c97520b29c1587455344/regex-2026.2.28-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71dcecaa113eebcc96622c17692672c2d104b1d71ddf7adeda90da7ddeb26fc", size = 796765, upload-time = "2026-02-28T02:16:55.905Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5d/57702597627fc23278ebf36fbb497ac91c0ce7fec89ac6c81e420ca3e38c/regex-2026.2.28-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:481df4623fa4969c8b11f3433ed7d5e3dc9cec0f008356c3212b3933fb77e3d8", size = 863093, upload-time = "2026-02-28T02:16:58.094Z" }, - { url = "https://files.pythonhosted.org/packages/02/6d/f3ecad537ca2811b4d26b54ca848cf70e04fcfc138667c146a9f3157779c/regex-2026.2.28-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64e7c6ad614573e0640f271e811a408d79a9e1fe62a46adb602f598df42a818d", size = 909455, upload-time = "2026-02-28T02:17:00.918Z" }, - { url = "https://files.pythonhosted.org/packages/9e/40/bb226f203caa22c1043c1ca79b36340156eca0f6a6742b46c3bb222a3a57/regex-2026.2.28-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b08a06976ff4fb0d83077022fde3eca06c55432bb997d8c0495b9a4e9872f4", size = 802037, upload-time = "2026-02-28T02:17:02.842Z" }, - { url = "https://files.pythonhosted.org/packages/44/7c/c6d91d8911ac6803b45ca968e8e500c46934e58c0903cbc6d760ee817a0a/regex-2026.2.28-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:864cdd1a2ef5716b0ab468af40139e62ede1b3a53386b375ec0786bb6783fc05", size = 775113, upload-time = "2026-02-28T02:17:04.506Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8d/4a9368d168d47abd4158580b8c848709667b1cd293ff0c0c277279543bd0/regex-2026.2.28-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:511f7419f7afab475fd4d639d4aedfc54205bcb0800066753ef68a59f0f330b5", size = 784194, upload-time = "2026-02-28T02:17:06.888Z" }, - { url = "https://files.pythonhosted.org/packages/cc/bf/2c72ab5d8b7be462cb1651b5cc333da1d0068740342f350fcca3bca31947/regex-2026.2.28-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b42f7466e32bf15a961cf09f35fa6323cc72e64d3d2c990b10de1274a5da0a59", size = 856846, upload-time = "2026-02-28T02:17:09.11Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f4/6b65c979bb6d09f51bb2d2a7bc85de73c01ec73335d7ddd202dcb8cd1c8f/regex-2026.2.28-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8710d61737b0c0ce6836b1da7109f20d495e49b3809f30e27e9560be67a257bf", size = 763516, upload-time = "2026-02-28T02:17:11.004Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/29ea5e27400ee86d2cc2b4e80aa059df04eaf78b4f0c18576ae077aeff68/regex-2026.2.28-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4390c365fd2d45278f45afd4673cb90f7285f5701607e3ad4274df08e36140ae", size = 849278, upload-time = "2026-02-28T02:17:12.693Z" }, - { url = "https://files.pythonhosted.org/packages/1d/91/3233d03b5f865111cd517e1c95ee8b43e8b428d61fa73764a80c9bb6f537/regex-2026.2.28-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cb3b1db8ff6c7b8bf838ab05583ea15230cb2f678e569ab0e3a24d1e8320940b", size = 790068, upload-time = "2026-02-28T02:17:14.9Z" }, - { url = "https://files.pythonhosted.org/packages/76/92/abc706c1fb03b4580a09645b206a3fc032f5a9f457bc1a8038ac555658ab/regex-2026.2.28-cp312-cp312-win32.whl", hash = "sha256:f8ed9a5d4612df9d4de15878f0bc6aa7a268afbe5af21a3fdd97fa19516e978c", size = 266416, upload-time = "2026-02-28T02:17:17.15Z" }, - { url = "https://files.pythonhosted.org/packages/fa/06/2a6f7dff190e5fa9df9fb4acf2fdf17a1aa0f7f54596cba8de608db56b3a/regex-2026.2.28-cp312-cp312-win_amd64.whl", hash = "sha256:01d65fd24206c8e1e97e2e31b286c59009636c022eb5d003f52760b0f42155d4", size = 277297, upload-time = "2026-02-28T02:17:18.723Z" }, - { url = "https://files.pythonhosted.org/packages/b7/f0/58a2484851fadf284458fdbd728f580d55c1abac059ae9f048c63b92f427/regex-2026.2.28-cp312-cp312-win_arm64.whl", hash = "sha256:c0b5ccbb8ffb433939d248707d4a8b31993cb76ab1a0187ca886bf50e96df952", size = 270408, upload-time = "2026-02-28T02:17:20.328Z" }, - { url = "https://files.pythonhosted.org/packages/87/f6/dc9ef48c61b79c8201585bf37fa70cd781977da86e466cd94e8e95d2443b/regex-2026.2.28-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6d63a07e5ec8ce7184452cb00c41c37b49e67dc4f73b2955b5b8e782ea970784", size = 489311, upload-time = "2026-02-28T02:17:22.591Z" }, - { url = "https://files.pythonhosted.org/packages/95/c8/c20390f2232d3f7956f420f4ef1852608ad57aa26c3dd78516cb9f3dc913/regex-2026.2.28-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e59bc8f30414d283ae8ee1617b13d8112e7135cb92830f0ec3688cb29152585a", size = 291285, upload-time = "2026-02-28T02:17:24.355Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a6/ba1068a631ebd71a230e7d8013fcd284b7c89c35f46f34a7da02082141b1/regex-2026.2.28-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:de0cf053139f96219ccfabb4a8dd2d217c8c82cb206c91d9f109f3f552d6b43d", size = 289051, upload-time = "2026-02-28T02:17:26.722Z" }, - { url = "https://files.pythonhosted.org/packages/1d/1b/7cc3b7af4c244c204b7a80924bd3d85aecd9ba5bc82b485c5806ee8cda9e/regex-2026.2.28-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb4db2f17e6484904f986c5a657cec85574c76b5c5e61c7aae9ffa1bc6224f95", size = 796842, upload-time = "2026-02-28T02:17:29.064Z" }, - { url = "https://files.pythonhosted.org/packages/24/87/26bd03efc60e0d772ac1e7b60a2e6325af98d974e2358f659c507d3c76db/regex-2026.2.28-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52b017b35ac2214d0db5f4f90e303634dc44e4aba4bd6235a27f97ecbe5b0472", size = 863083, upload-time = "2026-02-28T02:17:31.363Z" }, - { url = "https://files.pythonhosted.org/packages/ae/54/aeaf4afb1aa0a65e40de52a61dc2ac5b00a83c6cb081c8a1d0dda74f3010/regex-2026.2.28-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:69fc560ccbf08a09dc9b52ab69cacfae51e0ed80dc5693078bdc97db2f91ae96", size = 909412, upload-time = "2026-02-28T02:17:33.248Z" }, - { url = "https://files.pythonhosted.org/packages/12/2f/049901def913954e640d199bbc6a7ca2902b6aeda0e5da9d17f114100ec2/regex-2026.2.28-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e61eea47230eba62a31f3e8a0e3164d0f37ef9f40529fb2c79361bc6b53d2a92", size = 802101, upload-time = "2026-02-28T02:17:35.053Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/512fb9ff7f5b15ea204bb1967ebb649059446decacccb201381f9fa6aad4/regex-2026.2.28-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4f5c0b182ad4269e7381b7c27fdb0408399881f7a92a4624fd5487f2971dfc11", size = 775260, upload-time = "2026-02-28T02:17:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a8/9a92935878aba19bd72706b9db5646a6f993d99b3f6ed42c02ec8beb1d61/regex-2026.2.28-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:96f6269a2882fbb0ee76967116b83679dc628e68eaea44e90884b8d53d833881", size = 784311, upload-time = "2026-02-28T02:17:39.855Z" }, - { url = "https://files.pythonhosted.org/packages/09/d3/fc51a8a738a49a6b6499626580554c9466d3ea561f2b72cfdc72e4149773/regex-2026.2.28-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b5acd4b6a95f37c3c3828e5d053a7d4edaedb85de551db0153754924cb7c83e3", size = 856876, upload-time = "2026-02-28T02:17:42.317Z" }, - { url = "https://files.pythonhosted.org/packages/08/b7/2e641f3d084b120ca4c52e8c762a78da0b32bf03ef546330db3e2635dc5f/regex-2026.2.28-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2234059cfe33d9813a3677ef7667999caea9eeaa83fef98eb6ce15c6cf9e0215", size = 763632, upload-time = "2026-02-28T02:17:45.073Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6d/0009021d97e79ee99f3d8641f0a8d001eed23479ade4c3125a5480bf3e2d/regex-2026.2.28-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c15af43c72a7fb0c97cbc66fa36a43546eddc5c06a662b64a0cbf30d6ac40944", size = 849320, upload-time = "2026-02-28T02:17:47.192Z" }, - { url = "https://files.pythonhosted.org/packages/05/7a/51cfbad5758f8edae430cb21961a9c8d04bce1dae4d2d18d4186eec7cfa1/regex-2026.2.28-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9185cc63359862a6e80fe97f696e04b0ad9a11c4ac0a4a927f979f611bfe3768", size = 790152, upload-time = "2026-02-28T02:17:49.067Z" }, - { url = "https://files.pythonhosted.org/packages/90/3d/a83e2b6b3daa142acb8c41d51de3876186307d5cb7490087031747662500/regex-2026.2.28-cp313-cp313-win32.whl", hash = "sha256:fb66e5245db9652abd7196ace599b04d9c0e4aa7c8f0e2803938377835780081", size = 266398, upload-time = "2026-02-28T02:17:50.744Z" }, - { url = "https://files.pythonhosted.org/packages/85/4f/16e9ebb1fe5425e11b9596c8d57bf8877dcb32391da0bfd33742e3290637/regex-2026.2.28-cp313-cp313-win_amd64.whl", hash = "sha256:71a911098be38c859ceb3f9a9ce43f4ed9f4c6720ad8684a066ea246b76ad9ff", size = 277282, upload-time = "2026-02-28T02:17:53.074Z" }, - { url = "https://files.pythonhosted.org/packages/07/b4/92851335332810c5a89723bf7a7e35c7209f90b7d4160024501717b28cc9/regex-2026.2.28-cp313-cp313-win_arm64.whl", hash = "sha256:39bb5727650b9a0275c6a6690f9bb3fe693a7e6cc5c3155b1240aedf8926423e", size = 270382, upload-time = "2026-02-28T02:17:54.888Z" }, - { url = "https://files.pythonhosted.org/packages/24/07/6c7e4cec1e585959e96cbc24299d97e4437a81173217af54f1804994e911/regex-2026.2.28-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:97054c55db06ab020342cc0d35d6f62a465fa7662871190175f1ad6c655c028f", size = 492541, upload-time = "2026-02-28T02:17:56.813Z" }, - { url = "https://files.pythonhosted.org/packages/7c/13/55eb22ada7f43d4f4bb3815b6132183ebc331c81bd496e2d1f3b8d862e0d/regex-2026.2.28-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d25a10811de831c2baa6aef3c0be91622f44dd8d31dd12e69f6398efb15e48b", size = 292984, upload-time = "2026-02-28T02:17:58.538Z" }, - { url = "https://files.pythonhosted.org/packages/5b/11/c301f8cb29ce9644a5ef85104c59244e6e7e90994a0f458da4d39baa8e17/regex-2026.2.28-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d6cfe798d8da41bb1862ed6e0cba14003d387c3c0c4a5d45591076ae9f0ce2f8", size = 291509, upload-time = "2026-02-28T02:18:00.208Z" }, - { url = "https://files.pythonhosted.org/packages/b5/43/aabe384ec1994b91796e903582427bc2ffaed9c4103819ed3c16d8e749f3/regex-2026.2.28-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd0ce43e71d825b7c0661f9c54d4d74bd97c56c3fd102a8985bcfea48236bacb", size = 809429, upload-time = "2026-02-28T02:18:02.328Z" }, - { url = "https://files.pythonhosted.org/packages/04/b8/8d2d987a816720c4f3109cee7c06a4b24ad0e02d4fc74919ab619e543737/regex-2026.2.28-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00945d007fd74a9084d2ab79b695b595c6b7ba3698972fadd43e23230c6979c1", size = 869422, upload-time = "2026-02-28T02:18:04.23Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ad/2c004509e763c0c3719f97c03eca26473bffb3868d54c5f280b8cd4f9e3d/regex-2026.2.28-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bec23c11cbbf09a4df32fe50d57cbdd777bc442269b6e39a1775654f1c95dee2", size = 915175, upload-time = "2026-02-28T02:18:06.791Z" }, - { url = "https://files.pythonhosted.org/packages/55/c2/fd429066da487ef555a9da73bf214894aec77fc8c66a261ee355a69871a8/regex-2026.2.28-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5cdcc17d935c8f9d3f4db5c2ebe2640c332e3822ad5d23c2f8e0228e6947943a", size = 812044, upload-time = "2026-02-28T02:18:08.736Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ca/feedb7055c62a3f7f659971bf45f0e0a87544b6b0cf462884761453f97c5/regex-2026.2.28-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a448af01e3d8031c89c5d902040b124a5e921a25c4e5e07a861ca591ce429341", size = 782056, upload-time = "2026-02-28T02:18:10.777Z" }, - { url = "https://files.pythonhosted.org/packages/95/30/1aa959ed0d25c1dd7dd5047ea8ba482ceaef38ce363c401fd32a6b923e60/regex-2026.2.28-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:10d28e19bd4888e4abf43bd3925f3c134c52fdf7259219003588a42e24c2aa25", size = 798743, upload-time = "2026-02-28T02:18:13.025Z" }, - { url = "https://files.pythonhosted.org/packages/3b/1f/dadb9cf359004784051c897dcf4d5d79895f73a1bbb7b827abaa4814ae80/regex-2026.2.28-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:99985a2c277dcb9ccb63f937451af5d65177af1efdeb8173ac55b61095a0a05c", size = 864633, upload-time = "2026-02-28T02:18:16.84Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f1/b9a25eb24e1cf79890f09e6ec971ee5b511519f1851de3453bc04f6c902b/regex-2026.2.28-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e1e7b24cb3ae9953a560c563045d1ba56ee4749fbd05cf21ba571069bd7be81b", size = 770862, upload-time = "2026-02-28T02:18:18.892Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/c5cb10b7aa6f182f9247a30cc9527e326601f46f4df864ac6db588d11fcd/regex-2026.2.28-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d8511a01d0e4ee1992eb3ba19e09bc1866fe03f05129c3aec3fdc4cbc77aad3f", size = 854788, upload-time = "2026-02-28T02:18:21.475Z" }, - { url = "https://files.pythonhosted.org/packages/0a/50/414ba0731c4bd40b011fa4703b2cc86879ec060c64f2a906e65a56452589/regex-2026.2.28-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aaffaecffcd2479ce87aa1e74076c221700b7c804e48e98e62500ee748f0f550", size = 800184, upload-time = "2026-02-28T02:18:23.492Z" }, - { url = "https://files.pythonhosted.org/packages/69/50/0c7290987f97e7e6830b0d853f69dc4dc5852c934aae63e7fdcd76b4c383/regex-2026.2.28-cp313-cp313t-win32.whl", hash = "sha256:ef77bdde9c9eba3f7fa5b58084b29bbcc74bcf55fdbeaa67c102a35b5bd7e7cc", size = 269137, upload-time = "2026-02-28T02:18:25.375Z" }, - { url = "https://files.pythonhosted.org/packages/68/80/ef26ff90e74ceb4051ad6efcbbb8a4be965184a57e879ebcbdef327d18fa/regex-2026.2.28-cp313-cp313t-win_amd64.whl", hash = "sha256:98adf340100cbe6fbaf8e6dc75e28f2c191b1be50ffefe292fb0e6f6eefdb0d8", size = 280682, upload-time = "2026-02-28T02:18:27.205Z" }, - { url = "https://files.pythonhosted.org/packages/69/8b/fbad9c52e83ffe8f97e3ed1aa0516e6dff6bb633a41da9e64645bc7efdc5/regex-2026.2.28-cp313-cp313t-win_arm64.whl", hash = "sha256:2fb950ac1d88e6b6a9414381f403797b236f9fa17e1eee07683af72b1634207b", size = 271735, upload-time = "2026-02-28T02:18:29.015Z" }, +version = "2026.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/3a246dbf05666918bd3664d9d787f84a9108f6f43cc953a077e4a7dfdb7e/regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423", size = 416000, upload-time = "2026-04-03T20:56:28.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/28/b972a4d3df61e1d7bcf1b59fdb3cddef22f88b6be43f161bb41ebc0e4081/regex-2026.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52", size = 490434, upload-time = "2026-04-03T20:53:40.219Z" }, + { url = "https://files.pythonhosted.org/packages/84/20/30041446cf6dc3e0eab344fc62770e84c23b6b68a3b657821f9f80cb69b4/regex-2026.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb", size = 292061, upload-time = "2026-04-03T20:53:41.862Z" }, + { url = "https://files.pythonhosted.org/packages/62/c8/3baa06d75c98c46d4cc4262b71fd2edb9062b5665e868bca57859dadf93a/regex-2026.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76", size = 289628, upload-time = "2026-04-03T20:53:43.701Z" }, + { url = "https://files.pythonhosted.org/packages/31/87/3accf55634caad8c0acab23f5135ef7d4a21c39f28c55c816ae012931408/regex-2026.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be", size = 796651, upload-time = "2026-04-03T20:53:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0c/aaa2c83f34efedbf06f61cb1942c25f6cf1ee3b200f832c4d05f28306c2e/regex-2026.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1", size = 865916, upload-time = "2026-04-03T20:53:47.064Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f6/8c6924c865124643e8f37823eca845dc27ac509b2ee58123685e71cd0279/regex-2026.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13", size = 912287, upload-time = "2026-04-03T20:53:49.422Z" }, + { url = "https://files.pythonhosted.org/packages/11/0e/a9f6f81013e0deaf559b25711623864970fe6a098314e374ccb1540a4152/regex-2026.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9", size = 801126, upload-time = "2026-04-03T20:53:51.096Z" }, + { url = "https://files.pythonhosted.org/packages/71/61/3a0cc8af2dc0c8deb48e644dd2521f173f7e6513c6e195aad9aa8dd77ac5/regex-2026.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d", size = 776788, upload-time = "2026-04-03T20:53:52.889Z" }, + { url = "https://files.pythonhosted.org/packages/64/0b/8bb9cbf21ef7dee58e49b0fdb066a7aded146c823202e16494a36777594f/regex-2026.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3", size = 785184, upload-time = "2026-04-03T20:53:55.627Z" }, + { url = "https://files.pythonhosted.org/packages/99/c2/d3e80e8137b25ee06c92627de4e4d98b94830e02b3e6f81f3d2e3f504cf5/regex-2026.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0", size = 859913, upload-time = "2026-04-03T20:53:57.249Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/9d5d876157d969c804622456ef250017ac7a8f83e0e14f903b9e6df5ce95/regex-2026.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043", size = 765732, upload-time = "2026-04-03T20:53:59.428Z" }, + { url = "https://files.pythonhosted.org/packages/82/80/b568935b4421388561c8ed42aff77247285d3ae3bb2a6ca22af63bae805e/regex-2026.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244", size = 852152, upload-time = "2026-04-03T20:54:01.505Z" }, + { url = "https://files.pythonhosted.org/packages/39/29/f0f81217e21cd998245da047405366385d5c6072048038a3d33b37a79dc0/regex-2026.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73", size = 789076, upload-time = "2026-04-03T20:54:03.323Z" }, + { url = "https://files.pythonhosted.org/packages/49/1d/1d957a61976ab9d4e767dd4f9d04b66cc0c41c5e36cf40e2d43688b5ae6f/regex-2026.4.4-cp312-cp312-win32.whl", hash = "sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f", size = 266700, upload-time = "2026-04-03T20:54:05.639Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/bf575d396aeb58ea13b06ef2adf624f65b70fafef6950a80fc3da9cae3bc/regex-2026.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b", size = 277768, upload-time = "2026-04-03T20:54:07.312Z" }, + { url = "https://files.pythonhosted.org/packages/c9/27/049df16ec6a6828ccd72add3c7f54b4df029669bea8e9817df6fff58be90/regex-2026.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983", size = 270568, upload-time = "2026-04-03T20:54:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/9d/83/c4373bc5f31f2cf4b66f9b7c31005bd87fe66f0dce17701f7db4ee79ee29/regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943", size = 490273, upload-time = "2026-04-03T20:54:11.202Z" }, + { url = "https://files.pythonhosted.org/packages/46/f8/fe62afbcc3cf4ad4ac9adeaafd98aa747869ae12d3e8e2ac293d0593c435/regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031", size = 291954, upload-time = "2026-04-03T20:54:13.412Z" }, + { url = "https://files.pythonhosted.org/packages/5a/92/4712b9fe6a33d232eeb1c189484b80c6c4b8422b90e766e1195d6e758207/regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7", size = 289487, upload-time = "2026-04-03T20:54:15.824Z" }, + { url = "https://files.pythonhosted.org/packages/88/2c/f83b93f85e01168f1070f045a42d4c937b69fdb8dd7ae82d307253f7e36e/regex-2026.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17", size = 796646, upload-time = "2026-04-03T20:54:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/df/55/61a2e17bf0c4dc57e11caf8dd11771280d8aaa361785f9e3bc40d653f4a7/regex-2026.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17", size = 865904, upload-time = "2026-04-03T20:54:20.019Z" }, + { url = "https://files.pythonhosted.org/packages/45/32/1ac8ed1b5a346b5993a3d256abe0a0f03b0b73c8cc88d928537368ac65b6/regex-2026.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae", size = 912304, upload-time = "2026-04-03T20:54:22.403Z" }, + { url = "https://files.pythonhosted.org/packages/26/47/2ee5c613ab546f0eddebf9905d23e07beb933416b1246c2d8791d01979b4/regex-2026.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e", size = 801126, upload-time = "2026-04-03T20:54:24.308Z" }, + { url = "https://files.pythonhosted.org/packages/75/cd/41dacd129ca9fd20bd7d02f83e0fad83e034ac8a084ec369c90f55ef37e2/regex-2026.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d", size = 776772, upload-time = "2026-04-03T20:54:26.319Z" }, + { url = "https://files.pythonhosted.org/packages/89/6d/5af0b588174cb5f46041fa7dd64d3fd5cd2fe51f18766703d1edc387f324/regex-2026.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27", size = 785228, upload-time = "2026-04-03T20:54:28.387Z" }, + { url = "https://files.pythonhosted.org/packages/b7/3b/f5a72b7045bd59575fc33bf1345f156fcfd5a8484aea6ad84b12c5a82114/regex-2026.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf", size = 860032, upload-time = "2026-04-03T20:54:30.641Z" }, + { url = "https://files.pythonhosted.org/packages/39/a4/72a317003d6fcd7a573584a85f59f525dfe8f67e355ca74eb6b53d66a5e2/regex-2026.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0", size = 765714, upload-time = "2026-04-03T20:54:32.789Z" }, + { url = "https://files.pythonhosted.org/packages/25/1e/5672e16f34dbbcb2560cc7e6a2fbb26dfa8b270711e730101da4423d3973/regex-2026.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa", size = 852078, upload-time = "2026-04-03T20:54:34.546Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0d/c813f0af7c6cc7ed7b9558bac2e5120b60ad0fa48f813e4d4bd55446f214/regex-2026.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b", size = 789181, upload-time = "2026-04-03T20:54:36.642Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6d/a344608d1adbd2a95090ddd906cec09a11be0e6517e878d02a5123e0917f/regex-2026.4.4-cp313-cp313-win32.whl", hash = "sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62", size = 266690, upload-time = "2026-04-03T20:54:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/31/07/54049f89b46235ca6f45cd6c88668a7050e77d4a15555e47dd40fde75263/regex-2026.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81", size = 277733, upload-time = "2026-04-03T20:54:40.11Z" }, + { url = "https://files.pythonhosted.org/packages/0e/21/61366a8e20f4d43fb597708cac7f0e2baadb491ecc9549b4980b2be27d16/regex-2026.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427", size = 270565, upload-time = "2026-04-03T20:54:41.883Z" }, + { url = "https://files.pythonhosted.org/packages/f1/1e/3a2b9672433bef02f5d39aa1143ca2c08f311c1d041c464a42be9ae648dc/regex-2026.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c", size = 494126, upload-time = "2026-04-03T20:54:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/4e/4b/c132a4f4fe18ad3340d89fcb56235132b69559136036b845be3c073142ed/regex-2026.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141", size = 293882, upload-time = "2026-04-03T20:54:45.41Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5f/eaa38092ce7a023656280f2341dbbd4ad5f05d780a70abba7bb4f4bea54c/regex-2026.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717", size = 292334, upload-time = "2026-04-03T20:54:47.051Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f6/dd38146af1392dac33db7074ab331cec23cced3759167735c42c5460a243/regex-2026.4.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07", size = 811691, upload-time = "2026-04-03T20:54:49.074Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f0/dc54c2e69f5eeec50601054998ec3690d5344277e782bd717e49867c1d29/regex-2026.4.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca", size = 871227, upload-time = "2026-04-03T20:54:51.035Z" }, + { url = "https://files.pythonhosted.org/packages/a1/af/cb16bd5dc61621e27df919a4449bbb7e5a1034c34d307e0a706e9cc0f3e3/regex-2026.4.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520", size = 917435, upload-time = "2026-04-03T20:54:52.994Z" }, + { url = "https://files.pythonhosted.org/packages/5c/71/8b260897f22996b666edd9402861668f45a2ca259f665ac029e6104a2d7d/regex-2026.4.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883", size = 816358, upload-time = "2026-04-03T20:54:54.884Z" }, + { url = "https://files.pythonhosted.org/packages/1c/60/775f7f72a510ef238254906c2f3d737fc80b16ca85f07d20e318d2eea894/regex-2026.4.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b", size = 785549, upload-time = "2026-04-03T20:54:57.01Z" }, + { url = "https://files.pythonhosted.org/packages/58/42/34d289b3627c03cf381e44da534a0021664188fa49ba41513da0b4ec6776/regex-2026.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1", size = 801364, upload-time = "2026-04-03T20:54:58.981Z" }, + { url = "https://files.pythonhosted.org/packages/fc/20/f6ecf319b382a8f1ab529e898b222c3f30600fcede7834733c26279e7465/regex-2026.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b", size = 866221, upload-time = "2026-04-03T20:55:00.88Z" }, + { url = "https://files.pythonhosted.org/packages/92/6a/9f16d3609d549bd96d7a0b2aee1625d7512ba6a03efc01652149ef88e74d/regex-2026.4.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff", size = 772530, upload-time = "2026-04-03T20:55:03.213Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f6/aa9768bc96a4c361ac96419fbaf2dcdc33970bb813df3ba9b09d5d7b6d96/regex-2026.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb", size = 856989, upload-time = "2026-04-03T20:55:05.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b4/c671db3556be2473ae3e4bb7a297c518d281452871501221251ea4ecba57/regex-2026.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4", size = 803241, upload-time = "2026-04-03T20:55:07.162Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5c/83e3b1d89fa4f6e5a1bc97b4abd4a9a97b3c1ac7854164f694f5f0ba98a0/regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa", size = 269921, upload-time = "2026-04-03T20:55:09.62Z" }, + { url = "https://files.pythonhosted.org/packages/28/07/077c387121f42cdb4d92b1301133c0d93b5709d096d1669ab847dda9fe2e/regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0", size = 281240, upload-time = "2026-04-03T20:55:11.521Z" }, + { url = "https://files.pythonhosted.org/packages/9d/22/ead4a4abc7c59a4d882662aa292ca02c8b617f30b6e163bc1728879e9353/regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe", size = 272440, upload-time = "2026-04-03T20:55:13.365Z" }, ] [[package]] name = "requests" -version = "2.32.5" +version = "2.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -2318,22 +2383,22 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, ] [[package]] name = "rich" -version = "14.3.3" +version = "15.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, ] [[package]] @@ -2390,27 +2455,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, - { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, - { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, - { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, - { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, - { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, - { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, - { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, - { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, - { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, +version = "0.15.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/8d/192f3d7103816158dfd5ea50d098ef2aec19194e6cbccd4b3485bdb2eb2d/ruff-0.15.11.tar.gz", hash = "sha256:f092b21708bf0e7437ce9ada249dfe688ff9a0954fc94abab05dcea7dcd29c33", size = 4637264, upload-time = "2026-04-16T18:46:26.58Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/1e/6aca3427f751295ab011828e15e9bf452200ac74484f1db4be0197b8170b/ruff-0.15.11-py3-none-linux_armv6l.whl", hash = "sha256:e927cfff503135c558eb581a0c9792264aae9507904eb27809cdcff2f2c847b7", size = 10607943, upload-time = "2026-04-16T18:46:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/e7/26/1341c262e74f36d4e84f3d6f4df0ac68cd53331a66bfc5080daa17c84c0b/ruff-0.15.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7a1b5b2938d8f890b76084d4fa843604d787a912541eae85fd7e233398bbb73e", size = 10988592, upload-time = "2026-04-16T18:46:00.742Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/850b1d6ffa9564fbb6740429bad53df1094082fe515c8c1e74b6d8d05f18/ruff-0.15.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d4176f3d194afbdaee6e41b9ccb1a2c287dba8700047df474abfbe773825d1cb", size = 10338501, upload-time = "2026-04-16T18:46:03.723Z" }, + { url = "https://files.pythonhosted.org/packages/f2/11/cc1284d3e298c45a817a6aadb6c3e1d70b45c9b36d8d9cce3387b495a03a/ruff-0.15.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b17c886fb88203ced3afe7f14e8d5ae96e9d2f4ccc0ee66aa19f2c2675a27e4", size = 10670693, upload-time = "2026-04-16T18:46:41.941Z" }, + { url = "https://files.pythonhosted.org/packages/ce/9e/f8288b034ab72b371513c13f9a41d9ba3effac54e24bfb467b007daee2ca/ruff-0.15.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49fafa220220afe7758a487b048de4c8f9f767f37dfefad46b9dd06759d003eb", size = 10416177, upload-time = "2026-04-16T18:46:21.717Z" }, + { url = "https://files.pythonhosted.org/packages/85/71/504d79abfd3d92532ba6bbe3d1c19fada03e494332a59e37c7c2dabae427/ruff-0.15.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2ab8427e74a00d93b8bda1307b1e60970d40f304af38bccb218e056c220120d", size = 11221886, upload-time = "2026-04-16T18:46:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/43/5a/947e6ab7a5ad603d65b474be15a4cbc6d29832db5d762cd142e4e3a74164/ruff-0.15.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:195072c0c8e1fc8f940652073df082e37a5d9cb43b4ab1e4d0566ab8977a13b7", size = 12075183, upload-time = "2026-04-16T18:46:07.944Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a1/0b7bb6268775fdd3a0818aee8efd8f5b4e231d24dd4d528ced2534023182/ruff-0.15.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a0996d486af3920dec930a2e7daed4847dfc12649b537a9335585ada163e9e", size = 11516575, upload-time = "2026-04-16T18:46:31.687Z" }, + { url = "https://files.pythonhosted.org/packages/30/c3/bb5168fc4d233cc06e95f482770d0f3c87945a0cd9f614b90ea8dc2f2833/ruff-0.15.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bef2cb556d509259f1fe440bb9cd33c756222cf0a7afe90d15edf0866702431", size = 11306537, upload-time = "2026-04-16T18:46:36.988Z" }, + { url = "https://files.pythonhosted.org/packages/e4/92/4cfae6441f3967317946f3b788136eecf093729b94d6561f963ed810c82e/ruff-0.15.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:030d921a836d7d4a12cf6e8d984a88b66094ccb0e0f17ddd55067c331191bf19", size = 11296813, upload-time = "2026-04-16T18:46:24.182Z" }, + { url = "https://files.pythonhosted.org/packages/43/26/972784c5dde8313acde8ac71ba8ac65475b85db4a2352a76c9934361f9bc/ruff-0.15.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0e783b599b4577788dbbb66b9addcef87e9a8832f4ce0c19e34bf55543a2f890", size = 10633136, upload-time = "2026-04-16T18:46:39.802Z" }, + { url = "https://files.pythonhosted.org/packages/5b/53/3985a4f185020c2f367f2e08a103032e12564829742a1b417980ce1514a0/ruff-0.15.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ae90592246625ba4a34349d68ec28d4400d75182b71baa196ddb9f82db025ef5", size = 10424701, upload-time = "2026-04-16T18:46:10.381Z" }, + { url = "https://files.pythonhosted.org/packages/d3/57/bf0dfb32241b56c83bb663a826133da4bf17f682ba8c096973065f6e6a68/ruff-0.15.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1f111d62e3c983ed20e0ca2e800f8d77433a5b1161947df99a5c2a3fb60514f0", size = 10873887, upload-time = "2026-04-16T18:46:29.157Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/e48076b2a57dc33ee8c7a957296f97c744ca891a8ffb4ffb1aaa3b3f517d/ruff-0.15.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:06f483d6646f59eaffba9ae30956370d3a886625f511a3108994000480621d1c", size = 11404316, upload-time = "2026-04-16T18:46:19.462Z" }, + { url = "https://files.pythonhosted.org/packages/88/27/0195d15fe7a897cbcba0904792c4b7c9fdd958456c3a17d2ea6093716a9a/ruff-0.15.11-py3-none-win32.whl", hash = "sha256:476a2aa56b7da0b73a3ee80b6b2f0e19cce544245479adde7baa65466664d5f3", size = 10655535, upload-time = "2026-04-16T18:46:12.47Z" }, + { url = "https://files.pythonhosted.org/packages/3a/5e/c927b325bd4c1d3620211a4b96f47864633199feed60fa936025ab27e090/ruff-0.15.11-py3-none-win_amd64.whl", hash = "sha256:8b6756d88d7e234fb0c98c91511aae3cd519d5e3ed271cae31b20f39cb2a12a3", size = 11779692, upload-time = "2026-04-16T18:46:17.268Z" }, + { url = "https://files.pythonhosted.org/packages/63/b6/aeadee5443e49baa2facd51131159fd6301cc4ccfc1541e4df7b021c37dd/ruff-0.15.11-py3-none-win_arm64.whl", hash = "sha256:063fed18cc1bbe0ee7393957284a6fe8b588c6a406a285af3ee3f46da2391ee4", size = 11032614, upload-time = "2026-04-16T18:46:34.487Z" }, ] [[package]] @@ -2548,11 +2613,11 @@ wheels = [ [[package]] name = "setuptools" -version = "82.0.1" +version = "81.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, ] [[package]] @@ -2622,35 +2687,35 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.48" +version = "2.0.49" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7", size = 9886075, upload-time = "2026-03-02T15:28:51.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/91/a42ae716f8925e9659df2da21ba941f158686856107a61cc97a95e7647a3/sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b", size = 2155737, upload-time = "2026-03-02T15:49:13.207Z" }, - { url = "https://files.pythonhosted.org/packages/b9/52/f75f516a1f3888f027c1cfb5d22d4376f4b46236f2e8669dcb0cddc60275/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb", size = 3337020, upload-time = "2026-03-02T15:50:34.547Z" }, - { url = "https://files.pythonhosted.org/packages/37/9a/0c28b6371e0cdcb14f8f1930778cb3123acfcbd2c95bb9cf6b4a2ba0cce3/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894", size = 3349983, upload-time = "2026-03-02T15:53:25.542Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/0aee8f3ff20b1dcbceb46ca2d87fcc3d48b407925a383ff668218509d132/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9", size = 3279690, upload-time = "2026-03-02T15:50:36.277Z" }, - { url = "https://files.pythonhosted.org/packages/ce/8c/a957bc91293b49181350bfd55e6dfc6e30b7f7d83dc6792d72043274a390/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e", size = 3314738, upload-time = "2026-03-02T15:53:27.519Z" }, - { url = "https://files.pythonhosted.org/packages/4b/44/1d257d9f9556661e7bdc83667cc414ba210acfc110c82938cb3611eea58f/sqlalchemy-2.0.48-cp312-cp312-win32.whl", hash = "sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99", size = 2115546, upload-time = "2026-03-02T15:54:31.591Z" }, - { url = "https://files.pythonhosted.org/packages/f2/af/c3c7e1f3a2b383155a16454df62ae8c62a30dd238e42e68c24cebebbfae6/sqlalchemy-2.0.48-cp312-cp312-win_amd64.whl", hash = "sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a", size = 2142484, upload-time = "2026-03-02T15:54:34.072Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4", size = 2152599, upload-time = "2026-03-02T15:49:14.41Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f", size = 3278825, upload-time = "2026-03-02T15:50:38.269Z" }, - { url = "https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed", size = 3295200, upload-time = "2026-03-02T15:53:29.366Z" }, - { url = "https://files.pythonhosted.org/packages/87/dc/1609a4442aefd750ea2f32629559394ec92e89ac1d621a7f462b70f736ff/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658", size = 3226876, upload-time = "2026-03-02T15:50:39.802Z" }, - { url = "https://files.pythonhosted.org/packages/37/c3/6ae2ab5ea2fa989fbac4e674de01224b7a9d744becaf59bb967d62e99bed/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8", size = 3265045, upload-time = "2026-03-02T15:53:31.421Z" }, - { url = "https://files.pythonhosted.org/packages/6f/82/ea4665d1bb98c50c19666e672f21b81356bd6077c4574e3d2bbb84541f53/sqlalchemy-2.0.48-cp313-cp313-win32.whl", hash = "sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131", size = 2113700, upload-time = "2026-03-02T15:54:35.825Z" }, - { url = "https://files.pythonhosted.org/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl", hash = "sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2", size = 2139487, upload-time = "2026-03-02T15:54:37.079Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/7b17bd50244b78a49d22cc63c969d71dc4de54567dc152a9b46f6fae40ce/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae", size = 3558851, upload-time = "2026-03-02T15:57:48.607Z" }, - { url = "https://files.pythonhosted.org/packages/20/0d/213668e9aca61d370f7d2a6449ea4ec699747fac67d4bda1bb3d129025be/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb", size = 3525525, upload-time = "2026-03-02T16:04:38.058Z" }, - { url = "https://files.pythonhosted.org/packages/85/d7/a84edf412979e7d59c69b89a5871f90a49228360594680e667cb2c46a828/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b", size = 3466611, upload-time = "2026-03-02T15:57:50.759Z" }, - { url = "https://files.pythonhosted.org/packages/86/55/42404ce5770f6be26a2b0607e7866c31b9a4176c819e9a7a5e0a055770be/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121", size = 3475812, upload-time = "2026-03-02T16:04:40.092Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ae/29b87775fadc43e627cf582fe3bda4d02e300f6b8f2747c764950d13784c/sqlalchemy-2.0.48-cp313-cp313t-win32.whl", hash = "sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485", size = 2141335, upload-time = "2026-03-02T15:52:51.518Z" }, - { url = "https://files.pythonhosted.org/packages/91/44/f39d063c90f2443e5b46ec4819abd3d8de653893aae92df42a5c4f5843de/sqlalchemy-2.0.48-cp313-cp313t-win_amd64.whl", hash = "sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79", size = 2173095, upload-time = "2026-03-02T15:52:52.79Z" }, - { url = "https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096", size = 1940202, upload-time = "2026-03-02T15:52:43.285Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/09/45/461788f35e0364a8da7bda51a1fe1b09762d0c32f12f63727998d85a873b/sqlalchemy-2.0.49.tar.gz", hash = "sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f", size = 9898221, upload-time = "2026-04-03T16:38:11.704Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/b3/2de412451330756aaaa72d27131db6dde23995efe62c941184e15242a5fa/sqlalchemy-2.0.49-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b", size = 2157681, upload-time = "2026-04-03T16:53:07.132Z" }, + { url = "https://files.pythonhosted.org/packages/50/84/b2a56e2105bd11ebf9f0b93abddd748e1a78d592819099359aa98134a8bf/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982", size = 3338976, upload-time = "2026-04-03T17:07:40Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fa/65fcae2ed62f84ab72cf89536c7c3217a156e71a2c111b1305ab6f0690e2/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672", size = 3351937, upload-time = "2026-04-03T17:12:23.374Z" }, + { url = "https://files.pythonhosted.org/packages/f8/2f/6fd118563572a7fe475925742eb6b3443b2250e346a0cc27d8d408e73773/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e", size = 3281646, upload-time = "2026-04-03T17:07:41.949Z" }, + { url = "https://files.pythonhosted.org/packages/c5/d7/410f4a007c65275b9cf82354adb4bb8ba587b176d0a6ee99caa16fe638f8/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750", size = 3316695, upload-time = "2026-04-03T17:12:25.642Z" }, + { url = "https://files.pythonhosted.org/packages/d9/95/81f594aa60ded13273a844539041ccf1e66c5a7bed0a8e27810a3b52d522/sqlalchemy-2.0.49-cp312-cp312-win32.whl", hash = "sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0", size = 2117483, upload-time = "2026-04-03T17:05:40.896Z" }, + { url = "https://files.pythonhosted.org/packages/47/9e/fd90114059175cac64e4fafa9bf3ac20584384d66de40793ae2e2f26f3bb/sqlalchemy-2.0.49-cp312-cp312-win_amd64.whl", hash = "sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4", size = 2144494, upload-time = "2026-04-03T17:05:42.282Z" }, + { url = "https://files.pythonhosted.org/packages/ae/81/81755f50eb2478eaf2049728491d4ea4f416c1eb013338682173259efa09/sqlalchemy-2.0.49-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120", size = 2154547, upload-time = "2026-04-03T16:53:08.64Z" }, + { url = "https://files.pythonhosted.org/packages/a2/bc/3494270da80811d08bcfa247404292428c4fe16294932bce5593f215cad9/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2", size = 3280782, upload-time = "2026-04-03T17:07:43.508Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f5/038741f5e747a5f6ea3e72487211579d8cbea5eb9827a9cbd61d0108c4bd/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3", size = 3297156, upload-time = "2026-04-03T17:12:27.697Z" }, + { url = "https://files.pythonhosted.org/packages/88/50/a6af0ff9dc954b43a65ca9b5367334e45d99684c90a3d3413fc19a02d43c/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7", size = 3228832, upload-time = "2026-04-03T17:07:45.38Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d1/5f6bdad8de0bf546fc74370939621396515e0cdb9067402d6ba1b8afbe9a/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33", size = 3267000, upload-time = "2026-04-03T17:12:29.657Z" }, + { url = "https://files.pythonhosted.org/packages/f7/30/ad62227b4a9819a5e1c6abff77c0f614fa7c9326e5a3bdbee90f7139382b/sqlalchemy-2.0.49-cp313-cp313-win32.whl", hash = "sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b", size = 2115641, upload-time = "2026-04-03T17:05:43.989Z" }, + { url = "https://files.pythonhosted.org/packages/17/3a/7215b1b7d6d49dc9a87211be44562077f5f04f9bb5a59552c1c8e2d98173/sqlalchemy-2.0.49-cp313-cp313-win_amd64.whl", hash = "sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148", size = 2141498, upload-time = "2026-04-03T17:05:45.7Z" }, + { url = "https://files.pythonhosted.org/packages/28/4b/52a0cb2687a9cd1648252bb257be5a1ba2c2ded20ba695c65756a55a15a4/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518", size = 3560807, upload-time = "2026-04-03T16:58:31.666Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d8/fda95459204877eed0458550d6c7c64c98cc50c2d8d618026737de9ed41a/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d", size = 3527481, upload-time = "2026-04-03T17:06:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0a/2aac8b78ac6487240cf7afef8f203ca783e8796002dc0cf65c4ee99ff8bb/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0", size = 3468565, upload-time = "2026-04-03T16:58:33.414Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/ce71cfa82c50a373fd2148b3c870be05027155ce791dc9a5dcf439790b8b/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08", size = 3477769, upload-time = "2026-04-03T17:06:02.787Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e8/0a9f5c1f7c6f9ca480319bf57c2d7423f08d31445974167a27d14483c948/sqlalchemy-2.0.49-cp313-cp313t-win32.whl", hash = "sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d", size = 2143319, upload-time = "2026-04-03T17:02:04.328Z" }, + { url = "https://files.pythonhosted.org/packages/0e/51/fb5240729fbec73006e137c4f7a7918ffd583ab08921e6ff81a999d6517a/sqlalchemy-2.0.49-cp313-cp313t-win_amd64.whl", hash = "sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba", size = 2175104, upload-time = "2026-04-03T17:02:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/e5/30/8519fdde58a7bdf155b714359791ad1dc018b47d60269d5d160d311fdc36/sqlalchemy-2.0.49-py3-none-any.whl", hash = "sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0", size = 1942158, upload-time = "2026-04-03T16:53:44.135Z" }, ] [[package]] @@ -2664,15 +2729,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.52.1" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, ] [[package]] @@ -2738,14 +2803,14 @@ wheels = [ [[package]] name = "tifffile" -version = "2026.3.3" +version = "2026.4.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/cb/2f6d79c7576e22c116352a801f4c3c8ace5957e9aced862012430b62e14f/tifffile-2026.3.3.tar.gz", hash = "sha256:d9a1266bed6f2ee1dd0abde2018a38b4f8b2935cb843df381d70ac4eac5458b7", size = 388745, upload-time = "2026-03-03T19:14:38.134Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4a/e687f5957fead200faad58dbf9c9431a2bbb118040e96f5fb8a55f7ebc50/tifffile-2026.4.11.tar.gz", hash = "sha256:17758ff0c0d4db385792a083ad3ca51fcb0f4d942642f4d8f8bc1287fdcf17bc", size = 394956, upload-time = "2026-04-12T01:57:28.793Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl", hash = "sha256:e8be15c94273113d31ecb7aa3a39822189dd11c4967e3cc88c178f1ad2fd1170", size = 243960, upload-time = "2026-03-03T19:14:35.808Z" }, + { url = "https://files.pythonhosted.org/packages/3f/9f/74f110b4271ded519c7add4341cbabc824de26817ff1c345b3109df9e99c/tifffile-2026.4.11-py3-none-any.whl", hash = "sha256:9b94ffeddb39e97601af646345e8808f885773de01b299e480ed6d3a41509ec9", size = 248227, upload-time = "2026-04-12T01:57:26.969Z" }, ] [[package]] @@ -2792,52 +2857,37 @@ wheels = [ [[package]] name = "torch" -version = "2.10.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, { name = "setuptools" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triton", marker = "sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, - { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, - { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, - { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, - { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, - { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, - { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, - { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" }, + { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115, upload-time = "2026-03-23T18:11:06.944Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279, upload-time = "2026-03-23T18:10:31.481Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047, upload-time = "2026-03-23T18:10:55.931Z" }, + { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801, upload-time = "2026-03-23T18:10:18.649Z" }, + { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382, upload-time = "2026-03-23T18:08:30.835Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509, upload-time = "2026-03-23T18:08:47.213Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842, upload-time = "2026-03-23T18:09:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574, upload-time = "2026-03-23T18:10:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324, upload-time = "2026-03-23T18:09:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026, upload-time = "2026-03-23T18:09:20.842Z" }, + { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702, upload-time = "2026-03-23T18:09:47.304Z" }, ] [[package]] @@ -2857,7 +2907,7 @@ wheels = [ [[package]] name = "torchvision" -version = "0.25.0" +version = "0.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -2865,18 +2915,18 @@ dependencies = [ { name = "torch" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/56/3a/6ea0d73f49a9bef38a1b3a92e8dd455cea58470985d25635beab93841748/torchvision-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2abe430c90b1d5e552680037d68da4eb80a5852ebb1c811b2b89d299b10573b", size = 1874920, upload-time = "2026-01-21T16:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/51/f8/c0e1ef27c66e15406fece94930e7d6feee4cb6374bbc02d945a630d6426e/torchvision-0.25.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b75deafa2dfea3e2c2a525559b04783515e3463f6e830cb71de0fb7ea36fe233", size = 2344556, upload-time = "2026-01-21T16:27:40.125Z" }, - { url = "https://files.pythonhosted.org/packages/68/2f/f24b039169db474e8688f649377de082a965fbf85daf4e46c44412f1d15a/torchvision-0.25.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f25aa9e380865b11ea6e9d99d84df86b9cc959f1a007cd966fc6f1ab2ed0e248", size = 8072351, upload-time = "2026-01-21T16:27:21.074Z" }, - { url = "https://files.pythonhosted.org/packages/ad/16/8f650c2e288977cf0f8f85184b90ee56ed170a4919347fc74ee99286ed6f/torchvision-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9c55ae8d673ab493325d1267cbd285bb94d56f99626c00ac4644de32a59ede3", size = 4303059, upload-time = "2026-01-21T16:27:11.08Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5b/1562a04a6a5a4cf8cf40016a0cdeda91ede75d6962cff7f809a85ae966a5/torchvision-0.25.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:24e11199e4d84ba9c5ee7825ebdf1cd37ce8deec225117f10243cae984ced3ec", size = 1874918, upload-time = "2026-01-21T16:27:39.02Z" }, - { url = "https://files.pythonhosted.org/packages/36/b1/3d6c42f62c272ce34fcce609bb8939bdf873dab5f1b798fd4e880255f129/torchvision-0.25.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5f271136d2d2c0b7a24c5671795c6e4fd8da4e0ea98aeb1041f62bc04c4370ef", size = 2309106, upload-time = "2026-01-21T16:27:30.624Z" }, - { url = "https://files.pythonhosted.org/packages/c7/60/59bb9c8b67cce356daeed4cb96a717caa4f69c9822f72e223a0eae7a9bd9/torchvision-0.25.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:855c0dc6d37f462482da7531c6788518baedca1e0847f3df42a911713acdfe52", size = 8071522, upload-time = "2026-01-21T16:27:29.392Z" }, - { url = "https://files.pythonhosted.org/packages/32/a5/9a9b1de0720f884ea50dbf9acb22cbe5312e51d7b8c4ac6ba9b51efd9bba/torchvision-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:cef0196be31be421f6f462d1e9da1101be7332d91984caa6f8022e6c78a5877f", size = 4321911, upload-time = "2026-01-21T16:27:35.195Z" }, - { url = "https://files.pythonhosted.org/packages/52/99/dca81ed21ebaeff2b67cc9f815a20fdaa418b69f5f9ea4c6ed71721470db/torchvision-0.25.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a8f8061284395ce31bcd460f2169013382ccf411148ceb2ee38e718e9860f5a7", size = 1896209, upload-time = "2026-01-21T16:27:32.159Z" }, - { url = "https://files.pythonhosted.org/packages/28/cc/2103149761fdb4eaed58a53e8437b2d716d48f05174fab1d9fcf1e2a2244/torchvision-0.25.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:146d02c9876858420adf41f3189fe90e3d6a409cbfa65454c09f25fb33bf7266", size = 2310735, upload-time = "2026-01-21T16:27:22.327Z" }, - { url = "https://files.pythonhosted.org/packages/76/ad/f4c985ad52ddd3b22711c588501be1b330adaeaf6850317f66751711b78c/torchvision-0.25.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c4d395cb2c4a2712f6eb93a34476cdf7aae74bb6ea2ea1917f858e96344b00aa", size = 8089557, upload-time = "2026-01-21T16:27:27.666Z" }, - { url = "https://files.pythonhosted.org/packages/63/cc/0ea68b5802e5e3c31f44b307e74947bad5a38cc655231d845534ed50ddb8/torchvision-0.25.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5e6b449e9fa7d642142c0e27c41e5a43b508d57ed8e79b7c0a0c28652da8678c", size = 4344260, upload-time = "2026-01-21T16:27:17.018Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e7/56b47cc3b132aea90ccce22bcb8975dec688b002150012acc842846039d0/torchvision-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c409e1c3fdebec7a3834465086dbda8bf7680eff79abf7fd2f10c6b59520a7a4", size = 1863502, upload-time = "2026-03-23T18:12:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ec/5c31c92c08b65662fe9604a4067ae8232582805949f11ddc042cebe818ed/torchvision-0.26.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:406557718e62fdf10f5706e88d8a5ec000f872da913bf629aab9297622585547", size = 7767944, upload-time = "2026-03-23T18:12:42.805Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d8/cb6ccda1a1f35a6597645818641701207b3e8e13553e75fce5d86bac74b2/torchvision-0.26.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d61a5abb6b42a0c0c311996c2ac4b83a94418a97182c83b055a2a4ae985e05aa", size = 7522205, upload-time = "2026-03-23T18:12:54.654Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a9/c272623a0f735c35f0f6cd6dc74784d4f970e800cf063bb76687895a2ab9/torchvision-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:7993c01648e7c61d191b018e84d38fe0825c8fcb2720cd0f37caf7ba14404aa1", size = 4255155, upload-time = "2026-03-23T18:12:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/0762f77f53605d10c9477be39bb47722cc8e383bbbc2531471ce0e396c07/torchvision-0.26.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5d63dd43162691258b1b3529b9041bac7d54caa37eae0925f997108268cbf7c4", size = 1860809, upload-time = "2026-03-23T18:12:47.629Z" }, + { url = "https://files.pythonhosted.org/packages/e6/81/0b3e58d1478c660a5af4268713486b2df7203f35abd9195fea87348a5178/torchvision-0.26.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a39c7a26538c41fda453f9a9692b5ff9b35a5437db1d94f3027f6f509c160eac", size = 7727494, upload-time = "2026-03-23T18:12:46.062Z" }, + { url = "https://files.pythonhosted.org/packages/b6/dc/d9ab5d29115aa05e12e30f1397a3eeae1d88a511241dc3bce48dc4342675/torchvision-0.26.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b7e6213620bbf97742e5f79832f9e9d769e6cf0f744c5b53dad80b76db633691", size = 7521747, upload-time = "2026-03-23T18:12:36.815Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1b/f1bc86a918c5f6feab1eeff11982e2060f4704332e96185463d27855bdf5/torchvision-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:4280c35ec8cba1fcc8294fb87e136924708726864c379e4c54494797d86bc474", size = 4319880, upload-time = "2026-03-23T18:12:38.168Z" }, + { url = "https://files.pythonhosted.org/packages/66/28/b4ad0a723ed95b003454caffcc41894b34bd8379df340848cae2c33871de/torchvision-0.26.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:358fc4726d0c08615b6d83b3149854f11efb2a564ed1acb6fce882e151412d23", size = 1951973, upload-time = "2026-03-23T18:12:48.781Z" }, + { url = "https://files.pythonhosted.org/packages/71/e2/7a89096e6cf2f3336353b5338ba925e0addf9d8601920340e6bdf47e8eb3/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:3daf9cc149cf3cdcbd4df9c59dae69ffca86c6823250442c3bbfd63fc2e26c61", size = 7728679, upload-time = "2026-03-23T18:12:26.196Z" }, + { url = "https://files.pythonhosted.org/packages/69/1d/4e1eebc17d18ce080a11dcf3df3f8f717f0efdfa00983f06e8ba79259f61/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:82c3965eca27e86a316e31e4c3e5a16d353e0bcbe0ef8efa2e66502c54493c4b", size = 7609138, upload-time = "2026-03-23T18:12:35.327Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a4/f1155e943ae5b32400d7000adc81c79bb0392b16ceb33bcf13e02e48cced/torchvision-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ebc043cc5a4f0bf22e7680806dbba37ffb19e70f6953bbb44ed1a90aeb5c9bea", size = 4248202, upload-time = "2026-03-23T18:12:41.423Z" }, ] [[package]] @@ -2893,7 +2943,7 @@ wheels = [ [[package]] name = "transformers" -version = "5.3.0" +version = "5.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -2906,9 +2956,9 @@ dependencies = [ { name = "tqdm" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/1a/70e830d53ecc96ce69cfa8de38f163712d2b43ac52fbd743f39f56025c31/transformers-5.3.0.tar.gz", hash = "sha256:009555b364029da9e2946d41f1c5de9f15e6b1df46b189b7293f33a161b9c557", size = 8830831, upload-time = "2026-03-04T17:41:46.119Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/e9/c6c80a07690142a7d05444271f47b9f3c8aac7dea01d52e1137ee480ad78/transformers-5.6.2.tar.gz", hash = "sha256:e657134c3e5a6bc00a3c35f4e2674bb51adfcd89898495b788a18552bac2b91a", size = 8311867, upload-time = "2026-04-23T18:33:29.332Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/88/ae8320064e32679a5429a2c9ebbc05c2bf32cefb6e076f9b07f6d685a9b4/transformers-5.3.0-py3-none-any.whl", hash = "sha256:50ac8c89c3c7033444fb3f9f53138096b997ebb70d4b5e50a2e810bf12d3d29a", size = 10661827, upload-time = "2026-03-04T17:41:42.722Z" }, + { url = "https://files.pythonhosted.org/packages/5d/95/0b0218149b0d6f14df35f5b8f676fa83df4f19ed253c3cc447107ef86eca/transformers-5.6.2-py3-none-any.whl", hash = "sha256:f8d3a1bb96778fed9b8aabfd0dd6e19843e4b0f2bb6b59f32b8a92051b0f348f", size = 10364898, upload-time = "2026-04-23T18:33:26.081Z" }, ] [[package]] @@ -2926,7 +2976,7 @@ wheels = [ [[package]] name = "typer" -version = "0.24.1" +version = "0.24.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -2934,9 +2984,9 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/b8/9ebb531b6c2d377af08ac6746a5df3425b21853a5d2260876919b58a2a4a/typer-0.24.2.tar.gz", hash = "sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480", size = 119849, upload-time = "2026-04-22T17:45:34.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/39/d1/9484b497e0a0410b901c12b8251c3e746e1e863f7d28419ffe06f7892fda/typer-0.24.2-py3-none-any.whl", hash = "sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8", size = 55977, upload-time = "2026-04-22T17:45:33.055Z" }, ] [[package]] @@ -2962,11 +3012,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2025.3" +version = "2026.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, + { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, ] [[package]] @@ -2980,20 +3030,20 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.41.0" +version = "0.46.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/ce/eeb58ae4ac36fe09e3842eb02e0eb676bf2c53ae062b98f1b2531673efdd/uvicorn-0.41.0.tar.gz", hash = "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", size = 82633, upload-time = "2026-02-16T23:07:24.1Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, + { url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926, upload-time = "2026-04-23T07:15:58.355Z" }, ] [[package]] name = "virtualenv" -version = "21.2.0" +version = "21.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -3001,9 +3051,9 @@ dependencies = [ { name = "platformdirs" }, { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/98/3a7e644e19cb26133488caff231be390579860bbbb3da35913c49a1d0a46/virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada", size = 5850742, upload-time = "2026-04-14T22:15:31.438Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, + { url = "https://files.pythonhosted.org/packages/27/8d/edd0bd910ff803c308ee9a6b7778621af0d10252219ad9f19ef4d4982a61/virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac", size = 5831232, upload-time = "2026-04-14T22:15:29.342Z" }, ] [[package]] @@ -3017,14 +3067,14 @@ wheels = [ [[package]] name = "werkzeug" -version = "3.1.6" +version = "3.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/f1/ee81806690a87dab5f5653c1f146c92bc066d7f4cebc603ef88eb9e13957/werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", size = 864736, upload-time = "2026-02-19T15:17:18.884Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/ec/d58832f89ede95652fd01f4f24236af7d32b70cab2196dfcc2d2fd13c5c2/werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131", size = 225166, upload-time = "2026-02-19T15:17:17.475Z" }, + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, ] [[package]] @@ -3139,9 +3189,9 @@ wheels = [ [[package]] name = "zipp" -version = "3.23.0" +version = "3.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, ] From da13b0c3e2520f9a4214a57d38e8cbe4966f5e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 12:27:15 +0000 Subject: [PATCH 036/284] feat: separate tile embedding data --- configs/datamodule/datasets/embeddings/labeled.yaml | 2 +- configs/datamodule/datasets/embeddings/unlabeled.yaml | 2 +- ...s_datamodule.yaml => tile_embeddings_datamodule.yaml} | 0 configs/experiment/predict/embeddings/base.yaml | 2 +- configs/experiment/test/embeddings/base.yaml | 2 +- configs/experiment/train/embeddings/base.yaml | 2 +- prostate_cancer/attention_mil_model.py | 1 + prostate_cancer/datamodule/__init__.py | 2 +- .../{embedding_dataset.py => tile_embedding_dataset.py} | 9 ++++----- .../datamodule/{data_module.py => tile_data_module.py} | 0 10 files changed, 11 insertions(+), 11 deletions(-) rename configs/datamodule/{embeddings_datamodule.yaml => tile_embeddings_datamodule.yaml} (100%) rename prostate_cancer/datamodule/datasets/{embedding_dataset.py => tile_embedding_dataset.py} (93%) rename prostate_cancer/datamodule/{data_module.py => tile_data_module.py} (100%) diff --git a/configs/datamodule/datasets/embeddings/labeled.yaml b/configs/datamodule/datasets/embeddings/labeled.yaml index be0aba53..7d6ac31c 100644 --- a/configs/datamodule/datasets/embeddings/labeled.yaml +++ b/configs/datamodule/datasets/embeddings/labeled.yaml @@ -1,4 +1,4 @@ -_target_: prostate_cancer.datamodule.datasets.LabeledEmbeddingsDataset +_target_: prostate_cancer.datamodule.datasets.LabeledTileEmbeddingsDataset uris: ??? thresholds: ${thresholds} embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/unlabeled.yaml b/configs/datamodule/datasets/embeddings/unlabeled.yaml index 618f2a76..72f79123 100644 --- a/configs/datamodule/datasets/embeddings/unlabeled.yaml +++ b/configs/datamodule/datasets/embeddings/unlabeled.yaml @@ -1,4 +1,4 @@ -_target_: prostate_cancer.datamodule.datasets.UnlabeledEmbeddingsDataset +_target_: prostate_cancer.datamodule.datasets.UnlabeledTileEmbeddingsDataset uris: ??? thresholds: ${thresholds} embeddings_uri: ??? diff --git a/configs/datamodule/embeddings_datamodule.yaml b/configs/datamodule/tile_embeddings_datamodule.yaml similarity index 100% rename from configs/datamodule/embeddings_datamodule.yaml rename to configs/datamodule/tile_embeddings_datamodule.yaml diff --git a/configs/experiment/predict/embeddings/base.yaml b/configs/experiment/predict/embeddings/base.yaml index 9f2cc6b0..e7d12d6d 100644 --- a/configs/experiment/predict/embeddings/base.yaml +++ b/configs/experiment/predict/embeddings/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/predict/base - - /datamodule/embeddings_datamodule@datamodule + - /datamodule/tile_embeddings_datamodule@datamodule - _self_ datamodule: diff --git a/configs/experiment/test/embeddings/base.yaml b/configs/experiment/test/embeddings/base.yaml index 48c2d59e..47ba01f4 100644 --- a/configs/experiment/test/embeddings/base.yaml +++ b/configs/experiment/test/embeddings/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/test/base - - /datamodule/embeddings_datamodule@datamodule + - /datamodule/tile_embeddings_datamodule@datamodule - _self_ datamodule: diff --git a/configs/experiment/train/embeddings/base.yaml b/configs/experiment/train/embeddings/base.yaml index c4bbd0a4..39a587a9 100644 --- a/configs/experiment/train/embeddings/base.yaml +++ b/configs/experiment/train/embeddings/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/train/base - - /datamodule/embeddings_datamodule@datamodule + - /datamodule/tile_embeddings_datamodule@datamodule - _self_ datamodule: diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index c50131aa..93f4d342 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -93,6 +93,7 @@ def forward(self, x: Tensor) -> Tensor: return x.squeeze(-1) # (batch_size,) def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: + # bag ~ all embeddings from a single slide bags, labels, _ = batch outputs = self(bags) diff --git a/prostate_cancer/datamodule/__init__.py b/prostate_cancer/datamodule/__init__.py index 299bff3b..d5fa0b21 100644 --- a/prostate_cancer/datamodule/__init__.py +++ b/prostate_cancer/datamodule/__init__.py @@ -1,4 +1,4 @@ -from prostate_cancer.datamodule.data_module import TileDataModule +from prostate_cancer.datamodule.tile_data_module import TileDataModule __all__ = ["TileDataModule"] diff --git a/prostate_cancer/datamodule/datasets/embedding_dataset.py b/prostate_cancer/datamodule/datasets/tile_embedding_dataset.py similarity index 93% rename from prostate_cancer/datamodule/datasets/embedding_dataset.py rename to prostate_cancer/datamodule/datasets/tile_embedding_dataset.py index 73ee6f34..a0913ce9 100644 --- a/prostate_cancer/datamodule/datasets/embedding_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_embedding_dataset.py @@ -21,9 +21,7 @@ T = TypeVar("T", covariant=True) -class EmbeddingsDataset(FilterableDataset[T]): - """This dataset class wraps labeled gigapath features for all the slides in given URIs.""" - +class TileEmbeddingsDataset(FilterableDataset[T]): def __init__( self, uris: Iterable[str], @@ -102,12 +100,13 @@ def _filter_tiles_embeddings_by_slide( return slide_tiles, slide_embeddings -class LabeledEmbeddingsDataset(EmbeddingsDataset[LabeledTileSample]): ... +class LabeledTileEmbeddingsDataset(TileEmbeddingsDataset[LabeledTileSample]): ... -class UnlabeledEmbeddingsDataset(EmbeddingsDataset[UnlabeledTileSample]): ... +class UnlabeledTileEmbeddingsDataset(TileEmbeddingsDataset[UnlabeledTileSample]): ... +# dataset of tile embeddings for a single slide class _TileEmbeddingsSlide(Dataset[LabeledTileSample | UnlabeledTileSample]): def __init__( self, diff --git a/prostate_cancer/datamodule/data_module.py b/prostate_cancer/datamodule/tile_data_module.py similarity index 100% rename from prostate_cancer/datamodule/data_module.py rename to prostate_cancer/datamodule/tile_data_module.py From c0f8eeb5c4c20b1187f1643f6fac8695c20307c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 13:17:57 +0000 Subject: [PATCH 037/284] feat: datamodule strucutre --- .../datamodule/datasets/__init__.py | 16 ++- prostate_cancer/datamodule/datasets/base.py | 47 ++++--- .../datasets/slide_embeddings_dataset.py | 125 ++++++++++++++++++ .../datamodule/datasets/tile_dataset.py | 3 +- ..._dataset.py => tile_embeddings_dataset.py} | 3 +- .../datamodule/samplers/__init__.py | 5 +- ...lide_stratified_weighted_random_sampler.py | 25 ++++ .../stratified_weighted_random_sampler.py | 12 -- .../datamodule/slide_data_module.py | 93 +++++++++++++ .../datamodule/tile_data_module.py | 3 +- 10 files changed, 289 insertions(+), 43 deletions(-) create mode 100644 prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py rename prostate_cancer/datamodule/datasets/{tile_embedding_dataset.py => tile_embeddings_dataset.py} (97%) create mode 100644 prostate_cancer/datamodule/samplers/slide_stratified_weighted_random_sampler.py create mode 100644 prostate_cancer/datamodule/slide_data_module.py diff --git a/prostate_cancer/datamodule/datasets/__init__.py b/prostate_cancer/datamodule/datasets/__init__.py index 40897c10..ea76019e 100644 --- a/prostate_cancer/datamodule/datasets/__init__.py +++ b/prostate_cancer/datamodule/datasets/__init__.py @@ -1,16 +1,22 @@ -from prostate_cancer.datamodule.datasets.embedding_dataset import ( - LabeledEmbeddingsDataset, - UnlabeledEmbeddingsDataset, +from prostate_cancer.datamodule.datasets.slide_embeddings_dataset import ( + LabeledSlideEmbeddingsDataset, + UnlabeledSlideEmbeddingsDataset, ) from prostate_cancer.datamodule.datasets.tile_dataset import ( LabeledTilesDataset, UnlabeledTilesDataset, ) +from prostate_cancer.datamodule.datasets.tile_embeddings_dataset import ( + LabeledTileEmbeddingsDataset, + UnlabeledTileEmbeddingsDataset, +) __all__ = [ - "LabeledEmbeddingsDataset", + "LabeledSlideEmbeddingsDataset", + "LabeledTileEmbeddingsDataset", "LabeledTilesDataset", - "UnlabeledEmbeddingsDataset", + "UnlabeledSlideEmbeddingsDataset", + "UnlabeledTileEmbeddingsDataset", "UnlabeledTilesDataset", ] diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 7c29a7ad..f9cf8774 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -13,6 +13,30 @@ def get_slide_name(slide: pd.Series) -> str: return Path(slide.path).stem +def filter_tiles_by_thresholds( + tiles: pd.DataFrame, thresholds: dict[str, float] +) -> pd.DataFrame: + for percentage in [ + "tissue_roi_percentage", + "exclude_percentage", + "another_pathology_percentage", + "residual_percentage", + "blur_percentage", + "folding_percentage", + ]: + if percentage in tiles.columns: + t = percentage.replace("percentage", "t") + assert t in thresholds, f"{t} for {percentage}" + mask = ( + tiles[percentage] > thresholds[t] + if "tissue" in percentage + else tiles[percentage] <= thresholds[t] + ) + tiles = tiles[mask] + + return tiles + + class FilterableDataset(MetaTiledSlides[T]): """This class abstracts the filtering which is needed for all the datasets.""" @@ -29,30 +53,9 @@ def __init__( self.thresholds = thresholds super().__init__(uris=uris) - def filter_tiles_by_thresholds(self, tiles: pd.DataFrame) -> pd.DataFrame: - for percentage in [ - "tissue_roi_percentage", - "exclude_percentage", - "another_pathology_percentage", - "residual_percentage", - "blur_percentage", - "folding_percentage", - ]: - if percentage in tiles.columns: - t = percentage.replace("percentage", "t") - assert t in self.thresholds, f"{t} for {percentage}" - mask = ( - tiles[percentage] > self.thresholds[t] - if "tissue" in percentage - else tiles[percentage] <= self.thresholds[t] - ) - tiles = tiles[mask] - - return tiles - def prepare_tiles(self, tiles: pd.DataFrame) -> pd.DataFrame: assert self.labeled, "Only allowed for labeled dataset" - tiles = self.filter_tiles_by_thresholds(tiles) + tiles = filter_tiles_by_thresholds(tiles, self.thresholds) tiles["carcinoma"] = tiles["carcinoma_roi_percentage"] > self.carcinoma_roi_t if self.stratified_filter: tiles = self.filter_non_carcinoma(tiles) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py new file mode 100644 index 00000000..c9a5fc42 --- /dev/null +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -0,0 +1,125 @@ +"""These Datasets were taken from Adam Kukučka Ulcerative Colitis project and modified.""" + +from collections.abc import Iterable +from pathlib import Path +from typing import Generic, TypeVar + +import mlflow +import mlflow.artifacts +import pandas as pd +import torch +import torch.nn.functional as F +from torch.utils.data import Dataset + +from prostate_cancer.datamodule.datasets.base import filter_tiles_by_thresholds +from prostate_cancer.typing import ( + LabeledSlideSample, + SlideMetadata, + UnlabeledSlideSample, +) + + +T = TypeVar("T", bound=LabeledSlideSample | UnlabeledSlideSample) + + +class SlideEmbeddingsDataset(Dataset[T], Generic[T]): + def __init__( + self, + thresholds: dict[str, float], + tiling_uris: Iterable[str], + embeddings_uri: str, + padding: bool = True, + include_labels: bool = True, + ) -> None: + self.thresholds = thresholds + self.include_labels = include_labels + self.slides, self.tiles, self.embeddings_folder = self.download_artifacts( + tiling_uris, embeddings_uri + ) + self.padding = padding + self.max_embeddings = self.tiles["slide_id"].value_counts().max() + + def download_artifacts( + self, tiling_uris: Iterable[str], embeddings_uri: str + ) -> tuple[pd.DataFrame, pd.DataFrame, Path]: + slide_dfs = [] + tile_dfs = [] + + for tiling_uri in tiling_uris: + tiling_folder = Path(mlflow.artifacts.download_artifacts(tiling_uri)) + slide_dfs.append(pd.read_parquet(tiling_folder / "slides.parquet")) + tile_dfs.append(pd.read_parquet(tiling_folder / "tiles.parquet")) + + embeddings_dir = Path(mlflow.artifacts.download_artifacts(embeddings_uri)) + + return ( + pd.concat(slide_dfs, ignore_index=True), + pd.concat(tile_dfs, ignore_index=True), + embeddings_dir, + ) + + def __len__(self) -> int: + return len(self.slides) + + def __getitem__(self, idx: int) -> T: + slide_metadata = self.slides.iloc[idx] + slide_name = Path(slide_metadata.path).stem + slide_embeddings = torch.load( + self.embeddings_folder / Path(slide_name).with_suffix(".pt"), + map_location="cpu", + ) + + slide_tiles = self.tiles[self.tiles["slide_id"] == slide_metadata.id] + assert len(slide_embeddings) == len(slide_tiles), "Size mismatch" + filtered_tiles = filter_tiles_by_thresholds(slide_tiles, self.thresholds) + slide_embeddings = slide_embeddings[filtered_tiles.index.tolist()] + + pad_amount = self.max_embeddings - slide_embeddings.shape[0] + if self.padding: + slide_embeddings = F.pad(slide_embeddings, (0, 0, 0, pad_amount), value=0.0) + + metadata = SlideMetadata( + slide_id=slide_metadata["id"], + slide_name=slide_name, + slide_path=Path(slide_metadata["path"]), + ) + + if not self.include_labels: + return slide_embeddings, metadata # type: ignore[return-value] + + label = torch.tensor(slide_metadata.carcinoma).float() + return slide_embeddings, label, metadata # type: ignore[return-value] + + +class LabeledSlideEmbeddingsDataset(SlideEmbeddingsDataset[LabeledSlideSample]): + def __init__( + self, + thresholds: dict[str, float], + tiling_uris: Iterable[str], + embeddings_uri: str, + padding: bool = True, + ) -> None: + super().__init__( + thresholds=thresholds, + tiling_uris=tiling_uris, + embeddings_uri=embeddings_uri, + padding=padding, + include_labels=True, + ) + + +class UnlabeledSlideEmbeddingsDataset(SlideEmbeddingsDataset[UnlabeledSlideSample]): + def __init__( + self, + thresholds: dict[str, float], + tiling_uris: Iterable[str], + embeddings_uri: str, + padding: bool = True, + ) -> None: + super().__init__( + thresholds=thresholds, + tiling_uris=tiling_uris, + embeddings_uri=embeddings_uri, + padding=padding, + include_labels=False, + ) diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_dataset.py index d77fe4be..2d651e69 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_dataset.py @@ -11,6 +11,7 @@ from prostate_cancer.datamodule.datasets.base import ( FilterableDataset, + filter_tiles_by_thresholds, get_slide_name, ) from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample @@ -42,7 +43,7 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: self.tiles = ( self.prepare_tiles(self.tiles) if self.labeled - else self.filter_tiles_by_thresholds(self.tiles) + else filter_tiles_by_thresholds(self.tiles, self.thresholds) ) return ( cast( diff --git a/prostate_cancer/datamodule/datasets/tile_embedding_dataset.py b/prostate_cancer/datamodule/datasets/tile_embeddings_dataset.py similarity index 97% rename from prostate_cancer/datamodule/datasets/tile_embedding_dataset.py rename to prostate_cancer/datamodule/datasets/tile_embeddings_dataset.py index a0913ce9..fc62f071 100644 --- a/prostate_cancer/datamodule/datasets/tile_embedding_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_embeddings_dataset.py @@ -10,6 +10,7 @@ from prostate_cancer.datamodule.datasets.base import ( FilterableDataset, + filter_tiles_by_thresholds, get_slide_name, ) from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample @@ -94,7 +95,7 @@ def _filter_tiles_embeddings_by_slide( slide_tiles = ( self.prepare_tiles(slide_tiles) if self.labeled - else self.filter_tiles_by_thresholds(slide_tiles) + else filter_tiles_by_thresholds(slide_tiles, self.thresholds) ) slide_embeddings = slide_embeddings[slide_tiles.index.tolist()] return slide_tiles, slide_embeddings diff --git a/prostate_cancer/datamodule/samplers/__init__.py b/prostate_cancer/datamodule/samplers/__init__.py index 3dfe8a32..b0dc4288 100644 --- a/prostate_cancer/datamodule/samplers/__init__.py +++ b/prostate_cancer/datamodule/samplers/__init__.py @@ -1,6 +1,9 @@ +from prostate_cancer.datamodule.samplers.slide_stratified_weighted_random_sampler import ( + SlideStratifiedWeightedRandomSampler, +) from prostate_cancer.datamodule.samplers.stratified_weighted_random_sampler import ( StratifiedWeightedRandomSampler, ) -__all__ = ["StratifiedWeightedRandomSampler"] +__all__ = ["SlideStratifiedWeightedRandomSampler", "StratifiedWeightedRandomSampler"] diff --git a/prostate_cancer/datamodule/samplers/slide_stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/slide_stratified_weighted_random_sampler.py new file mode 100644 index 00000000..4019e1b4 --- /dev/null +++ b/prostate_cancer/datamodule/samplers/slide_stratified_weighted_random_sampler.py @@ -0,0 +1,25 @@ +from collections.abc import Sequence + +import pandas as pd +from torch.utils.data import WeightedRandomSampler + +from prostate_cancer.datamodule.datasets import LabeledSlideEmbeddingsDataset + + +class SlideStratifiedWeightedRandomSampler(WeightedRandomSampler): + def __init__( + self, + dataset: LabeledSlideEmbeddingsDataset, + target_col: str, + replacement: bool = True, + ) -> None: + super().__init__( + self._get_weights(dataset.slides, target_col), + num_samples=len(dataset), + replacement=replacement, + ) + + def _get_weights(self, df: pd.DataFrame, target_col: str) -> Sequence[float]: + value_counts = df[target_col].value_counts() + weights = 1 / df[target_col].map(value_counts) + return weights.tolist() diff --git a/prostate_cancer/datamodule/samplers/stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/stratified_weighted_random_sampler.py index c95f17fe..3f3f54ce 100644 --- a/prostate_cancer/datamodule/samplers/stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/stratified_weighted_random_sampler.py @@ -7,18 +7,6 @@ class StratifiedWeightedRandomSampler(WeightedRandomSampler): - """Weighted Random Sampler with weights based on the class distribution. - - This sampler assigns weights to each sample in the dataset based on the inverse of the class frequency. - This ensures that classes with fewer samples are given higher weights, and classes with more samples - are given lower weights. This helps in balancing the dataset during sampling, especially when there - is a class imbalance. - - Arguments: - dataset (ProstateCancer): ProstateCancer dataset - replacement (bool): If True, samples are drawn with replacement. Default is True. - """ - def __init__( self, dataset: LabeledTilesDataset, target_col: str, replacement: bool = True ) -> None: diff --git a/prostate_cancer/datamodule/slide_data_module.py b/prostate_cancer/datamodule/slide_data_module.py new file mode 100644 index 00000000..ddbf5d88 --- /dev/null +++ b/prostate_cancer/datamodule/slide_data_module.py @@ -0,0 +1,93 @@ +from collections.abc import Iterable +from typing import TYPE_CHECKING, cast + +from hydra.utils import instantiate +from lightning import LightningDataModule +from omegaconf import DictConfig +from torch.utils.data import DataLoader + + +if TYPE_CHECKING: + from prostate_cancer.datamodule.datasets import ( + LabeledSlideEmbeddingsDataset, + UnlabeledSlideEmbeddingsDataset, + ) +from prostate_cancer.datamodule.samplers import SlideStratifiedWeightedRandomSampler +from prostate_cancer.typing import ( + LabeledSlideSampleBatch, + UnlabeledSlideSampleBatch, +) + + +class SlideDataModule(LightningDataModule): + def __init__( + self, + batch_size: int, + num_workers: int = 0, + **datasets: DictConfig, + ) -> None: + super().__init__() + self.batch_size = batch_size + self.num_workers = num_workers + self.datasets = datasets + + def setup(self, stage: str) -> None: + match stage: + case "fit": + self.train = cast( + "LabeledSlideEmbeddingsDataset", + instantiate(self.datasets["train"]), + ) + self.val = cast( + "LabeledSlideEmbeddingsDataset", + instantiate(self.datasets["val"]), + ) + case "val": + self.val = cast( + "LabeledSlideEmbeddingsDataset", + instantiate(self.datasets["val"]), + ) + case "test": + self.test = cast( + "LabeledSlideEmbeddingsDataset", + instantiate(self.datasets["test"]), + ) + case "predict": + self.predict = cast( + "UnlabeledSlideEmbeddingsDataset", + instantiate(self.datasets["predict"]), + ) + + def train_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: + return DataLoader( + self.train, + sampler=SlideStratifiedWeightedRandomSampler(self.train, "carinoma"), + batch_size=self.batch_size, + num_workers=self.num_workers, + persistent_workers=self.num_workers > 0, + drop_last=True, + ) + + def val_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: + return DataLoader( + self.val, + batch_size=self.batch_size, + num_workers=self.num_workers, + persistent_workers=self.num_workers > 0, + ) + + def test_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: + return DataLoader( + self.test, + batch_size=self.batch_size, + num_workers=self.num_workers, + persistent_workers=self.num_workers > 0, + ) + + def predict_dataloader(self) -> Iterable[UnlabeledSlideSampleBatch]: + return DataLoader( + self.predict, + batch_size=self.batch_size, + num_workers=self.num_workers, + persistent_workers=self.num_workers > 0, + ) diff --git a/prostate_cancer/datamodule/tile_data_module.py b/prostate_cancer/datamodule/tile_data_module.py index f5cc1801..e34c88c6 100644 --- a/prostate_cancer/datamodule/tile_data_module.py +++ b/prostate_cancer/datamodule/tile_data_module.py @@ -11,6 +11,7 @@ LabeledTileSample, LabeledTileSampleBatch, UnlabeledTileSample, + UnlabeledTileSampleBatch, ) @@ -99,7 +100,7 @@ def test_dataloader(self) -> list[Iterable[LabeledTileSampleBatch]]: for dataset in self.test.datasets ] - def predict_dataloader(self) -> list[Iterable[LabeledTileSampleBatch]]: + def predict_dataloader(self) -> list[Iterable[UnlabeledTileSampleBatch]]: return [ DataLoader( dataset, batch_size=self.batch_size, num_workers=self.num_workers From 6e317c756f7619b0ec4c81f6b2dc977d65d1d7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 13:22:41 +0000 Subject: [PATCH 038/284] feat: datamodule configs --- .../datasets/embeddings/slide_labeled.yaml | 4 ++++ .../datasets/embeddings/slide_unlabeled.yaml | 4 ++++ .../embeddings/{labeled.yaml => tile_labeled.yaml} | 0 .../{unlabeled.yaml => tile_unlabeled.yaml} | 0 configs/datamodule/images_datamodule.yaml | 2 ++ configs/datamodule/images_processor_datamodule.yaml | 2 ++ configs/datamodule/slide_embeddings_datamodule.yaml | 12 ++++++++++++ configs/datamodule/tile_embeddings_datamodule.yaml | 10 ++++++---- prostate_cancer/__main__.py | 2 +- prostate_cancer/datamodule/__init__.py | 4 ++-- 10 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 configs/datamodule/datasets/embeddings/slide_labeled.yaml create mode 100644 configs/datamodule/datasets/embeddings/slide_unlabeled.yaml rename configs/datamodule/datasets/embeddings/{labeled.yaml => tile_labeled.yaml} (100%) rename configs/datamodule/datasets/embeddings/{unlabeled.yaml => tile_unlabeled.yaml} (100%) create mode 100644 configs/datamodule/slide_embeddings_datamodule.yaml diff --git a/configs/datamodule/datasets/embeddings/slide_labeled.yaml b/configs/datamodule/datasets/embeddings/slide_labeled.yaml new file mode 100644 index 00000000..b8891097 --- /dev/null +++ b/configs/datamodule/datasets/embeddings/slide_labeled.yaml @@ -0,0 +1,4 @@ +_target_: prostate_cancer.datamodule.datasets.LabeledSlideEmbeddingsDataset +tiling_uris: ??? +thresholds: ${thresholds} +embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml b/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml new file mode 100644 index 00000000..1c5e479b --- /dev/null +++ b/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml @@ -0,0 +1,4 @@ +_target_: prostate_cancer.datamodule.datasets.UnlabeledSlideEmbeddingsDataset +tiling_uris: ??? +thresholds: ${thresholds} +embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/labeled.yaml b/configs/datamodule/datasets/embeddings/tile_labeled.yaml similarity index 100% rename from configs/datamodule/datasets/embeddings/labeled.yaml rename to configs/datamodule/datasets/embeddings/tile_labeled.yaml diff --git a/configs/datamodule/datasets/embeddings/unlabeled.yaml b/configs/datamodule/datasets/embeddings/tile_unlabeled.yaml similarity index 100% rename from configs/datamodule/datasets/embeddings/unlabeled.yaml rename to configs/datamodule/datasets/embeddings/tile_unlabeled.yaml diff --git a/configs/datamodule/images_datamodule.yaml b/configs/datamodule/images_datamodule.yaml index ecbe5654..a1c42010 100644 --- a/configs/datamodule/images_datamodule.yaml +++ b/configs/datamodule/images_datamodule.yaml @@ -7,6 +7,8 @@ defaults: - transforms@default_transform: normalize - samplers@sampler: stratified_weighted_random +_target_: prostate_cancer.datamodule.TileDataModule + train: transforms: ??? # specified during the training (stain / non-stain augmentations) stratified_filter: true diff --git a/configs/datamodule/images_processor_datamodule.yaml b/configs/datamodule/images_processor_datamodule.yaml index 22c93253..8ca2fe88 100644 --- a/configs/datamodule/images_processor_datamodule.yaml +++ b/configs/datamodule/images_processor_datamodule.yaml @@ -8,6 +8,8 @@ defaults: - transforms@default_transform: augmentations - processor: google_vit +_target_: prostate_cancer.datamodule.TileDataModule + train: transforms: ${datamodule.default_transform} processor: ${datamodule.processor} diff --git a/configs/datamodule/slide_embeddings_datamodule.yaml b/configs/datamodule/slide_embeddings_datamodule.yaml new file mode 100644 index 00000000..845e4e17 --- /dev/null +++ b/configs/datamodule/slide_embeddings_datamodule.yaml @@ -0,0 +1,12 @@ +defaults: + - datasets@train: embeddings/slide_labeled + - datasets@val: embeddings/slide_labeled + - datasets@test: embeddings/slide_labeled + - datasets@predict: embeddings/slide_unlabeled + + - samplers@sampler: slide_stratified_weighted_random + +_target_: prostate_cancer.datamodule.SlideDataModule + +batch_size: ??? +num_workers: ??? diff --git a/configs/datamodule/tile_embeddings_datamodule.yaml b/configs/datamodule/tile_embeddings_datamodule.yaml index 75386d97..62aba12f 100644 --- a/configs/datamodule/tile_embeddings_datamodule.yaml +++ b/configs/datamodule/tile_embeddings_datamodule.yaml @@ -1,11 +1,13 @@ defaults: - - datasets@train: embeddings/labeled - - datasets@val: embeddings/labeled - - datasets@test: embeddings/labeled - - datasets@predict: embeddings/unlabeled + - datasets@train: embeddings/tile_labeled + - datasets@val: embeddings/tile_labeled + - datasets@test: embeddings/tile_labeled + - datasets@predict: embeddings/tile_unlabeled - samplers@sampler: stratified_weighted_random +_target_: prostate_cancer.datamodule.TileDataModule + train: stratified_filter: true val: diff --git a/prostate_cancer/__main__.py b/prostate_cancer/__main__.py index 948a2135..8e2e091c 100644 --- a/prostate_cancer/__main__.py +++ b/prostate_cancer/__main__.py @@ -31,8 +31,8 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: data = hydra.utils.instantiate( config.datamodule, _recursive_=False, # to avoid instantiating all the datasets - _target_=TileDataModule, ) + model = hydra.utils.instantiate( config.model ) # Model target is required in the config file diff --git a/prostate_cancer/datamodule/__init__.py b/prostate_cancer/datamodule/__init__.py index d5fa0b21..6baac630 100644 --- a/prostate_cancer/datamodule/__init__.py +++ b/prostate_cancer/datamodule/__init__.py @@ -1,4 +1,4 @@ from prostate_cancer.datamodule.tile_data_module import TileDataModule +from prostate_cancer.datamodule.slide_data_module import SlideDataModule - -__all__ = ["TileDataModule"] +__all__ = ["TileDataModule", "SlideDataModule"] From e50a2f4c4636fbefb17e27af54b237347721314d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 13:26:02 +0000 Subject: [PATCH 039/284] feat: mil training config --- .../train/slide_embeddings/base.yaml | 19 +++++++++++++++++++ .../train/slide_embeddings/pgp.yaml | 14 ++++++++++++++ .../train/slide_embeddings/virchow2.yaml | 14 ++++++++++++++ .../{embeddings => tile_embeddings}/base.yaml | 0 .../{embeddings => tile_embeddings}/pgp.yaml | 2 +- .../virchow2.yaml | 2 +- 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 configs/experiment/train/slide_embeddings/base.yaml create mode 100644 configs/experiment/train/slide_embeddings/pgp.yaml create mode 100644 configs/experiment/train/slide_embeddings/virchow2.yaml rename configs/experiment/train/{embeddings => tile_embeddings}/base.yaml (100%) rename configs/experiment/train/{embeddings => tile_embeddings}/pgp.yaml (83%) rename configs/experiment/train/{embeddings => tile_embeddings}/virchow2.yaml (84%) diff --git a/configs/experiment/train/slide_embeddings/base.yaml b/configs/experiment/train/slide_embeddings/base.yaml new file mode 100644 index 00000000..b038ff5b --- /dev/null +++ b/configs/experiment/train/slide_embeddings/base.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /experiment/train/base + - /datamodule/slide_embeddings_datamodule@datamodule + - _self_ + +datamodule: + batch_size: 4 + num_workers: 8 + + # both foundation models share tiles (embeddings are specified per model) + train: + uris: + - ${train_data.tiles_uri_224} + + val: + uris: + - ${val_data.tiles_uri_224} diff --git a/configs/experiment/train/slide_embeddings/pgp.yaml b/configs/experiment/train/slide_embeddings/pgp.yaml new file mode 100644 index 00000000..f67aee63 --- /dev/null +++ b/configs/experiment/train/slide_embeddings/pgp.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /experiment/train/slide_embeddings/base + - /model/pgp_model@model + - _self_ + +datamodule: + train: + embeddings_uri: ${train_data.pgp_embeddings_uri} + val: + embeddings_uri: ${val_data.pgp_embeddings_uri} + +model_name: "PGP (MIL)" diff --git a/configs/experiment/train/slide_embeddings/virchow2.yaml b/configs/experiment/train/slide_embeddings/virchow2.yaml new file mode 100644 index 00000000..84fb9acb --- /dev/null +++ b/configs/experiment/train/slide_embeddings/virchow2.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /experiment/train/slide_embeddings/base + - /model/virchow2_model@model + - _self_ + +datamodule: + train: + embeddings_uri: ${train_data.virchow2_embeddings_uri} + val: + embeddings_uri: ${val_data.virchow2_embeddings_uri} + +model_name: "Virchow2 (MIL)" diff --git a/configs/experiment/train/embeddings/base.yaml b/configs/experiment/train/tile_embeddings/base.yaml similarity index 100% rename from configs/experiment/train/embeddings/base.yaml rename to configs/experiment/train/tile_embeddings/base.yaml diff --git a/configs/experiment/train/embeddings/pgp.yaml b/configs/experiment/train/tile_embeddings/pgp.yaml similarity index 83% rename from configs/experiment/train/embeddings/pgp.yaml rename to configs/experiment/train/tile_embeddings/pgp.yaml index 9c9f7267..fd2caf84 100644 --- a/configs/experiment/train/embeddings/pgp.yaml +++ b/configs/experiment/train/tile_embeddings/pgp.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/embeddings/base + - /experiment/train/tile_embeddings/base - /model/pgp_model@model - _self_ diff --git a/configs/experiment/train/embeddings/virchow2.yaml b/configs/experiment/train/tile_embeddings/virchow2.yaml similarity index 84% rename from configs/experiment/train/embeddings/virchow2.yaml rename to configs/experiment/train/tile_embeddings/virchow2.yaml index b3c40ba3..6871ec30 100644 --- a/configs/experiment/train/embeddings/virchow2.yaml +++ b/configs/experiment/train/tile_embeddings/virchow2.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/embeddings/base + - /experiment/train/tile_embeddings/base - /model/virchow2_model@model - _self_ From 14c7a13735f8d2ec360ec2b83d103c1d4e3d5b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 13:50:06 +0000 Subject: [PATCH 040/284] feat: prepare training mil --- .../datasets/embeddings/slide_labeled.yaml | 2 +- .../datasets/embeddings/slide_unlabeled.yaml | 2 +- configs/datamodule/images_datamodule.yaml | 2 +- .../datamodule/images_processor_datamodule.yaml | 2 +- .../slide_stratified_weighted_random.yaml | 3 +++ .../samplers/stratified_weighted_random.yaml | 3 --- .../tile_stratified_weighted_random.yaml | 3 +++ .../datamodule/tile_embeddings_datamodule.yaml | 2 +- .../experiment/train/slide_embeddings/pgp.yaml | 2 +- .../train/slide_embeddings/virchow2.yaml | 2 +- configs/model/pgp_mil_model.yaml | 3 +++ configs/model/virchow2_mil_model.yaml | 3 +++ prostate_cancer/__main__.py | 1 - prostate_cancer/datamodule/__init__.py | 5 +++-- prostate_cancer/datamodule/samplers/__init__.py | 9 ++++++--- ... tile_stratified_weighted_random_sampler.py} | 2 +- prostate_cancer/datamodule/slide_data_module.py | 17 +++++++++++++++-- 17 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 configs/datamodule/samplers/slide_stratified_weighted_random.yaml delete mode 100644 configs/datamodule/samplers/stratified_weighted_random.yaml create mode 100644 configs/datamodule/samplers/tile_stratified_weighted_random.yaml create mode 100644 configs/model/pgp_mil_model.yaml create mode 100644 configs/model/virchow2_mil_model.yaml rename prostate_cancer/datamodule/samplers/{stratified_weighted_random_sampler.py => tile_stratified_weighted_random_sampler.py} (91%) diff --git a/configs/datamodule/datasets/embeddings/slide_labeled.yaml b/configs/datamodule/datasets/embeddings/slide_labeled.yaml index b8891097..31becb7d 100644 --- a/configs/datamodule/datasets/embeddings/slide_labeled.yaml +++ b/configs/datamodule/datasets/embeddings/slide_labeled.yaml @@ -1,4 +1,4 @@ _target_: prostate_cancer.datamodule.datasets.LabeledSlideEmbeddingsDataset -tiling_uris: ??? +uris: ??? thresholds: ${thresholds} embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml b/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml index 1c5e479b..c5605f17 100644 --- a/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml +++ b/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml @@ -1,4 +1,4 @@ _target_: prostate_cancer.datamodule.datasets.UnlabeledSlideEmbeddingsDataset -tiling_uris: ??? +uris: ??? thresholds: ${thresholds} embeddings_uri: ??? diff --git a/configs/datamodule/images_datamodule.yaml b/configs/datamodule/images_datamodule.yaml index a1c42010..a7e5941b 100644 --- a/configs/datamodule/images_datamodule.yaml +++ b/configs/datamodule/images_datamodule.yaml @@ -5,7 +5,7 @@ defaults: - datasets@predict: images/unlabeled - transforms@default_transform: normalize - - samplers@sampler: stratified_weighted_random + - samplers@sampler: tile_stratified_weighted_random _target_: prostate_cancer.datamodule.TileDataModule diff --git a/configs/datamodule/images_processor_datamodule.yaml b/configs/datamodule/images_processor_datamodule.yaml index 8ca2fe88..861b03e9 100644 --- a/configs/datamodule/images_processor_datamodule.yaml +++ b/configs/datamodule/images_processor_datamodule.yaml @@ -4,7 +4,7 @@ defaults: - datasets@test: images/labeled - datasets@predict: images/unlabeled - - samplers@sampler: stratified_weighted_random + - samplers@sampler: tile_stratified_weighted_random - transforms@default_transform: augmentations - processor: google_vit diff --git a/configs/datamodule/samplers/slide_stratified_weighted_random.yaml b/configs/datamodule/samplers/slide_stratified_weighted_random.yaml new file mode 100644 index 00000000..67ce9ece --- /dev/null +++ b/configs/datamodule/samplers/slide_stratified_weighted_random.yaml @@ -0,0 +1,3 @@ +_partial_: true +_target_: prostate_cancer.datamodule.samplers.SlideStratifiedWeightedRandomSampler +replacement: true \ No newline at end of file diff --git a/configs/datamodule/samplers/stratified_weighted_random.yaml b/configs/datamodule/samplers/stratified_weighted_random.yaml deleted file mode 100644 index 11ddd503..00000000 --- a/configs/datamodule/samplers/stratified_weighted_random.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_partial_: true -_target_: prostate_cancer.datamodule.samplers.StratifiedWeightedRandomSampler -replacement: true \ No newline at end of file diff --git a/configs/datamodule/samplers/tile_stratified_weighted_random.yaml b/configs/datamodule/samplers/tile_stratified_weighted_random.yaml new file mode 100644 index 00000000..13427598 --- /dev/null +++ b/configs/datamodule/samplers/tile_stratified_weighted_random.yaml @@ -0,0 +1,3 @@ +_partial_: true +_target_: prostate_cancer.datamodule.samplers.TileStratifiedWeightedRandomSampler +replacement: true \ No newline at end of file diff --git a/configs/datamodule/tile_embeddings_datamodule.yaml b/configs/datamodule/tile_embeddings_datamodule.yaml index 62aba12f..2585d948 100644 --- a/configs/datamodule/tile_embeddings_datamodule.yaml +++ b/configs/datamodule/tile_embeddings_datamodule.yaml @@ -4,7 +4,7 @@ defaults: - datasets@test: embeddings/tile_labeled - datasets@predict: embeddings/tile_unlabeled - - samplers@sampler: stratified_weighted_random + - samplers@sampler: tile_stratified_weighted_random _target_: prostate_cancer.datamodule.TileDataModule diff --git a/configs/experiment/train/slide_embeddings/pgp.yaml b/configs/experiment/train/slide_embeddings/pgp.yaml index f67aee63..25811449 100644 --- a/configs/experiment/train/slide_embeddings/pgp.yaml +++ b/configs/experiment/train/slide_embeddings/pgp.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/train/slide_embeddings/base - - /model/pgp_model@model + - /model/pgp_mil_model@model - _self_ datamodule: diff --git a/configs/experiment/train/slide_embeddings/virchow2.yaml b/configs/experiment/train/slide_embeddings/virchow2.yaml index 84fb9acb..4bf95eca 100644 --- a/configs/experiment/train/slide_embeddings/virchow2.yaml +++ b/configs/experiment/train/slide_embeddings/virchow2.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/train/slide_embeddings/base - - /model/virchow2_model@model + - /model/virchow2_mil_model@model - _self_ datamodule: diff --git a/configs/model/pgp_mil_model.yaml b/configs/model/pgp_mil_model.yaml new file mode 100644 index 00000000..fa50e745 --- /dev/null +++ b/configs/model/pgp_mil_model.yaml @@ -0,0 +1,3 @@ +_target_: prostate_cancer.attention_mil_model.ProstateCancerAttentionMIL +foundation: "pgp" +lr: ??? diff --git a/configs/model/virchow2_mil_model.yaml b/configs/model/virchow2_mil_model.yaml new file mode 100644 index 00000000..bfcb3ff4 --- /dev/null +++ b/configs/model/virchow2_mil_model.yaml @@ -0,0 +1,3 @@ +_target_: prostate_cancer.attention_mil_model.ProstateCancerAttentionMIL +foundation: "virchow2" +lr: ??? diff --git a/prostate_cancer/__main__.py b/prostate_cancer/__main__.py index 8e2e091c..a37ab902 100644 --- a/prostate_cancer/__main__.py +++ b/prostate_cancer/__main__.py @@ -8,7 +8,6 @@ from rationai.mlkit import Trainer, autolog from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger -from prostate_cancer.datamodule import TileDataModule from prostate_cancer.log_title import log_checkpoint_title diff --git a/prostate_cancer/datamodule/__init__.py b/prostate_cancer/datamodule/__init__.py index 6baac630..3c622ea3 100644 --- a/prostate_cancer/datamodule/__init__.py +++ b/prostate_cancer/datamodule/__init__.py @@ -1,4 +1,5 @@ -from prostate_cancer.datamodule.tile_data_module import TileDataModule from prostate_cancer.datamodule.slide_data_module import SlideDataModule +from prostate_cancer.datamodule.tile_data_module import TileDataModule + -__all__ = ["TileDataModule", "SlideDataModule"] +__all__ = ["SlideDataModule", "TileDataModule"] diff --git a/prostate_cancer/datamodule/samplers/__init__.py b/prostate_cancer/datamodule/samplers/__init__.py index b0dc4288..0f8f1fce 100644 --- a/prostate_cancer/datamodule/samplers/__init__.py +++ b/prostate_cancer/datamodule/samplers/__init__.py @@ -1,9 +1,12 @@ from prostate_cancer.datamodule.samplers.slide_stratified_weighted_random_sampler import ( SlideStratifiedWeightedRandomSampler, ) -from prostate_cancer.datamodule.samplers.stratified_weighted_random_sampler import ( - StratifiedWeightedRandomSampler, +from prostate_cancer.datamodule.samplers.tile_stratified_weighted_random_sampler import ( + TileStratifiedWeightedRandomSampler, ) -__all__ = ["SlideStratifiedWeightedRandomSampler", "StratifiedWeightedRandomSampler"] +__all__ = [ + "SlideStratifiedWeightedRandomSampler", + "TileStratifiedWeightedRandomSampler", +] diff --git a/prostate_cancer/datamodule/samplers/stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py similarity index 91% rename from prostate_cancer/datamodule/samplers/stratified_weighted_random_sampler.py rename to prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py index 3f3f54ce..b6927fb1 100644 --- a/prostate_cancer/datamodule/samplers/stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py @@ -6,7 +6,7 @@ from prostate_cancer.datamodule.datasets import LabeledTilesDataset -class StratifiedWeightedRandomSampler(WeightedRandomSampler): +class TileStratifiedWeightedRandomSampler(WeightedRandomSampler): def __init__( self, dataset: LabeledTilesDataset, target_col: str, replacement: bool = True ) -> None: diff --git a/prostate_cancer/datamodule/slide_data_module.py b/prostate_cancer/datamodule/slide_data_module.py index ddbf5d88..92af7a82 100644 --- a/prostate_cancer/datamodule/slide_data_module.py +++ b/prostate_cancer/datamodule/slide_data_module.py @@ -12,7 +12,7 @@ LabeledSlideEmbeddingsDataset, UnlabeledSlideEmbeddingsDataset, ) -from prostate_cancer.datamodule.samplers import SlideStratifiedWeightedRandomSampler + from prostate_cancer.typing import ( LabeledSlideSampleBatch, UnlabeledSlideSampleBatch, @@ -24,12 +24,14 @@ def __init__( self, batch_size: int, num_workers: int = 0, + sampler: DictConfig | None = None, **datasets: DictConfig, ) -> None: super().__init__() self.batch_size = batch_size self.num_workers = num_workers self.datasets = datasets + self.sampler_partial = sampler def setup(self, stage: str) -> None: match stage: @@ -59,9 +61,20 @@ def setup(self, stage: str) -> None: ) def train_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: + + if self.sampler_partial: + sampler = instantiate(self.sampler_partial)( + dataset=self.train, target_col="carcinoma" + ) + shuffle = False + else: + sampler = None + shuffle = True + return DataLoader( self.train, - sampler=SlideStratifiedWeightedRandomSampler(self.train, "carinoma"), + sampler=sampler, + shuffle=shuffle, batch_size=self.batch_size, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, From b74bf20bcdc2e3a76a0e98a4d57b7cfa3393786a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 13:56:08 +0000 Subject: [PATCH 041/284] feat: use uris --- .../datamodule/datasets/slide_embeddings_dataset.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index c9a5fc42..1a2cb710 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -26,7 +26,7 @@ class SlideEmbeddingsDataset(Dataset[T], Generic[T]): def __init__( self, thresholds: dict[str, float], - tiling_uris: Iterable[str], + uris: Iterable[str], embeddings_uri: str, padding: bool = True, include_labels: bool = True, @@ -34,7 +34,7 @@ def __init__( self.thresholds = thresholds self.include_labels = include_labels self.slides, self.tiles, self.embeddings_folder = self.download_artifacts( - tiling_uris, embeddings_uri + uris, embeddings_uri ) self.padding = padding self.max_embeddings = self.tiles["slide_id"].value_counts().max() @@ -95,13 +95,13 @@ class LabeledSlideEmbeddingsDataset(SlideEmbeddingsDataset[LabeledSlideSample]): def __init__( self, thresholds: dict[str, float], - tiling_uris: Iterable[str], + uris: Iterable[str], embeddings_uri: str, padding: bool = True, ) -> None: super().__init__( thresholds=thresholds, - tiling_uris=tiling_uris, + uris=uris, embeddings_uri=embeddings_uri, padding=padding, include_labels=True, @@ -112,13 +112,13 @@ class UnlabeledSlideEmbeddingsDataset(SlideEmbeddingsDataset[UnlabeledSlideSampl def __init__( self, thresholds: dict[str, float], - tiling_uris: Iterable[str], + uris: Iterable[str], embeddings_uri: str, padding: bool = True, ) -> None: super().__init__( thresholds=thresholds, - tiling_uris=tiling_uris, + uris=uris, embeddings_uri=embeddings_uri, padding=padding, include_labels=False, From 9b78335ffa6ba0d1e703debe3375cee34658c82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 24 Apr 2026 18:37:39 +0000 Subject: [PATCH 042/284] feat: update embeds --- configs/data/mmci_tl_test.yaml | 4 ++-- configs/data/mmci_tl_train.yaml | 4 ++-- configs/data/mmci_tl_val.yaml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/data/mmci_tl_test.yaml b/configs/data/mmci_tl_test.yaml index 5fc47839..2f9e2974 100644 --- a/configs/data/mmci_tl_test.yaml +++ b/configs/data/mmci_tl_test.yaml @@ -1,8 +1,8 @@ tiles_uri_512: "mlflow-artifacts:/65/c47cd842fbf14bf399cd4aca43af3127/artifacts/MMCI TL Test 512" tiles_uri_224: "mlflow-artifacts:/65/74c7b6e1a10444688910b8835adf4208/artifacts/MMCI TL Test 224" -pgp_embeddings_uri: "mlflow-artifacts:/65/2442eee3c09140889b02f0b7ae70eead/artifacts/test (224)" -virchow2_embeddings_uri: "mlflow-artifacts:/65/0279038fa2e54c33a5c326cbd147851f/artifacts/MMCI TL Test 224" +pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Test 224" +virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Test 224" metadata_table: "mlflow-artifacts:/65/bfc694d7d3234720bfb699a6504ae7b2/artifacts/mmci_tl_test_data.csv" diff --git a/configs/data/mmci_tl_train.yaml b/configs/data/mmci_tl_train.yaml index 0c9fd9cb..4c31aba6 100644 --- a/configs/data/mmci_tl_train.yaml +++ b/configs/data/mmci_tl_train.yaml @@ -1,8 +1,8 @@ tiles_uri_512: "mlflow-artifacts:/65/b587398845c54e1c84e8b60f11c42b15/artifacts/MMCI TL Train 512" tiles_uri_224: "mlflow-artifacts:/65/6576370c8866436a8816655bf77caf1a/artifacts/MMCI TL Train 224" -pgp_embeddings_uri: "mlflow-artifacts:/65/2442eee3c09140889b02f0b7ae70eead/artifacts/train (224)" -virchow2_embeddings_uri: "mlflow-artifacts:/65/0279038fa2e54c33a5c326cbd147851f/artifacts/MMCI TL Train 224" +pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Train 224" +virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Train 224" metadata_table: "mlflow-artifacts:/65/612ba86ea216490db86fd3e09bacf316/artifacts/mmci_tl_train.csv" diff --git a/configs/data/mmci_tl_val.yaml b/configs/data/mmci_tl_val.yaml index acc64764..ec305e0a 100644 --- a/configs/data/mmci_tl_val.yaml +++ b/configs/data/mmci_tl_val.yaml @@ -1,8 +1,8 @@ tiles_uri_512: "mlflow-artifacts:/65/a43da470f89848abb3a32e59c504ee1a/artifacts/MMCI TL Val 512" tiles_uri_224: "mlflow-artifacts:/65/2b313f9f1db04ba5803e9093677bb404/artifacts/MMCI TL Val 224" -pgp_embeddings_uri: "mlflow-artifacts:/65/2442eee3c09140889b02f0b7ae70eead/artifacts/val (224)" -virchow2_embeddings_uri: "mlflow-artifacts:/65/0279038fa2e54c33a5c326cbd147851f/artifacts/MMCI TL Val 224" +pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Val 224" +virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Val 224" metadata_table: "mlflow-artifacts:/65/fc8b5da06d644e8ab0535f71f798a0e8/artifacts/mmci_tl_val.csv" From 2efe6636675b2626b8efb8fb0cbfc95e51753e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 26 Apr 2026 06:30:29 +0000 Subject: [PATCH 043/284] fix: index --- prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index 1a2cb710..29accbb8 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -69,7 +69,7 @@ def __getitem__(self, idx: int) -> T: map_location="cpu", ) - slide_tiles = self.tiles[self.tiles["slide_id"] == slide_metadata.id] + slide_tiles = self.tiles[self.tiles["slide_id"] == slide_metadata.id].reset_index(drop=True) assert len(slide_embeddings) == len(slide_tiles), "Size mismatch" filtered_tiles = filter_tiles_by_thresholds(slide_tiles, self.thresholds) slide_embeddings = slide_embeddings[filtered_tiles.index.tolist()] From ee6bbce7a0c3f032fc10c608426eb68888053d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 26 Apr 2026 11:22:20 +0000 Subject: [PATCH 044/284] fix: collate --- .../datamodule/datasets/slide_embeddings_dataset.py | 2 +- prostate_cancer/typing.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index 29accbb8..7fa833a4 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -81,7 +81,7 @@ def __getitem__(self, idx: int) -> T: metadata = SlideMetadata( slide_id=slide_metadata["id"], slide_name=slide_name, - slide_path=Path(slide_metadata["path"]), + slide_path=slide_metadata["path"], ) if not self.include_labels: diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 67f2609b..8209160f 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -26,13 +26,13 @@ class TileMetadataBatch(TypedDict): class SlideMetadata(TypedDict): slide_id: str slide_name: str - slide_path: Path + slide_path: str class SlideMetadataBatch(TypedDict): slide_id: list[str] slide_name: list[str] - slide_path: list[Path] + slide_path: list[str] LabeledSlideSample = tuple[Tensor, Tensor, TileMetadata] From 14a3368cb60ff17e4964f18743855edcf1530555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Apr 2026 06:04:06 +0000 Subject: [PATCH 045/284] feat: increase epochs --- configs/experiment/train/base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/train/base.yaml b/configs/experiment/train/base.yaml index d4638218..6545a6c1 100644 --- a/configs/experiment/train/base.yaml +++ b/configs/experiment/train/base.yaml @@ -7,7 +7,7 @@ defaults: trainer: enable_checkpointing: True - max_epochs: 20 + max_epochs: 50 log_every_n_steps: 5 val_check_interval: 0.25 From 1e9de83066f6804ac2160c84299258631385e751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Apr 2026 06:16:33 +0000 Subject: [PATCH 046/284] feat: replace linear with embedding classifier --- prostate_cancer/attention_mil_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 93f4d342..4b9a7d1a 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -18,7 +18,7 @@ ) from prostate_cancer.typing import LabeledSlideSampleBatch, UnlabeledSlideSampleBatch - +from prostate_cancer.modeling.decode_head import BinaryEmbeddingClassifier class ProstateCancerAttentionMIL(LightningModule): def __init__(self, foundation: str, lr: float) -> None: @@ -38,7 +38,7 @@ def __init__(self, foundation: str, lr: float) -> None: nn.Linear(512, 1), ) - self.classifier = nn.Linear(input_dim, 1) + self.classifier = BinaryEmbeddingClassifier(input_dim) self.criterion = nn.BCELoss() self.lr = lr From feb8e6aad0c3dbf30d4a7599c9314d87b6e6752a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Apr 2026 08:01:00 +0000 Subject: [PATCH 047/284] feat: revert to linear --- prostate_cancer/attention_mil_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 4b9a7d1a..93f4d342 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -18,7 +18,7 @@ ) from prostate_cancer.typing import LabeledSlideSampleBatch, UnlabeledSlideSampleBatch -from prostate_cancer.modeling.decode_head import BinaryEmbeddingClassifier + class ProstateCancerAttentionMIL(LightningModule): def __init__(self, foundation: str, lr: float) -> None: @@ -38,7 +38,7 @@ def __init__(self, foundation: str, lr: float) -> None: nn.Linear(512, 1), ) - self.classifier = BinaryEmbeddingClassifier(input_dim) + self.classifier = nn.Linear(input_dim, 1) self.criterion = nn.BCELoss() self.lr = lr From 2a15dd48df2781e2f8b0033d775c76a916c02f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Apr 2026 08:41:28 +0000 Subject: [PATCH 048/284] feat: mil pred callback --- prostate_cancer/attention_mil_model.py | 27 ++++--- .../callbacks/mil_prediction_callback.py | 79 +++++++++++++++++++ .../datasets/slide_embeddings_dataset.py | 6 +- prostate_cancer/typing.py | 12 ++- 4 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 prostate_cancer/callbacks/mil_prediction_callback.py diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 93f4d342..ae783bb1 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -17,7 +17,11 @@ Specificity, ) -from prostate_cancer.typing import LabeledSlideSampleBatch, UnlabeledSlideSampleBatch +from prostate_cancer.typing import ( + LabeledSlideSampleBatch, + MILModelOutput, + UnlabeledSlideSampleBatch, +) class ProstateCancerAttentionMIL(LightningModule): @@ -55,7 +59,7 @@ def __init__(self, foundation: str, lr: float) -> None: self.val_metrics = MetricCollection(deepcopy(metrics), prefix="validation/") self.test_metrics = MetricCollection(deepcopy(metrics), prefix="test/") - def forward(self, x: Tensor) -> Tensor: + def forward(self, x: Tensor) -> MILModelOutput: # x has shape (batch_size, num_tiles_padded, embedding_dim) # Just identity @@ -84,19 +88,22 @@ def forward(self, x: Tensor) -> Tensor: ) # (batch_size, num_tiles_padded, 1) # TL predictions - x = self.classifier(x) # (batch_size, num_tiles_padded, 1) + tl_preds = self.classifier(x) # (batch_size, num_tiles_padded, 1) # weight TL predictions with attention - x = torch.sum(attention_weights * x, dim=1) # (batch_size, 1) - x = x.sigmoid() # (batch_size, 1) + sl_pred_raw = torch.sum(attention_weights * tl_preds, dim=1) # (batch_size, 1) + sl_pred = sl_pred_raw.sigmoid() # (batch_size, 1) - return x.squeeze(-1) # (batch_size,) + return ( + sl_pred.squeeze(-1), + tl_preds, + ) # (batch_size,), (batch_size, num_tiles_padded, 1) def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide bags, labels, _ = batch - outputs = self(bags) + outputs, _ = self(bags) loss = self.criterion(outputs, labels) self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) @@ -110,7 +117,7 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: def validation_step(self, batch: LabeledSlideSampleBatch) -> None: bags, labels, _ = batch - outputs = self(bags) + outputs, _ = self(bags) loss = self.criterion(outputs, labels) self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) @@ -122,7 +129,7 @@ def validation_step(self, batch: LabeledSlideSampleBatch) -> None: def test_step(self, batch: LabeledSlideSampleBatch) -> None: bags, labels, _ = batch - outputs = self(bags) + outputs, _ = self(bags) self.test_metrics.update(outputs, labels) self.log_dict( @@ -131,7 +138,7 @@ def test_step(self, batch: LabeledSlideSampleBatch) -> None: return outputs - def predict_step(self, batch: UnlabeledSlideSampleBatch) -> Tensor: + def predict_step(self, batch: UnlabeledSlideSampleBatch) -> MILModelOutput: return self(batch[0]) def configure_optimizers(self) -> Optimizer: diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py new file mode 100644 index 00000000..f6d6612f --- /dev/null +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -0,0 +1,79 @@ +from pathlib import Path +from typing import TYPE_CHECKING, cast + +import mlflow +import pandas as pd +from lightning import Callback, LightningModule, Trainer +from rationai.masks.mask_builders import ScalarMaskBuilder +from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger + +from prostate_cancer.typing import MILModelOutput, UnlabeledSlideSampleBatch + + +if TYPE_CHECKING: + from prostate_cancer.datamodule import SlideDataModule + + +class MILPredictionCallback(Callback): + def get_mask_builder(self, slide_name: str, trainer: Trainer) -> ScalarMaskBuilder: + datamodule = cast("SlideDataModule", trainer.datamodule) + slides = cast("pd.DataFrame", datamodule.predict.slides) + slides["name"] = slides["path"].apply(lambda x: Path(x).stem) + + _slide = slides[slides["name"] == slide_name] + assert len(_slide) == 1 + slide = _slide.iloc[0] + + kwargs = { + "save_dir": Path("heatmaps"), + "filename": Path(slide.path).stem, + "extent_x": slide.extent_x, + "extent_y": slide.extent_y, + "mpp_x": slide.mpp_x, + "mpp_y": slide.mpp_y, + "extent_tile": slide.tile_extent_x, + "stride": slide.stride_x, + } + + return ScalarMaskBuilder(**kwargs) + + def on_predict_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, + batch: UnlabeledSlideSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + assert isinstance(trainer.logger, MLFlowLogger) + sl_preds, tl_preds = outputs + + # Log SL predictions + trainer.logger.log_table( + { + "slide": [m["slide_name"] for m in batch[1]], + "prediction": sl_preds.tolist(), + }, + artifact_file="tables/sl_predictions.json", + ) + + # Log TL predictions + slides_embeddings, metadata_batch = batch + for slide_embeddings, xs, ys, slide_name, tl_preds_slide in zip( + slides_embeddings, + metadata_batch["xs"], + metadata_batch["ys"], + metadata_batch["slide_name"], + tl_preds, + strict=True, + ): + mask_builder = self.get_mask_builder(slide_name, trainer) + slide_embeddings = slide_embeddings[ + : len(xs) + ] # take only real tiles (not padding) + mask_builder.update(tl_preds_slide, xs, ys) + + mlflow.log_artifact( + str(mask_builder.save()), artifact_path=str(mask_builder.save_dir) + ) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index 7fa833a4..d2a0bd2b 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -69,7 +69,9 @@ def __getitem__(self, idx: int) -> T: map_location="cpu", ) - slide_tiles = self.tiles[self.tiles["slide_id"] == slide_metadata.id].reset_index(drop=True) + slide_tiles = self.tiles[ + self.tiles["slide_id"] == slide_metadata.id + ].reset_index(drop=True) assert len(slide_embeddings) == len(slide_tiles), "Size mismatch" filtered_tiles = filter_tiles_by_thresholds(slide_tiles, self.thresholds) slide_embeddings = slide_embeddings[filtered_tiles.index.tolist()] @@ -82,6 +84,8 @@ def __getitem__(self, idx: int) -> T: slide_id=slide_metadata["id"], slide_name=slide_name, slide_path=slide_metadata["path"], + x=torch.from_numpy(filtered_tiles["x"].to_numpy()), + y=torch.from_numpy(filtered_tiles["y"].to_numpy()), ) if not self.include_labels: diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 8209160f..f46ee588 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -1,4 +1,3 @@ -from pathlib import Path from typing import TypeAlias, TypedDict from torch import Tensor @@ -27,16 +26,23 @@ class SlideMetadata(TypedDict): slide_id: str slide_name: str slide_path: str + xs: Tensor + ys: Tensor class SlideMetadataBatch(TypedDict): slide_id: list[str] slide_name: list[str] slide_path: list[str] + n_tiles: list[int] + xs: Tensor + ys: Tensor LabeledSlideSample = tuple[Tensor, Tensor, TileMetadata] UnlabeledSlideSample = tuple[Tensor, TileMetadata] -LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, TileMetadataBatch] -UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, TileMetadataBatch] +LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, SlideMetadataBatch] +UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, SlideMetadataBatch] + +MILModelOutput = tuple[Tensor, Tensor] # SL preds, TL preds From 8d1e86688b4be1ac568868015d1c2dce87e636e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Apr 2026 08:53:46 +0000 Subject: [PATCH 049/284] feat: mil callback configs --- configs/callbacks/mil_prediction_callback.yaml | 2 ++ .../experiment/predict/slide_embeddings/base.yaml | 14 ++++++++++++++ .../predict/slide_embeddings/pgp/base.yaml | 11 +++++++++++ .../slide_embeddings/pgp/mil_predictions/base.yaml | 8 ++++++++ .../pgp/mil_predictions/mmci_tl_val.yaml | 6 ++++++ .../predict/slide_embeddings/virchow2/base.yaml | 11 +++++++++++ .../virchow2/mil_predictions/base.yaml | 8 ++++++++ .../virchow2/mil_predictions/mmci_tl_val.yaml | 6 ++++++ configs/model/checkpoints/virchow2_mil.yaml | 2 ++ prostate_cancer/callbacks/__init__.py | 3 ++- .../callbacks/mil_prediction_callback.py | 7 +++++-- .../datasets/slide_embeddings_dataset.py | 4 ++-- 12 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 configs/callbacks/mil_prediction_callback.yaml create mode 100644 configs/experiment/predict/slide_embeddings/base.yaml create mode 100644 configs/experiment/predict/slide_embeddings/pgp/base.yaml create mode 100644 configs/experiment/predict/slide_embeddings/pgp/mil_predictions/base.yaml create mode 100644 configs/experiment/predict/slide_embeddings/pgp/mil_predictions/mmci_tl_val.yaml create mode 100644 configs/experiment/predict/slide_embeddings/virchow2/base.yaml create mode 100644 configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml create mode 100644 configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml create mode 100644 configs/model/checkpoints/virchow2_mil.yaml diff --git a/configs/callbacks/mil_prediction_callback.yaml b/configs/callbacks/mil_prediction_callback.yaml new file mode 100644 index 00000000..9651a8a1 --- /dev/null +++ b/configs/callbacks/mil_prediction_callback.yaml @@ -0,0 +1,2 @@ +mil_prediction_callback: + _target_: prostate_cancer.callbacks.MILPredictionCallback \ No newline at end of file diff --git a/configs/experiment/predict/slide_embeddings/base.yaml b/configs/experiment/predict/slide_embeddings/base.yaml new file mode 100644 index 00000000..98a29bb1 --- /dev/null +++ b/configs/experiment/predict/slide_embeddings/base.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /experiment/predict/base + - /datamodule/slide_embeddings_datamodule@datamodule + - _self_ + +datamodule: + batch_size: 4 + num_workers: 8 + + predict: + uris: + - ${predict_data.tiles_uri_224} diff --git a/configs/experiment/predict/slide_embeddings/pgp/base.yaml b/configs/experiment/predict/slide_embeddings/pgp/base.yaml new file mode 100644 index 00000000..ab464e53 --- /dev/null +++ b/configs/experiment/predict/slide_embeddings/pgp/base.yaml @@ -0,0 +1,11 @@ +# @package _global_ + +defaults: + - /experiment/predict/slide_embeddings/base + - /model/pgp_mil_model@model + - /model/checkpoints/pgp_mil@model_data + - _self_ + +datamodule: + predict: + embeddings_uri: ${predict_data.pgp_embeddings_uri} diff --git a/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/base.yaml b/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/base.yaml new file mode 100644 index 00000000..2919eb71 --- /dev/null +++ b/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/mil_predictions@callbacks + - /experiment/predict/slide_embeddings/pgp/base + - _self_ + +use_case: "MIL predictions" diff --git a/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/mmci_tl_val.yaml new file mode 100644 index 00000000..4e143054 --- /dev/null +++ b/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/mmci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/slide_embeddings/pgp/mil_predictions/base + - /data/mmci_tl_val@predict_data + - _self_ diff --git a/configs/experiment/predict/slide_embeddings/virchow2/base.yaml b/configs/experiment/predict/slide_embeddings/virchow2/base.yaml new file mode 100644 index 00000000..66e3cb9d --- /dev/null +++ b/configs/experiment/predict/slide_embeddings/virchow2/base.yaml @@ -0,0 +1,11 @@ +# @package _global_ + +defaults: + - /experiment/predict/slide_embeddings/base + - /model/virchow2_mil_model@model + - /model/checkpoints/virchow2_mil@model_data + - _self_ + +datamodule: + predict: + embeddings_uri: ${predict_data.virchow2_embeddings_uri} diff --git a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml new file mode 100644 index 00000000..f49408c1 --- /dev/null +++ b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/mil_predictions@callbacks + - /experiment/predict/slide_embeddings/virchow2/base + - _self_ + +use_case: "MIL predictions" diff --git a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml new file mode 100644 index 00000000..f79a765e --- /dev/null +++ b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/slide_embeddings/virchow2/mil_predictions/base + - /data/mmci_tl_val@predict_data + - _self_ diff --git a/configs/model/checkpoints/virchow2_mil.yaml b/configs/model/checkpoints/virchow2_mil.yaml new file mode 100644 index 00000000..761f9d25 --- /dev/null +++ b/configs/model/checkpoints/virchow2_mil.yaml @@ -0,0 +1,2 @@ +checkpoint: "mlflow-artifacts:/65/a649871245c040a39022206cbc9471f1/artifacts/checkpoints/epoch=0-step=135/checkpoint.ckpt" +model_name: "Virchow2 (MIL)" diff --git a/prostate_cancer/callbacks/__init__.py b/prostate_cancer/callbacks/__init__.py index d699a432..bb043208 100644 --- a/prostate_cancer/callbacks/__init__.py +++ b/prostate_cancer/callbacks/__init__.py @@ -11,7 +11,7 @@ from prostate_cancer.callbacks.nested_metrics_callback import NestedMetricsCallback from prostate_cancer.callbacks.num_positive_callback import NumPositiveCallback from prostate_cancer.callbacks.tile_histograms_callback import TileHistogramsCallback - +from prostate_cancer.callbacks.mil_prediction_callback import MILPredictionCallback __all__ = [ "AggregatorCallback", @@ -23,4 +23,5 @@ "NestedMetricsCallback", "NumPositiveCallback", "TileHistogramsCallback", + "MILPredictionCallback", ] diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index f6d6612f..0570655b 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -16,6 +16,9 @@ class MILPredictionCallback(Callback): def get_mask_builder(self, slide_name: str, trainer: Trainer) -> ScalarMaskBuilder: + if not hasattr(trainer, "datamodule"): + raise ValueError("Trainer should have datamodule attribute") + datamodule = cast("SlideDataModule", trainer.datamodule) slides = cast("pd.DataFrame", datamodule.predict.slides) slides["name"] = slides["path"].apply(lambda x: Path(x).stem) @@ -48,18 +51,18 @@ def on_predict_batch_end( ) -> None: assert isinstance(trainer.logger, MLFlowLogger) sl_preds, tl_preds = outputs + slides_embeddings, metadata_batch = batch # Log SL predictions trainer.logger.log_table( { - "slide": [m["slide_name"] for m in batch[1]], + "slide": [name for name in metadata_batch["slide_name"]], "prediction": sl_preds.tolist(), }, artifact_file="tables/sl_predictions.json", ) # Log TL predictions - slides_embeddings, metadata_batch = batch for slide_embeddings, xs, ys, slide_name, tl_preds_slide in zip( slides_embeddings, metadata_batch["xs"], diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index d2a0bd2b..b3687cfa 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -84,8 +84,8 @@ def __getitem__(self, idx: int) -> T: slide_id=slide_metadata["id"], slide_name=slide_name, slide_path=slide_metadata["path"], - x=torch.from_numpy(filtered_tiles["x"].to_numpy()), - y=torch.from_numpy(filtered_tiles["y"].to_numpy()), + xs=torch.from_numpy(filtered_tiles["x"].to_numpy()), + ys=torch.from_numpy(filtered_tiles["y"].to_numpy()), ) if not self.include_labels: From e316aaea9b1790fbf5ff7f8a3a85a878a9c7bc7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 29 Apr 2026 06:41:08 +0000 Subject: [PATCH 050/284] fix: name --- .../predict/slide_embeddings/virchow2/mil_predictions/base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml index f49408c1..013014d2 100644 --- a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml +++ b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /callbacks/mil_predictions@callbacks + - /callbacks/mil_prediction_callback@callbacks - /experiment/predict/slide_embeddings/virchow2/base - _self_ From ddbbc8b2e2fd8d2712478db12db9034c0a541a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 08:30:07 +0000 Subject: [PATCH 051/284] feat: nest model --- configs/experiment/predict/slide_embeddings/virchow2/base.yaml | 2 +- configs/model/checkpoints/{ => mmci_tl}/virchow2_mil.yaml | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename configs/model/checkpoints/{ => mmci_tl}/virchow2_mil.yaml (100%) diff --git a/configs/experiment/predict/slide_embeddings/virchow2/base.yaml b/configs/experiment/predict/slide_embeddings/virchow2/base.yaml index 66e3cb9d..3dc72276 100644 --- a/configs/experiment/predict/slide_embeddings/virchow2/base.yaml +++ b/configs/experiment/predict/slide_embeddings/virchow2/base.yaml @@ -3,7 +3,7 @@ defaults: - /experiment/predict/slide_embeddings/base - /model/virchow2_mil_model@model - - /model/checkpoints/virchow2_mil@model_data + - /model/checkpoints/mmci_tl/virchow2_mil@model_data - _self_ datamodule: diff --git a/configs/model/checkpoints/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml similarity index 100% rename from configs/model/checkpoints/virchow2_mil.yaml rename to configs/model/checkpoints/mmci_tl/virchow2_mil.yaml From 289e48537531da22614b311627b2369528eb4be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 08:39:20 +0000 Subject: [PATCH 052/284] fix: path --- configs/experiment/predict/slide_embeddings/base.yaml | 4 ++-- .../virchow2/mil_predictions/mmci_tl_val.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/experiment/predict/slide_embeddings/base.yaml b/configs/experiment/predict/slide_embeddings/base.yaml index 98a29bb1..df6b04ea 100644 --- a/configs/experiment/predict/slide_embeddings/base.yaml +++ b/configs/experiment/predict/slide_embeddings/base.yaml @@ -6,8 +6,8 @@ defaults: - _self_ datamodule: - batch_size: 4 - num_workers: 8 + batch_size: 2 + num_workers: 4 predict: uris: diff --git a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml index f79a765e..f08ac4b0 100644 --- a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml +++ b/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml @@ -2,5 +2,5 @@ defaults: - /experiment/predict/slide_embeddings/virchow2/mil_predictions/base - - /data/mmci_tl_val@predict_data + - /data/mmci/tl_val@predict_data - _self_ From dfdc710628859cc14560129ceb0b3e1fa819c045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 08:41:10 +0000 Subject: [PATCH 053/284] fix: add stats --- configs/model/checkpoints/mmci_tl/virchow2_mil.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml index 761f9d25..3ae9869d 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml @@ -1,2 +1,6 @@ +defaults: + - base + - _self_ + checkpoint: "mlflow-artifacts:/65/a649871245c040a39022206cbc9471f1/artifacts/checkpoints/epoch=0-step=135/checkpoint.ckpt" model_name: "Virchow2 (MIL)" From ed1da4464971b057a4c1b5af072e70da47f27428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 08:45:16 +0000 Subject: [PATCH 054/284] feat: do not enforce tl threshold --- configs/experiment/predict/base.yaml | 2 -- configs/experiment/predict/embeddings/base.yaml | 5 +++++ configs/experiment/predict/images/base.yaml | 6 +++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/configs/experiment/predict/base.yaml b/configs/experiment/predict/base.yaml index 06324291..6b043e6c 100644 --- a/configs/experiment/predict/base.yaml +++ b/configs/experiment/predict/base.yaml @@ -2,9 +2,7 @@ model: lr: 0 - tl_threshold: ${tile_threshold} -tile_threshold: ${model_data.tile_threshold} checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} thresholds: ${predict_data.thresholds} diff --git a/configs/experiment/predict/embeddings/base.yaml b/configs/experiment/predict/embeddings/base.yaml index e7d12d6d..e216fc56 100644 --- a/configs/experiment/predict/embeddings/base.yaml +++ b/configs/experiment/predict/embeddings/base.yaml @@ -12,3 +12,8 @@ datamodule: predict: uris: - ${predict_data.tiles_uri_224} + +model: + tl_threshold: ${tile_threshold} + +tile_threshold: ${model_data.tile_threshold} diff --git a/configs/experiment/predict/images/base.yaml b/configs/experiment/predict/images/base.yaml index 5950c093..cfc96a0d 100644 --- a/configs/experiment/predict/images/base.yaml +++ b/configs/experiment/predict/images/base.yaml @@ -6,7 +6,6 @@ defaults: - _self_ - datamodule: batch_size: 64 num_workers: 8 @@ -14,3 +13,8 @@ datamodule: predict: uris: - ${predict_data.tiles_uri_512} + +model: + tl_threshold: ${tile_threshold} + +tile_threshold: ${model_data.tile_threshold} From 73fce9f8eaa42c62cbc85ea566bef5b1217832e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 09:11:37 +0000 Subject: [PATCH 055/284] chore: debug --- prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index b3687cfa..c66382f5 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -77,6 +77,7 @@ def __getitem__(self, idx: int) -> T: slide_embeddings = slide_embeddings[filtered_tiles.index.tolist()] pad_amount = self.max_embeddings - slide_embeddings.shape[0] + print("PADDING", pad_amount) if self.padding: slide_embeddings = F.pad(slide_embeddings, (0, 0, 0, pad_amount), value=0.0) From c71fe768d57e854c88f948940d4c03c4b75a8549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 10:03:05 +0000 Subject: [PATCH 056/284] feat: another debug --- prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index c66382f5..78564aed 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -77,10 +77,10 @@ def __getitem__(self, idx: int) -> T: slide_embeddings = slide_embeddings[filtered_tiles.index.tolist()] pad_amount = self.max_embeddings - slide_embeddings.shape[0] - print("PADDING", pad_amount) if self.padding: slide_embeddings = F.pad(slide_embeddings, (0, 0, 0, pad_amount), value=0.0) + print("PADDING", pad_amount, len(slide_embeddings), self.max_embeddings) metadata = SlideMetadata( slide_id=slide_metadata["id"], slide_name=slide_name, From a0d7312b5b31dc42af81070f68de1620f8a83e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 10:11:23 +0000 Subject: [PATCH 057/284] chore: debug --- prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index 78564aed..8bc8c04b 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -80,7 +80,7 @@ def __getitem__(self, idx: int) -> T: if self.padding: slide_embeddings = F.pad(slide_embeddings, (0, 0, 0, pad_amount), value=0.0) - print("PADDING", pad_amount, len(slide_embeddings), self.max_embeddings) + print("PADDING", pad_amount, self.max_embeddings, slide_embeddings.shape) metadata = SlideMetadata( slide_id=slide_metadata["id"], slide_name=slide_name, From 8d51cbb85400e0a0bf49d83cd4f2050e23c7e53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 10:52:17 +0000 Subject: [PATCH 058/284] feat: custom collate --- prostate_cancer/callbacks/__init__.py | 5 +-- .../callbacks/mil_prediction_callback.py | 23 +++++-------- .../datasets/slide_embeddings_dataset.py | 1 - .../datamodule/slide_data_module.py | 33 +++++++++++++++++++ prostate_cancer/typing.py | 17 +++------- 5 files changed, 48 insertions(+), 31 deletions(-) diff --git a/prostate_cancer/callbacks/__init__.py b/prostate_cancer/callbacks/__init__.py index bb043208..b2b3d3e8 100644 --- a/prostate_cancer/callbacks/__init__.py +++ b/prostate_cancer/callbacks/__init__.py @@ -8,10 +8,11 @@ EstimationCallback, ) from prostate_cancer.callbacks.heatmap_callback import HeatmapCallback +from prostate_cancer.callbacks.mil_prediction_callback import MILPredictionCallback from prostate_cancer.callbacks.nested_metrics_callback import NestedMetricsCallback from prostate_cancer.callbacks.num_positive_callback import NumPositiveCallback from prostate_cancer.callbacks.tile_histograms_callback import TileHistogramsCallback -from prostate_cancer.callbacks.mil_prediction_callback import MILPredictionCallback + __all__ = [ "AggregatorCallback", @@ -20,8 +21,8 @@ "CurvesCallback", "EstimationCallback", "HeatmapCallback", + "MILPredictionCallback", "NestedMetricsCallback", "NumPositiveCallback", "TileHistogramsCallback", - "MILPredictionCallback", ] diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 0570655b..611e4038 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -51,31 +51,24 @@ def on_predict_batch_end( ) -> None: assert isinstance(trainer.logger, MLFlowLogger) sl_preds, tl_preds = outputs - slides_embeddings, metadata_batch = batch + _, metadata_batch = batch # Log SL predictions trainer.logger.log_table( { - "slide": [name for name in metadata_batch["slide_name"]], + "slide": [metadata["slide_name"] for metadata in metadata_batch], "prediction": sl_preds.tolist(), }, artifact_file="tables/sl_predictions.json", ) # Log TL predictions - for slide_embeddings, xs, ys, slide_name, tl_preds_slide in zip( - slides_embeddings, - metadata_batch["xs"], - metadata_batch["ys"], - metadata_batch["slide_name"], - tl_preds, - strict=True, - ): - mask_builder = self.get_mask_builder(slide_name, trainer) - slide_embeddings = slide_embeddings[ - : len(xs) - ] # take only real tiles (not padding) - mask_builder.update(tl_preds_slide, xs, ys) + for metadata, tl_preds_slide in zip(metadata_batch, tl_preds, strict=True): + mask_builder = self.get_mask_builder(metadata["slide_name"], trainer) + tl_preds_slide = tl_preds_slide[ + : len(metadata["xs"]) + ] # take only real predictions (not padding) + mask_builder.update(tl_preds_slide, metadata["xs"], metadata["ys"]) mlflow.log_artifact( str(mask_builder.save()), artifact_path=str(mask_builder.save_dir) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index 8bc8c04b..b3687cfa 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -80,7 +80,6 @@ def __getitem__(self, idx: int) -> T: if self.padding: slide_embeddings = F.pad(slide_embeddings, (0, 0, 0, pad_amount), value=0.0) - print("PADDING", pad_amount, self.max_embeddings, slide_embeddings.shape) metadata = SlideMetadata( slide_id=slide_metadata["id"], slide_name=slide_name, diff --git a/prostate_cancer/datamodule/slide_data_module.py b/prostate_cancer/datamodule/slide_data_module.py index 92af7a82..4799a20d 100644 --- a/prostate_cancer/datamodule/slide_data_module.py +++ b/prostate_cancer/datamodule/slide_data_module.py @@ -1,6 +1,7 @@ from collections.abc import Iterable from typing import TYPE_CHECKING, cast +import torch from hydra.utils import instantiate from lightning import LightningDataModule from omegaconf import DictConfig @@ -14,7 +15,9 @@ ) from prostate_cancer.typing import ( + LabeledSlideSample, LabeledSlideSampleBatch, + UnlabeledSlideSample, UnlabeledSlideSampleBatch, ) @@ -75,6 +78,7 @@ def train_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: self.train, sampler=sampler, shuffle=shuffle, + collate_fn=collate_fn_labeled, batch_size=self.batch_size, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, @@ -85,6 +89,7 @@ def val_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: return DataLoader( self.val, batch_size=self.batch_size, + collate_fn=collate_fn_labeled, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, ) @@ -93,6 +98,7 @@ def test_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: return DataLoader( self.test, batch_size=self.batch_size, + collate_fn=collate_fn_labeled, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, ) @@ -101,6 +107,33 @@ def predict_dataloader(self) -> Iterable[UnlabeledSlideSampleBatch]: return DataLoader( self.predict, batch_size=self.batch_size, + collate_fn=collate_fn_unlabeled, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, ) + + +def collate_fn_labeled(batch: list[LabeledSlideSample]) -> LabeledSlideSampleBatch: + inputs = [] + labels = [] + metadatas = [] + for input, label, metadata in batch: + inputs.append(input) + labels.append(label) + metadatas.append(metadata) + + inputs_tensor = torch.stack(inputs) + labels_tensor = torch.stack(labels) + return inputs_tensor, labels_tensor, metadatas + + +def collate_fn_unlabeled( + batch: list[UnlabeledSlideSample], +) -> UnlabeledSlideSampleBatch: + inputs = [] + metadatas = [] + for input, metadata in batch: + inputs.append(input) + metadatas.append(metadata) + inputs_tensor = torch.stack(inputs) + return inputs_tensor, metadatas diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index f46ee588..b86c2302 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -30,19 +30,10 @@ class SlideMetadata(TypedDict): ys: Tensor -class SlideMetadataBatch(TypedDict): - slide_id: list[str] - slide_name: list[str] - slide_path: list[str] - n_tiles: list[int] - xs: Tensor - ys: Tensor - - -LabeledSlideSample = tuple[Tensor, Tensor, TileMetadata] -UnlabeledSlideSample = tuple[Tensor, TileMetadata] +LabeledSlideSample = tuple[Tensor, Tensor, SlideMetadata] +UnlabeledSlideSample = tuple[Tensor, SlideMetadata] -LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, SlideMetadataBatch] -UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, SlideMetadataBatch] +LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, list[SlideMetadata]] +UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, list[SlideMetadata]] MILModelOutput = tuple[Tensor, Tensor] # SL preds, TL preds From 1655d022ffd7b3529c8c48c2bf0649076a78ecec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 16 May 2026 11:07:03 +0000 Subject: [PATCH 059/284] fix: cpu migrate --- prostate_cancer/callbacks/mil_prediction_callback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 611e4038..ef1453dc 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -68,7 +68,7 @@ def on_predict_batch_end( tl_preds_slide = tl_preds_slide[ : len(metadata["xs"]) ] # take only real predictions (not padding) - mask_builder.update(tl_preds_slide, metadata["xs"], metadata["ys"]) + mask_builder.update(tl_preds_slide.cpu(), metadata["xs"], metadata["ys"]) mlflow.log_artifact( str(mask_builder.save()), artifact_path=str(mask_builder.save_dir) From 4fbbd677a9f25a4bad7d6693c3e135cd9399ae64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 18 May 2026 13:13:24 +0000 Subject: [PATCH 060/284] feat: tl sl train --- prostate_cancer/attention_mil_model.py | 50 +++++++++++-------- .../datasets/slide_embeddings_dataset.py | 14 +++++- .../datamodule/slide_data_module.py | 13 +++-- prostate_cancer/typing.py | 4 +- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index ae783bb1..bcbf927f 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -43,7 +43,8 @@ def __init__(self, foundation: str, lr: float) -> None: ) self.classifier = nn.Linear(input_dim, 1) - self.criterion = nn.BCELoss() + self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") + self.tl_criterion = nn.BCEWithLogitsLoss(reduction="mean") self.lr = lr metrics: dict[str, Metric | MetricCollection] = { @@ -88,26 +89,31 @@ def forward(self, x: Tensor) -> MILModelOutput: ) # (batch_size, num_tiles_padded, 1) # TL predictions - tl_preds = self.classifier(x) # (batch_size, num_tiles_padded, 1) + tl_preds_raw = self.classifier(x) # (batch_size, num_tiles_padded, 1) + tl_preds_valid_raw = tl_preds_raw * mask # weight TL predictions with attention - sl_pred_raw = torch.sum(attention_weights * tl_preds, dim=1) # (batch_size, 1) - sl_pred = sl_pred_raw.sigmoid() # (batch_size, 1) + sl_pred_raw = torch.sum( + attention_weights * tl_preds_valid_raw, dim=1 + ) # (batch_size, 1) return ( - sl_pred.squeeze(-1), - tl_preds, - ) # (batch_size,), (batch_size, num_tiles_padded, 1) + sl_pred_raw.squeeze(-1), + tl_preds_valid_raw.squeeze(-1), + ) # (batch_size,), (batch_size, num_tiles_padded) def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide - bags, labels, _ = batch + bags, tl_labels, sl_labels, _ = batch + + sl_outputs, tl_outputs = self(bags) + sl_loss = self.sl_criterion(sl_outputs, sl_labels) + tl_loss = self.tl_criterion(tl_outputs, tl_labels) + loss = sl_loss + tl_loss - outputs, _ = self(bags) - loss = self.criterion(outputs, labels) self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) - self.train_metrics.update(outputs, labels) + self.train_metrics.update(sl_outputs, sl_labels) self.log_dict( self.train_metrics, on_epoch=True, on_step=False, batch_size=len(bags) ) @@ -115,31 +121,33 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: return loss def validation_step(self, batch: LabeledSlideSampleBatch) -> None: - bags, labels, _ = batch + bags, tl_labels, sl_labels, _ = batch + + sl_outputs, tl_outputs = self(bags) + sl_loss = self.sl_criterion(sl_outputs, sl_labels) + tl_loss = self.tl_criterion(tl_outputs, tl_labels) + loss = sl_loss + tl_loss - outputs, _ = self(bags) - loss = self.criterion(outputs, labels) self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) - self.val_metrics.update(outputs, labels) + self.val_metrics.update(sl_outputs, sl_labels) self.log_dict( self.val_metrics, on_epoch=True, on_step=False, batch_size=len(bags) ) def test_step(self, batch: LabeledSlideSampleBatch) -> None: - bags, labels, _ = batch + bags, _, sl_labels, _ = batch - outputs, _ = self(bags) + sl_outputs, _ = self(bags) - self.test_metrics.update(outputs, labels) + self.test_metrics.update(sl_outputs, sl_labels) self.log_dict( self.test_metrics, on_epoch=True, on_step=False, batch_size=len(bags) ) - return outputs - def predict_step(self, batch: UnlabeledSlideSampleBatch) -> MILModelOutput: - return self(batch[0]) + sl_preds_raw, tl_preds_raw = self(batch[0]) + return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid() def configure_optimizers(self) -> Optimizer: return AdamW(self.parameters(), lr=self.lr) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index b3687cfa..eb6e53d9 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -36,6 +36,10 @@ def __init__( self.slides, self.tiles, self.embeddings_folder = self.download_artifacts( uris, embeddings_uri ) + + if include_labels: + self.tiles["carcinoma"] = self.tiles["carcinoma_roi_percentage"] > 0.5 + self.padding = padding self.max_embeddings = self.tiles["slide_id"].value_counts().max() @@ -91,8 +95,14 @@ def __getitem__(self, idx: int) -> T: if not self.include_labels: return slide_embeddings, metadata # type: ignore[return-value] - label = torch.tensor(slide_metadata.carcinoma).float() - return slide_embeddings, label, metadata # type: ignore[return-value] + sl_label = torch.tensor(slide_metadata["carcinoma"]).float() + + tl_labels = torch.zeros(len(slide_embeddings)).float() + tl_labels[: len(filtered_tiles)] = torch.tensor( + filtered_tiles["carcinoma"] + ).float() + + return slide_embeddings, tl_labels, sl_label, metadata # type: ignore[return-value] class LabeledSlideEmbeddingsDataset(SlideEmbeddingsDataset[LabeledSlideSample]): diff --git a/prostate_cancer/datamodule/slide_data_module.py b/prostate_cancer/datamodule/slide_data_module.py index 4799a20d..b6e72e1a 100644 --- a/prostate_cancer/datamodule/slide_data_module.py +++ b/prostate_cancer/datamodule/slide_data_module.py @@ -115,16 +115,19 @@ def predict_dataloader(self) -> Iterable[UnlabeledSlideSampleBatch]: def collate_fn_labeled(batch: list[LabeledSlideSample]) -> LabeledSlideSampleBatch: inputs = [] - labels = [] + sl_labels = [] + tl_labels = [] metadatas = [] - for input, label, metadata in batch: + for input, sl_label, tl_label, metadata in batch: inputs.append(input) - labels.append(label) + sl_labels.append(sl_label) + tl_labels.append(tl_label) metadatas.append(metadata) inputs_tensor = torch.stack(inputs) - labels_tensor = torch.stack(labels) - return inputs_tensor, labels_tensor, metadatas + sl_labels_tensor = torch.stack(sl_labels) + tl_labels_tensor = torch.stack(tl_labels) + return inputs_tensor, sl_labels_tensor, tl_labels_tensor, metadatas def collate_fn_unlabeled( diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index b86c2302..1e1a272a 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -30,10 +30,10 @@ class SlideMetadata(TypedDict): ys: Tensor -LabeledSlideSample = tuple[Tensor, Tensor, SlideMetadata] +LabeledSlideSample = tuple[Tensor, Tensor, Tensor, SlideMetadata] UnlabeledSlideSample = tuple[Tensor, SlideMetadata] -LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, list[SlideMetadata]] +LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, Tensor, list[SlideMetadata]] UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, list[SlideMetadata]] MILModelOutput = tuple[Tensor, Tensor] # SL preds, TL preds From 62277864272d11ca1ff0123d14be7437b9a0168a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 19 May 2026 12:10:24 +0000 Subject: [PATCH 061/284] feat: tl sl tran --- .../train/slide_embeddings/base.yaml | 8 ++++ prostate_cancer/attention_mil_model.py | 45 ++++++++++++++----- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/configs/experiment/train/slide_embeddings/base.yaml b/configs/experiment/train/slide_embeddings/base.yaml index b038ff5b..d2969f1d 100644 --- a/configs/experiment/train/slide_embeddings/base.yaml +++ b/configs/experiment/train/slide_embeddings/base.yaml @@ -5,6 +5,14 @@ defaults: - /datamodule/slide_embeddings_datamodule@datamodule - _self_ +callbacks: + checkpointing: + monitor: validation/loss + mode: min + early_stopping: + monitor: validation/loss + mode: min + datamodule: batch_size: 4 num_workers: 8 diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index bcbf927f..9fee64e6 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -56,9 +56,17 @@ def __init__(self, foundation: str, lr: float) -> None: "negative_predictive_value": NegativePredictiveValue("binary"), } - self.train_metrics = MetricCollection(deepcopy(metrics), prefix="train/") - self.val_metrics = MetricCollection(deepcopy(metrics), prefix="validation/") - self.test_metrics = MetricCollection(deepcopy(metrics), prefix="test/") + self.train_metrics_sl = MetricCollection(deepcopy(metrics), prefix="sl_train/") + self.val_metrics_sl = MetricCollection( + deepcopy(metrics), prefix="sl_validation/" + ) + self.test_metrics_sl = MetricCollection(deepcopy(metrics), prefix="sl_test/") + + self.train_metrics_tl = MetricCollection(deepcopy(metrics), prefix="tl_train/") + self.val_metrics_tl = MetricCollection( + deepcopy(metrics), prefix="tl_validation/" + ) + self.test_metrics_tl = MetricCollection(deepcopy(metrics), prefix="tl_test/") def forward(self, x: Tensor) -> MILModelOutput: # x has shape (batch_size, num_tiles_padded, embedding_dim) @@ -113,9 +121,14 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) - self.train_metrics.update(sl_outputs, sl_labels) + self.train_metrics_sl.update(sl_outputs, sl_labels) + self.train_metrics_tl.update(tl_outputs, tl_labels) + self.log_dict( - self.train_metrics, on_epoch=True, on_step=False, batch_size=len(bags) + self.train_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) + ) + self.log_dict( + self.train_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) return loss @@ -130,19 +143,29 @@ def validation_step(self, batch: LabeledSlideSampleBatch) -> None: self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) - self.val_metrics.update(sl_outputs, sl_labels) + self.val_metrics_sl.update(sl_outputs, sl_labels) + self.val_metrics_tl.update(tl_outputs, tl_labels) + self.log_dict( - self.val_metrics, on_epoch=True, on_step=False, batch_size=len(bags) + self.val_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) + ) + self.log_dict( + self.val_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) def test_step(self, batch: LabeledSlideSampleBatch) -> None: - bags, _, sl_labels, _ = batch + bags, tl_labels, sl_labels, _ = batch - sl_outputs, _ = self(bags) + sl_outputs, tl_outputs = self(bags) - self.test_metrics.update(sl_outputs, sl_labels) + self.test_metrics_sl.update(sl_outputs, sl_labels) + self.test_metrics_tl.update(tl_outputs, tl_labels) + + self.log_dict( + self.test_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) + ) self.log_dict( - self.test_metrics, on_epoch=True, on_step=False, batch_size=len(bags) + self.test_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) def predict_step(self, batch: UnlabeledSlideSampleBatch) -> MILModelOutput: From 7ce545c3a4e4df5c349d213997bdd70fe3c61fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 20 May 2026 07:17:50 +0000 Subject: [PATCH 062/284] feat: train data --- configs/experiment/train/slide_embeddings/base.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/experiment/train/slide_embeddings/base.yaml b/configs/experiment/train/slide_embeddings/base.yaml index d2969f1d..0b7fbf02 100644 --- a/configs/experiment/train/slide_embeddings/base.yaml +++ b/configs/experiment/train/slide_embeddings/base.yaml @@ -2,6 +2,8 @@ defaults: - /experiment/train/base + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data - /datamodule/slide_embeddings_datamodule@datamodule - _self_ From 5fe861d9a5e0b2e10eb57dfd6f37de0730f65e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 20 May 2026 07:22:17 +0000 Subject: [PATCH 063/284] fix: add tl th --- configs/model/virchow2_mil_model.yaml | 1 + prostate_cancer/attention_mil_model.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/configs/model/virchow2_mil_model.yaml b/configs/model/virchow2_mil_model.yaml index bfcb3ff4..790b9391 100644 --- a/configs/model/virchow2_mil_model.yaml +++ b/configs/model/virchow2_mil_model.yaml @@ -1,3 +1,4 @@ _target_: prostate_cancer.attention_mil_model.ProstateCancerAttentionMIL foundation: "virchow2" lr: ??? +tl_threshold: ??? diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 9fee64e6..d0267d63 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -25,7 +25,7 @@ class ProstateCancerAttentionMIL(LightningModule): - def __init__(self, foundation: str, lr: float) -> None: + def __init__(self, foundation: str, lr: float, tl_threshold: float) -> None: super().__init__() match foundation: case "pgp": From 101cd3182c75f767ddb838b9121d9f4944ebced0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 21 May 2026 05:43:24 +0000 Subject: [PATCH 064/284] feat: do not use series --- prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index eb6e53d9..792a704b 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -99,7 +99,7 @@ def __getitem__(self, idx: int) -> T: tl_labels = torch.zeros(len(slide_embeddings)).float() tl_labels[: len(filtered_tiles)] = torch.tensor( - filtered_tiles["carcinoma"] + filtered_tiles["carcinoma"].to_numpy() ).float() return slide_embeddings, tl_labels, sl_label, metadata # type: ignore[return-value] From 6ab94cb5d897c80d4cee801ae1a1850cdcd7675f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 21 May 2026 12:27:31 +0000 Subject: [PATCH 065/284] fix: padding --- prostate_cancer/attention_mil_model.py | 27 ++++++++++--------- .../callbacks/mil_prediction_callback.py | 8 +++--- .../datasets/slide_embeddings_dataset.py | 13 +++++++-- prostate_cancer/typing.py | 2 +- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index d0267d63..32518d63 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -44,7 +44,7 @@ def __init__(self, foundation: str, lr: float, tl_threshold: float) -> None: self.classifier = nn.Linear(input_dim, 1) self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") - self.tl_criterion = nn.BCEWithLogitsLoss(reduction="mean") + self.tl_criterion = nn.BCEWithLogitsLoss(reduction="none") # handle padding self.lr = lr metrics: dict[str, Metric | MetricCollection] = { @@ -108,21 +108,23 @@ def forward(self, x: Tensor) -> MILModelOutput: return ( sl_pred_raw.squeeze(-1), tl_preds_valid_raw.squeeze(-1), - ) # (batch_size,), (batch_size, num_tiles_padded) + mask.squeeze(-1), + ) # (batch_size,), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded) def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide bags, tl_labels, sl_labels, _ = batch - sl_outputs, tl_outputs = self(bags) + sl_outputs, tl_outputs, mask = self(bags) sl_loss = self.sl_criterion(sl_outputs, sl_labels) - tl_loss = self.tl_criterion(tl_outputs, tl_labels) + tl_loss_all = self.tl_criterion(tl_outputs, tl_labels) + tl_loss = (tl_loss_all * mask).sum() / mask.sum() loss = sl_loss + tl_loss self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) self.train_metrics_sl.update(sl_outputs, sl_labels) - self.train_metrics_tl.update(tl_outputs, tl_labels) + self.train_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) self.log_dict( self.train_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) @@ -136,15 +138,16 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: def validation_step(self, batch: LabeledSlideSampleBatch) -> None: bags, tl_labels, sl_labels, _ = batch - sl_outputs, tl_outputs = self(bags) + sl_outputs, tl_outputs, mask = self(bags) sl_loss = self.sl_criterion(sl_outputs, sl_labels) - tl_loss = self.tl_criterion(tl_outputs, tl_labels) + tl_loss_all = self.tl_criterion(tl_outputs, tl_labels) + tl_loss = (tl_loss_all * mask).sum() / mask.sum() loss = sl_loss + tl_loss self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) self.val_metrics_sl.update(sl_outputs, sl_labels) - self.val_metrics_tl.update(tl_outputs, tl_labels) + self.val_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) self.log_dict( self.val_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) @@ -156,10 +159,10 @@ def validation_step(self, batch: LabeledSlideSampleBatch) -> None: def test_step(self, batch: LabeledSlideSampleBatch) -> None: bags, tl_labels, sl_labels, _ = batch - sl_outputs, tl_outputs = self(bags) + sl_outputs, tl_outputs, mask = self(bags) self.test_metrics_sl.update(sl_outputs, sl_labels) - self.test_metrics_tl.update(tl_outputs, tl_labels) + self.test_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) self.log_dict( self.test_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) @@ -169,8 +172,8 @@ def test_step(self, batch: LabeledSlideSampleBatch) -> None: ) def predict_step(self, batch: UnlabeledSlideSampleBatch) -> MILModelOutput: - sl_preds_raw, tl_preds_raw = self(batch[0]) - return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid() + sl_preds_raw, tl_preds_raw, mask = self(batch[0]) + return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid(), mask def configure_optimizers(self) -> Optimizer: return AdamW(self.parameters(), lr=self.lr) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index ef1453dc..722b5043 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -50,7 +50,7 @@ def on_predict_batch_end( dataloader_idx: int = 0, ) -> None: assert isinstance(trainer.logger, MLFlowLogger) - sl_preds, tl_preds = outputs + sl_preds, tl_preds, batch_mask = outputs _, metadata_batch = batch # Log SL predictions @@ -63,10 +63,12 @@ def on_predict_batch_end( ) # Log TL predictions - for metadata, tl_preds_slide in zip(metadata_batch, tl_preds, strict=True): + for metadata, tl_preds_slide, mask_slide in zip( + metadata_batch, tl_preds, batch_mask, strict=True + ): mask_builder = self.get_mask_builder(metadata["slide_name"], trainer) tl_preds_slide = tl_preds_slide[ - : len(metadata["xs"]) + mask_slide.bool() ] # take only real predictions (not padding) mask_builder.update(tl_preds_slide.cpu(), metadata["xs"], metadata["ys"]) diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py index 792a704b..4be77a16 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py @@ -22,6 +22,11 @@ T = TypeVar("T", bound=LabeledSlideSample | UnlabeledSlideSample) +def get_embedding_ceiling(tiles: pd.DataFrame, thresholds: dict[str, float]) -> int: + filtered_tiles = filter_tiles_by_thresholds(tiles, thresholds) + return filtered_tiles["slide_id"].value_counts().max() + + class SlideEmbeddingsDataset(Dataset[T], Generic[T]): def __init__( self, @@ -38,10 +43,12 @@ def __init__( ) if include_labels: - self.tiles["carcinoma"] = self.tiles["carcinoma_roi_percentage"] > 0.5 + self.tiles["carcinoma"] = self.tiles[ + "carcinoma_roi_percentage" + ] > self.thresholds.get("carcinoma_roi_t") self.padding = padding - self.max_embeddings = self.tiles["slide_id"].value_counts().max() + self.max_embeddings = get_embedding_ceiling(self.tiles, self.thresholds) def download_artifacts( self, tiling_uris: Iterable[str], embeddings_uri: str @@ -81,6 +88,8 @@ def __getitem__(self, idx: int) -> T: slide_embeddings = slide_embeddings[filtered_tiles.index.tolist()] pad_amount = self.max_embeddings - slide_embeddings.shape[0] + assert pad_amount >= 0, "Invalid padding" + if self.padding: slide_embeddings = F.pad(slide_embeddings, (0, 0, 0, pad_amount), value=0.0) diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 1e1a272a..710b717d 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -36,4 +36,4 @@ class SlideMetadata(TypedDict): LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, Tensor, list[SlideMetadata]] UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, list[SlideMetadata]] -MILModelOutput = tuple[Tensor, Tensor] # SL preds, TL preds +MILModelOutput = tuple[Tensor, Tensor, Tensor] # SL preds, TL preds, Padding TL mask From e4c164d9610578096e39753187ccb35434927607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 22 May 2026 06:51:52 +0000 Subject: [PATCH 066/284] feat: extended mil model --- configs/model/checkpoints/mmci_tl/virchow2_mil.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml index 3ae9869d..2667f102 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml @@ -2,5 +2,5 @@ defaults: - base - _self_ -checkpoint: "mlflow-artifacts:/65/a649871245c040a39022206cbc9471f1/artifacts/checkpoints/epoch=0-step=135/checkpoint.ckpt" -model_name: "Virchow2 (MIL)" +checkpoint: "mlflow-artifacts:/65/1d58d8b3d3c347a9be88b25822313d98/artifacts/checkpoints/epoch=2-step=452/checkpoint.ckpt" +model_name: "Virchow2 (E-MIL)" From 6522599b491667c7414ad01db9f7db8384222680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 22 May 2026 06:55:27 +0000 Subject: [PATCH 067/284] fix: add tl threshold --- configs/model/checkpoints/mmci_tl/virchow2_mil.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml index 2667f102..a5b49a3f 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml @@ -2,5 +2,7 @@ defaults: - base - _self_ +tl_threshold: 0.5 + checkpoint: "mlflow-artifacts:/65/1d58d8b3d3c347a9be88b25822313d98/artifacts/checkpoints/epoch=2-step=452/checkpoint.ckpt" model_name: "Virchow2 (E-MIL)" From 52117433d509094f4c7020887e8068ad6158056a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 22 May 2026 07:03:55 +0000 Subject: [PATCH 068/284] fix: typo --- configs/model/checkpoints/mmci_tl/virchow2_mil.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml index a5b49a3f..3dd4f507 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml @@ -2,7 +2,7 @@ defaults: - base - _self_ -tl_threshold: 0.5 +tile_threshold: 0.5 checkpoint: "mlflow-artifacts:/65/1d58d8b3d3c347a9be88b25822313d98/artifacts/checkpoints/epoch=2-step=452/checkpoint.ckpt" model_name: "Virchow2 (E-MIL)" From b663b028908cbd1cd7b7635f032ec0aff7292bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 22 May 2026 07:11:22 +0000 Subject: [PATCH 069/284] feat: generalize --- configs/experiment/predict/base.yaml | 2 ++ configs/experiment/predict/embeddings/base.yaml | 5 ----- configs/experiment/predict/images/base.yaml | 5 ----- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/configs/experiment/predict/base.yaml b/configs/experiment/predict/base.yaml index 6b043e6c..a1f8f5f2 100644 --- a/configs/experiment/predict/base.yaml +++ b/configs/experiment/predict/base.yaml @@ -1,10 +1,12 @@ # @package _global_ model: + tl_threshold: ${tile_threshold} lr: 0 checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} +tile_threshold: ${model_data.tile_threshold} thresholds: ${predict_data.thresholds} mean: ${model_data.stats.mean} std: ${model_data.stats.std} diff --git a/configs/experiment/predict/embeddings/base.yaml b/configs/experiment/predict/embeddings/base.yaml index e216fc56..e7d12d6d 100644 --- a/configs/experiment/predict/embeddings/base.yaml +++ b/configs/experiment/predict/embeddings/base.yaml @@ -12,8 +12,3 @@ datamodule: predict: uris: - ${predict_data.tiles_uri_224} - -model: - tl_threshold: ${tile_threshold} - -tile_threshold: ${model_data.tile_threshold} diff --git a/configs/experiment/predict/images/base.yaml b/configs/experiment/predict/images/base.yaml index cfc96a0d..9c39bc2a 100644 --- a/configs/experiment/predict/images/base.yaml +++ b/configs/experiment/predict/images/base.yaml @@ -5,7 +5,6 @@ defaults: - /datamodule/images_datamodule@datamodule - _self_ - datamodule: batch_size: 64 num_workers: 8 @@ -14,7 +13,3 @@ datamodule: uris: - ${predict_data.tiles_uri_512} -model: - tl_threshold: ${tile_threshold} - -tile_threshold: ${model_data.tile_threshold} From 209457a91d4c8c606e158ac061c07695859f6579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 22 May 2026 08:43:39 +0000 Subject: [PATCH 070/284] feat: show also attention --- prostate_cancer/attention_mil_model.py | 15 ++++---- .../callbacks/mil_prediction_callback.py | 38 +++++++++++-------- prostate_cancer/typing.py | 4 +- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 32518d63..7cecebec 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -97,7 +97,7 @@ def forward(self, x: Tensor) -> MILModelOutput: ) # (batch_size, num_tiles_padded, 1) # TL predictions - tl_preds_raw = self.classifier(x) # (batch_size, num_tiles_padded, 1) + tl_preds_raw: Tensor = self.classifier(x) # (batch_size, num_tiles_padded, 1) tl_preds_valid_raw = tl_preds_raw * mask # weight TL predictions with attention @@ -109,13 +109,14 @@ def forward(self, x: Tensor) -> MILModelOutput: sl_pred_raw.squeeze(-1), tl_preds_valid_raw.squeeze(-1), mask.squeeze(-1), - ) # (batch_size,), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded) + attention_weights.squeeze(-1), + ) # (batch_size,), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded) def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide bags, tl_labels, sl_labels, _ = batch - sl_outputs, tl_outputs, mask = self(bags) + sl_outputs, tl_outputs, mask, _ = self(bags) sl_loss = self.sl_criterion(sl_outputs, sl_labels) tl_loss_all = self.tl_criterion(tl_outputs, tl_labels) tl_loss = (tl_loss_all * mask).sum() / mask.sum() @@ -138,7 +139,7 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: def validation_step(self, batch: LabeledSlideSampleBatch) -> None: bags, tl_labels, sl_labels, _ = batch - sl_outputs, tl_outputs, mask = self(bags) + sl_outputs, tl_outputs, mask, _ = self(bags) sl_loss = self.sl_criterion(sl_outputs, sl_labels) tl_loss_all = self.tl_criterion(tl_outputs, tl_labels) tl_loss = (tl_loss_all * mask).sum() / mask.sum() @@ -159,7 +160,7 @@ def validation_step(self, batch: LabeledSlideSampleBatch) -> None: def test_step(self, batch: LabeledSlideSampleBatch) -> None: bags, tl_labels, sl_labels, _ = batch - sl_outputs, tl_outputs, mask = self(bags) + sl_outputs, tl_outputs, mask, _ = self(bags) self.test_metrics_sl.update(sl_outputs, sl_labels) self.test_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) @@ -172,8 +173,8 @@ def test_step(self, batch: LabeledSlideSampleBatch) -> None: ) def predict_step(self, batch: UnlabeledSlideSampleBatch) -> MILModelOutput: - sl_preds_raw, tl_preds_raw, mask = self(batch[0]) - return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid(), mask + sl_preds_raw, tl_preds_raw, mask, attention = self(batch[0]) + return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid(), mask, attention def configure_optimizers(self) -> Optimizer: return AdamW(self.parameters(), lr=self.lr) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 722b5043..f97145b8 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -15,7 +15,9 @@ class MILPredictionCallback(Callback): - def get_mask_builder(self, slide_name: str, trainer: Trainer) -> ScalarMaskBuilder: + def get_mask_builder( + self, slide_name: str, trainer: Trainer, save_dir: str + ) -> ScalarMaskBuilder: if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") @@ -28,7 +30,7 @@ def get_mask_builder(self, slide_name: str, trainer: Trainer) -> ScalarMaskBuild slide = _slide.iloc[0] kwargs = { - "save_dir": Path("heatmaps"), + "save_dir": Path(save_dir), "filename": Path(slide.path).stem, "extent_x": slide.extent_x, "extent_y": slide.extent_y, @@ -50,28 +52,32 @@ def on_predict_batch_end( dataloader_idx: int = 0, ) -> None: assert isinstance(trainer.logger, MLFlowLogger) - sl_preds, tl_preds, batch_mask = outputs + sl_preds, tl_preds, batch_mask, batch_attention = outputs _, metadata_batch = batch # Log SL predictions trainer.logger.log_table( { "slide": [metadata["slide_name"] for metadata in metadata_batch], - "prediction": sl_preds.tolist(), + "sl_prediction": sl_preds.tolist(), }, artifact_file="tables/sl_predictions.json", ) - # Log TL predictions - for metadata, tl_preds_slide, mask_slide in zip( - metadata_batch, tl_preds, batch_mask, strict=True + # Log TL predictions and Attention Map + for metadata, tl_preds_slide, mask_slide, attention_slide in zip( + metadata_batch, tl_preds, batch_mask, batch_attention, strict=True ): - mask_builder = self.get_mask_builder(metadata["slide_name"], trainer) - tl_preds_slide = tl_preds_slide[ - mask_slide.bool() - ] # take only real predictions (not padding) - mask_builder.update(tl_preds_slide.cpu(), metadata["xs"], metadata["ys"]) - - mlflow.log_artifact( - str(mask_builder.save()), artifact_path=str(mask_builder.save_dir) - ) + for mask_type, data in zip( + ["heatmaps", "attention"], + [tl_preds_slide, attention_slide], + strict=True, + ): + mask_builder = self.get_mask_builder( + metadata["slide_name"], trainer, mask_type + ) + data = data[mask_slide.bool()] # take only real tiles (not padding) + mask_builder.update(data.cpu(), metadata["xs"], metadata["ys"]) + mlflow.log_artifact( + str(mask_builder.save()), artifact_path=str(mask_builder.save_dir) + ) diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 710b717d..1066e4f9 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -36,4 +36,6 @@ class SlideMetadata(TypedDict): LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, Tensor, list[SlideMetadata]] UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, list[SlideMetadata]] -MILModelOutput = tuple[Tensor, Tensor, Tensor] # SL preds, TL preds, Padding TL mask +MILModelOutput = tuple[ + Tensor, Tensor, Tensor, Tensor +] # SL preds, TL preds, Padding TL mask, Attention From e746d36aaec2ff3ecdb163c4ed059c3885ebc721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 25 May 2026 12:10:08 +0000 Subject: [PATCH 071/284] feat: rescale attn --- .../callbacks/mil_prediction_callback.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index f97145b8..e7f88024 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -3,6 +3,7 @@ import mlflow import pandas as pd +import torch from lightning import Callback, LightningModule, Trainer from rationai.masks.mask_builders import ScalarMaskBuilder from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger @@ -14,6 +15,12 @@ from prostate_cancer.datamodule import SlideDataModule +def min_max_normalization(tensor: torch.Tensor) -> torch.Tensor: + weights_max = tensor.max() + weights_min = tensor.min() + return (tensor - weights_min) / (weights_max - weights_min) + + class MILPredictionCallback(Callback): def get_mask_builder( self, slide_name: str, trainer: Trainer, save_dir: str @@ -69,7 +76,7 @@ def on_predict_batch_end( metadata_batch, tl_preds, batch_mask, batch_attention, strict=True ): for mask_type, data in zip( - ["heatmaps", "attention"], + ["heatmaps", "attention_rescaled"], [tl_preds_slide, attention_slide], strict=True, ): @@ -77,7 +84,9 @@ def on_predict_batch_end( metadata["slide_name"], trainer, mask_type ) data = data[mask_slide.bool()] # take only real tiles (not padding) - mask_builder.update(data.cpu(), metadata["xs"], metadata["ys"]) + mask_builder.update( + min_max_normalization(data).cpu(), metadata["xs"], metadata["ys"] + ) mlflow.log_artifact( str(mask_builder.save()), artifact_path=str(mask_builder.save_dir) ) From 59cde6ab2031c97a5605dec485780418a3d49beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 5 Jun 2026 09:21:51 +0000 Subject: [PATCH 072/284] feat: add sl --- .../predict/slide_embeddings/base.yaml | 4 ++ .../train/slide_embeddings/base.yaml | 3 ++ .../train/slide_embeddings/pgp.yaml | 2 +- .../train/slide_embeddings/virchow2.yaml | 2 +- .../checkpoints/mmci_tl/virchow2_mil.yaml | 1 + configs/model/virchow2_mil_model.yaml | 1 + prostate_cancer/attention_mil_model.py | 53 +++++++++++++------ 7 files changed, 47 insertions(+), 19 deletions(-) diff --git a/configs/experiment/predict/slide_embeddings/base.yaml b/configs/experiment/predict/slide_embeddings/base.yaml index df6b04ea..ca6ab9e1 100644 --- a/configs/experiment/predict/slide_embeddings/base.yaml +++ b/configs/experiment/predict/slide_embeddings/base.yaml @@ -5,6 +5,10 @@ defaults: - /datamodule/slide_embeddings_datamodule@datamodule - _self_ +slide_threshold: ${model_data.slide_threshold} +model: + sl_threshold: ${slide_threshold} + datamodule: batch_size: 2 num_workers: 4 diff --git a/configs/experiment/train/slide_embeddings/base.yaml b/configs/experiment/train/slide_embeddings/base.yaml index 0b7fbf02..ec8a6cff 100644 --- a/configs/experiment/train/slide_embeddings/base.yaml +++ b/configs/experiment/train/slide_embeddings/base.yaml @@ -7,6 +7,9 @@ defaults: - /datamodule/slide_embeddings_datamodule@datamodule - _self_ +model: + sl_threshold: 0.5 + callbacks: checkpointing: monitor: validation/loss diff --git a/configs/experiment/train/slide_embeddings/pgp.yaml b/configs/experiment/train/slide_embeddings/pgp.yaml index 25811449..c8d7f041 100644 --- a/configs/experiment/train/slide_embeddings/pgp.yaml +++ b/configs/experiment/train/slide_embeddings/pgp.yaml @@ -11,4 +11,4 @@ datamodule: val: embeddings_uri: ${val_data.pgp_embeddings_uri} -model_name: "PGP (MIL)" +model_name: "PGP (Hybrid-MIL)" diff --git a/configs/experiment/train/slide_embeddings/virchow2.yaml b/configs/experiment/train/slide_embeddings/virchow2.yaml index 4bf95eca..a55911be 100644 --- a/configs/experiment/train/slide_embeddings/virchow2.yaml +++ b/configs/experiment/train/slide_embeddings/virchow2.yaml @@ -11,4 +11,4 @@ datamodule: val: embeddings_uri: ${val_data.virchow2_embeddings_uri} -model_name: "Virchow2 (MIL)" +model_name: "Virchow2 (Hybrid-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml index 3dd4f507..50b5e202 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml @@ -3,6 +3,7 @@ defaults: - _self_ tile_threshold: 0.5 +slide_threshold: 0.5 checkpoint: "mlflow-artifacts:/65/1d58d8b3d3c347a9be88b25822313d98/artifacts/checkpoints/epoch=2-step=452/checkpoint.ckpt" model_name: "Virchow2 (E-MIL)" diff --git a/configs/model/virchow2_mil_model.yaml b/configs/model/virchow2_mil_model.yaml index 790b9391..29e9012f 100644 --- a/configs/model/virchow2_mil_model.yaml +++ b/configs/model/virchow2_mil_model.yaml @@ -2,3 +2,4 @@ _target_: prostate_cancer.attention_mil_model.ProstateCancerAttentionMIL foundation: "virchow2" lr: ??? tl_threshold: ??? +sl_threshold: ??? diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 7cecebec..b09f6ec5 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -25,7 +25,9 @@ class ProstateCancerAttentionMIL(LightningModule): - def __init__(self, foundation: str, lr: float, tl_threshold: float) -> None: + def __init__( + self, foundation: str, lr: float, tl_threshold: float, sl_threshold: float + ) -> None: super().__init__() match foundation: case "pgp": @@ -35,6 +37,8 @@ def __init__(self, foundation: str, lr: float, tl_threshold: float) -> None: case _: raise ValueError(f"Unknown foundation model: {foundation}") + # if we did not precompute the embeddings, we would obtain it from this module + # (idendity replaced with foundation model) self.encoder = nn.Identity() self.attention = nn.Sequential( nn.Linear(input_dim, 512), @@ -42,31 +46,46 @@ def __init__(self, foundation: str, lr: float, tl_threshold: float) -> None: nn.Linear(512, 1), ) + # TL Classifier self.classifier = nn.Linear(input_dim, 1) + self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") self.tl_criterion = nn.BCEWithLogitsLoss(reduction="none") # handle padding self.lr = lr - metrics: dict[str, Metric | MetricCollection] = { - "AUC": AUROC("binary"), - "accuracy": Accuracy("binary"), - "precision": Precision("binary"), - "recall": Recall("binary"), - "specificity": Specificity("binary"), - "negative_predictive_value": NegativePredictiveValue("binary"), - } - - self.train_metrics_sl = MetricCollection(deepcopy(metrics), prefix="sl_train/") + metrics: dict[str, dict[str, Metric | MetricCollection]] = {} + + for task_type, t in [("tl", tl_threshold), ("sl", sl_threshold)]: + metrics[task_type] = { + "AUC": AUROC("binary"), + "accuracy": Accuracy("binary", threshold=t), + "precision": Precision("binary", threshold=t), + "recall": Recall("binary", threshold=t), + "specificity": Specificity("binary", threshold=t), + "negative_predictive_value": NegativePredictiveValue( + "binary", threshold=t + ), + } + + self.train_metrics_sl = MetricCollection( + deepcopy(metrics["sl"]), prefix="sl_train/" + ) self.val_metrics_sl = MetricCollection( - deepcopy(metrics), prefix="sl_validation/" + deepcopy(metrics["sl"]), prefix="sl_validation/" + ) + self.test_metrics_sl = MetricCollection( + deepcopy(metrics["sl"]), prefix="sl_test/" ) - self.test_metrics_sl = MetricCollection(deepcopy(metrics), prefix="sl_test/") - self.train_metrics_tl = MetricCollection(deepcopy(metrics), prefix="tl_train/") + self.train_metrics_tl = MetricCollection( + deepcopy(metrics["tl"]), prefix="tl_train/" + ) self.val_metrics_tl = MetricCollection( - deepcopy(metrics), prefix="tl_validation/" + deepcopy(metrics["tl"]), prefix="tl_validation/" + ) + self.test_metrics_tl = MetricCollection( + deepcopy(metrics["tl"]), prefix="tl_test/" ) - self.test_metrics_tl = MetricCollection(deepcopy(metrics), prefix="tl_test/") def forward(self, x: Tensor) -> MILModelOutput: # x has shape (batch_size, num_tiles_padded, embedding_dim) @@ -82,7 +101,7 @@ def forward(self, x: Tensor) -> MILModelOutput: raw_attn.sigmoid(), dim=1 ) # (batch_size, num_tiles_padded, 1) - # Do not attend to padded tiles + # Do not attend to padded tiles (true for non-padded elements) mask = ( (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() ) # (batch_size, num_tiles_padded, 1) From 3ed778e2b66aab3585b4b652a8654a37df74c213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 9 Jun 2026 11:05:34 +0000 Subject: [PATCH 073/284] feat: training --- configs/model/pgp_mil_model.yaml | 2 ++ prostate_cancer/attention_mil_model.py | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/configs/model/pgp_mil_model.yaml b/configs/model/pgp_mil_model.yaml index fa50e745..180dedad 100644 --- a/configs/model/pgp_mil_model.yaml +++ b/configs/model/pgp_mil_model.yaml @@ -1,3 +1,5 @@ _target_: prostate_cancer.attention_mil_model.ProstateCancerAttentionMIL foundation: "pgp" lr: ??? +tl_threshold: ??? +sl_threshold: ??? diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index b09f6ec5..c1dc2528 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -22,6 +22,7 @@ MILModelOutput, UnlabeledSlideSampleBatch, ) +from prostate_cancer.modeling.decode_head import BinaryEmbeddingClassifier class ProstateCancerAttentionMIL(LightningModule): @@ -40,6 +41,8 @@ def __init__( # if we did not precompute the embeddings, we would obtain it from this module # (idendity replaced with foundation model) self.encoder = nn.Identity() + + # from a paper self.attention = nn.Sequential( nn.Linear(input_dim, 512), nn.Tanh(), @@ -47,7 +50,7 @@ def __init__( ) # TL Classifier - self.classifier = nn.Linear(input_dim, 1) + self.classifier = BinaryEmbeddingClassifier(input_dim) self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") self.tl_criterion = nn.BCEWithLogitsLoss(reduction="none") # handle padding @@ -55,6 +58,7 @@ def __init__( metrics: dict[str, dict[str, Metric | MetricCollection]] = {} + # both SL and TL metrics for task_type, t in [("tl", tl_threshold), ("sl", sl_threshold)]: metrics[task_type] = { "AUC": AUROC("binary"), From 050701bb85cc0347af313fe76def59b9be459b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 9 Jun 2026 11:49:32 +0000 Subject: [PATCH 074/284] fix: module --- prostate_cancer/attention_mil_model.py | 4 ++-- prostate_cancer/modeling/decode_head/__init__.py | 5 ++++- .../modeling/decode_head/mil_tl_classifier.py | 13 +++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 prostate_cancer/modeling/decode_head/mil_tl_classifier.py diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index c1dc2528..0f6512fb 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -17,12 +17,12 @@ Specificity, ) +from prostate_cancer.modeling.decode_head import BinaryMILEmbeddingClassifier from prostate_cancer.typing import ( LabeledSlideSampleBatch, MILModelOutput, UnlabeledSlideSampleBatch, ) -from prostate_cancer.modeling.decode_head import BinaryEmbeddingClassifier class ProstateCancerAttentionMIL(LightningModule): @@ -50,7 +50,7 @@ def __init__( ) # TL Classifier - self.classifier = BinaryEmbeddingClassifier(input_dim) + self.classifier = BinaryMILEmbeddingClassifier(input_dim) self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") self.tl_criterion = nn.BCEWithLogitsLoss(reduction="none") # handle padding diff --git a/prostate_cancer/modeling/decode_head/__init__.py b/prostate_cancer/modeling/decode_head/__init__.py index a964ca41..b638642a 100644 --- a/prostate_cancer/modeling/decode_head/__init__.py +++ b/prostate_cancer/modeling/decode_head/__init__.py @@ -3,12 +3,15 @@ from prostate_cancer.modeling.decode_head.embedding_classifier import ( BinaryEmbeddingClassifier, ) -from prostate_cancer.modeling.decode_head.vit_classifier import BinaryViTClassifier +from prostate_cancer.modeling.decode_head.mil_tl_classifier import ( + BinaryMILEmbeddingClassifier, +) __all__ = [ "BinaryCNNClassifier", "BinaryClassifier", "BinaryEmbeddingClassifier", + "BinaryMILEmbeddingClassifier", "BinaryViTClassifier", ] diff --git a/prostate_cancer/modeling/decode_head/mil_tl_classifier.py b/prostate_cancer/modeling/decode_head/mil_tl_classifier.py new file mode 100644 index 00000000..6c72b42d --- /dev/null +++ b/prostate_cancer/modeling/decode_head/mil_tl_classifier.py @@ -0,0 +1,13 @@ +from torch import Tensor + +from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier + + +class BinaryMILEmbeddingClassifier(BinaryClassifier): + def forward(self, x: Tensor) -> Tensor: + if x.ndim != 3: + raise ValueError(f"Expected 3D tensor, got {x.ndim}D") + + x = self.dropout(x) + x = self.proj(x) + return x From 4b7576af722d2e36b12dfe79b66788303034815f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 9 Jun 2026 15:00:25 +0000 Subject: [PATCH 075/284] feat: per task loss --- prostate_cancer/attention_mil_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 0f6512fb..6368ff9d 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -146,6 +146,8 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: loss = sl_loss + tl_loss self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) + self.log("train/sl_loss", sl_loss, on_step=True, prog_bar=True, batch_size=len(bags)) + self.log("train/tl_loss", tl_loss, on_step=True, prog_bar=True, batch_size=len(bags)) self.train_metrics_sl.update(sl_outputs, sl_labels) self.train_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) From 67c0627c13b0a986f78fbba7beb2307da2bf490a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 9 Jun 2026 18:44:48 +0000 Subject: [PATCH 076/284] feat: weighted tl loss --- prostate_cancer/attention_mil_model.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 6368ff9d..59915b0b 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -53,7 +53,9 @@ def __init__( self.classifier = BinaryMILEmbeddingClassifier(input_dim) self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") - self.tl_criterion = nn.BCEWithLogitsLoss(reduction="none") # handle padding + self.tl_criterion = nn.BCEWithLogitsLoss( + reduction="none", pos_weight=torch.tensor([9.65]) + ) # handle padding self.lr = lr metrics: dict[str, dict[str, Metric | MetricCollection]] = {} @@ -146,8 +148,12 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: loss = sl_loss + tl_loss self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) - self.log("train/sl_loss", sl_loss, on_step=True, prog_bar=True, batch_size=len(bags)) - self.log("train/tl_loss", tl_loss, on_step=True, prog_bar=True, batch_size=len(bags)) + self.log( + "train/sl_loss", sl_loss, on_step=True, prog_bar=True, batch_size=len(bags) + ) + self.log( + "train/tl_loss", tl_loss, on_step=True, prog_bar=True, batch_size=len(bags) + ) self.train_metrics_sl.update(sl_outputs, sl_labels) self.train_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) From bea38198a54387bec39eb10133c4d9eadcd0c2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 10 Jun 2026 06:52:16 +0000 Subject: [PATCH 077/284] feat: drop dropout --- prostate_cancer/attention_mil_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 59915b0b..d0c0d11d 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -50,7 +50,7 @@ def __init__( ) # TL Classifier - self.classifier = BinaryMILEmbeddingClassifier(input_dim) + self.classifier = nn.Linear(input_dim, 1) self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") self.tl_criterion = nn.BCEWithLogitsLoss( From 3fe8e36ac65988fe290989734f7fc5d99bcbd781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 11 Jun 2026 05:45:12 +0000 Subject: [PATCH 078/284] feat: add partial losses log --- prostate_cancer/attention_mil_model.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index d0c0d11d..c9b63ce1 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -177,6 +177,12 @@ def validation_step(self, batch: LabeledSlideSampleBatch) -> None: loss = sl_loss + tl_loss self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) + self.log( + "validation/sl_loss", sl_loss, on_step=True, prog_bar=True, batch_size=len(bags) + ) + self.log( + "validation/tl_loss", tl_loss, on_step=True, prog_bar=True, batch_size=len(bags) + ) self.val_metrics_sl.update(sl_outputs, sl_labels) self.val_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) From 6ae3fe9cd4c5713a76259fa510b474cc0fa6687c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 11 Jun 2026 08:16:29 +0000 Subject: [PATCH 079/284] fix: lint --- prostate_cancer/attention_mil_model.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index c9b63ce1..a0920306 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -17,7 +17,6 @@ Specificity, ) -from prostate_cancer.modeling.decode_head import BinaryMILEmbeddingClassifier from prostate_cancer.typing import ( LabeledSlideSampleBatch, MILModelOutput, @@ -178,10 +177,18 @@ def validation_step(self, batch: LabeledSlideSampleBatch) -> None: self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) self.log( - "validation/sl_loss", sl_loss, on_step=True, prog_bar=True, batch_size=len(bags) + "validation/sl_loss", + sl_loss, + on_step=True, + prog_bar=True, + batch_size=len(bags), ) self.log( - "validation/tl_loss", tl_loss, on_step=True, prog_bar=True, batch_size=len(bags) + "validation/tl_loss", + tl_loss, + on_step=True, + prog_bar=True, + batch_size=len(bags), ) self.val_metrics_sl.update(sl_outputs, sl_labels) From f1e9ef9c1d1a5bb434d48cbaab93ac9510a59e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 12 Jun 2026 09:28:17 +0000 Subject: [PATCH 080/284] feat: renaming --- .../bag_of_embeddings_datamodule.yaml | 12 ++++++ ...ed.yaml => bag_of_embeddings_labeled.yaml} | 2 +- ....yaml => bag_of_embeddings_unlabeled.yaml} | 2 +- ...e_labeled.yaml => embeddings_labeled.yaml} | 2 +- .../embeddings/embeddings_unlabeled.yaml | 4 ++ .../datasets/embeddings/tile_unlabeled.yaml | 4 -- ...module.yaml => embeddings_datamodule.yaml} | 8 ++-- configs/datamodule/processor/google_vit.yaml | 2 - ...g_of_tiles_stratified_weighted_random.yaml | 3 ++ .../slide_stratified_weighted_random.yaml | 3 -- .../slide_embeddings_datamodule.yaml | 12 ------ .../base.yaml | 2 +- .../pgp/base.yaml | 2 +- .../pgp/mil_predictions/base.yaml | 2 +- .../pgp/mil_predictions/mmci_tl_val.yaml | 2 +- .../virchow2/base.yaml | 2 +- .../virchow2/mil_predictions/base.yaml | 2 +- .../virchow2/mil_predictions/mmci_tl_val.yaml | 2 +- .../experiment/predict/embeddings/base.yaml | 2 +- configs/experiment/test/embeddings/base.yaml | 2 +- .../base.yaml | 2 +- .../pgp.yaml | 2 +- .../virchow2.yaml | 2 +- .../{tile_embeddings => embeddings}/base.yaml | 2 +- .../pgp/base.yaml | 2 +- .../pgp/mmci.yaml | 2 +- .../virchow2/base.yaml | 2 +- .../virchow2/mmci.yaml | 2 +- prostate_cancer/attention_mil_model.py | 12 +++--- .../callbacks/mil_prediction_callback.py | 8 ++-- prostate_cancer/datamodule/__init__.py | 4 +- ..._module.py => bag_of_tiles_data_module.py} | 40 ++++++++++--------- .../datamodule/datasets/__init__.py | 22 +++++----- ...ataset.py => bag_of_embeddings_dataset.py} | 14 ++++--- ...dings_dataset.py => embeddings_dataset.py} | 6 +-- .../datamodule/samplers/__init__.py | 6 +-- ...les_stratified_weighted_random_sampler.py} | 6 +-- .../modeling/decode_head/__init__.py | 5 +-- .../modeling/decode_head/mil_tl_classifier.py | 13 ------ prostate_cancer/typing.py | 14 ++++--- 40 files changed, 114 insertions(+), 124 deletions(-) create mode 100644 configs/datamodule/bag_of_embeddings_datamodule.yaml rename configs/datamodule/datasets/embeddings/{slide_labeled.yaml => bag_of_embeddings_labeled.yaml} (51%) rename configs/datamodule/datasets/embeddings/{slide_unlabeled.yaml => bag_of_embeddings_unlabeled.yaml} (52%) rename configs/datamodule/datasets/embeddings/{tile_labeled.yaml => embeddings_labeled.yaml} (62%) create mode 100644 configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml delete mode 100644 configs/datamodule/datasets/embeddings/tile_unlabeled.yaml rename configs/datamodule/{tile_embeddings_datamodule.yaml => embeddings_datamodule.yaml} (58%) delete mode 100644 configs/datamodule/processor/google_vit.yaml create mode 100644 configs/datamodule/samplers/bag_of_tiles_stratified_weighted_random.yaml delete mode 100644 configs/datamodule/samplers/slide_stratified_weighted_random.yaml delete mode 100644 configs/datamodule/slide_embeddings_datamodule.yaml rename configs/experiment/predict/{slide_embeddings => bag_of_embeddings}/base.yaml (82%) rename configs/experiment/predict/{slide_embeddings => bag_of_embeddings}/pgp/base.yaml (80%) rename configs/experiment/predict/{slide_embeddings => bag_of_embeddings}/pgp/mil_predictions/base.yaml (68%) rename configs/experiment/predict/{slide_embeddings => bag_of_embeddings}/pgp/mil_predictions/mmci_tl_val.yaml (53%) rename configs/experiment/predict/{slide_embeddings => bag_of_embeddings}/virchow2/base.yaml (82%) rename configs/experiment/predict/{slide_embeddings => bag_of_embeddings}/virchow2/mil_predictions/base.yaml (68%) rename configs/experiment/predict/{slide_embeddings => bag_of_embeddings}/virchow2/mil_predictions/mmci_tl_val.yaml (51%) rename configs/experiment/train/{slide_embeddings => bag_of_embeddings}/base.yaml (90%) rename configs/experiment/train/{slide_embeddings => bag_of_embeddings}/pgp.yaml (84%) rename configs/experiment/train/{slide_embeddings => bag_of_embeddings}/virchow2.yaml (85%) rename configs/experiment/train/{tile_embeddings => embeddings}/base.yaml (56%) rename configs/experiment/train/{tile_embeddings => embeddings}/pgp/base.yaml (83%) rename configs/experiment/train/{tile_embeddings => embeddings}/pgp/mmci.yaml (69%) rename configs/experiment/train/{tile_embeddings => embeddings}/virchow2/base.yaml (84%) rename configs/experiment/train/{tile_embeddings => embeddings}/virchow2/mmci.yaml (67%) rename prostate_cancer/datamodule/{slide_data_module.py => bag_of_tiles_data_module.py} (77%) rename prostate_cancer/datamodule/datasets/{slide_embeddings_dataset.py => bag_of_embeddings_dataset.py} (92%) rename prostate_cancer/datamodule/datasets/{tile_embeddings_dataset.py => embeddings_dataset.py} (95%) rename prostate_cancer/datamodule/samplers/{slide_stratified_weighted_random_sampler.py => bag_of_tiles_stratified_weighted_random_sampler.py} (77%) delete mode 100644 prostate_cancer/modeling/decode_head/mil_tl_classifier.py diff --git a/configs/datamodule/bag_of_embeddings_datamodule.yaml b/configs/datamodule/bag_of_embeddings_datamodule.yaml new file mode 100644 index 00000000..8dd59464 --- /dev/null +++ b/configs/datamodule/bag_of_embeddings_datamodule.yaml @@ -0,0 +1,12 @@ +defaults: + - datasets@train: embeddings/bag_of_embeddings_labeled + - datasets@val: embeddings/bag_of_embeddings_labeled + - datasets@test: embeddings/bag_of_embeddings_labeled + - datasets@predict: embeddings/bag_of_embeddings_unlabeled + + - samplers@sampler: bag_of_tiles_stratified_weighted_random + +_target_: prostate_cancer.datamodule.BagOfTilesDataModule + +batch_size: ??? +num_workers: ??? diff --git a/configs/datamodule/datasets/embeddings/slide_labeled.yaml b/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml similarity index 51% rename from configs/datamodule/datasets/embeddings/slide_labeled.yaml rename to configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml index 31becb7d..90ad214a 100644 --- a/configs/datamodule/datasets/embeddings/slide_labeled.yaml +++ b/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml @@ -1,4 +1,4 @@ -_target_: prostate_cancer.datamodule.datasets.LabeledSlideEmbeddingsDataset +_target_: prostate_cancer.datamodule.datasets.LabeledBagOfEmbeddingsDataset uris: ??? thresholds: ${thresholds} embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml b/configs/datamodule/datasets/embeddings/bag_of_embeddings_unlabeled.yaml similarity index 52% rename from configs/datamodule/datasets/embeddings/slide_unlabeled.yaml rename to configs/datamodule/datasets/embeddings/bag_of_embeddings_unlabeled.yaml index c5605f17..fabf2a8e 100644 --- a/configs/datamodule/datasets/embeddings/slide_unlabeled.yaml +++ b/configs/datamodule/datasets/embeddings/bag_of_embeddings_unlabeled.yaml @@ -1,4 +1,4 @@ -_target_: prostate_cancer.datamodule.datasets.UnlabeledSlideEmbeddingsDataset +_target_: prostate_cancer.datamodule.datasets.UnlabeledBagOfEmbeddingsDataset uris: ??? thresholds: ${thresholds} embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/tile_labeled.yaml b/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml similarity index 62% rename from configs/datamodule/datasets/embeddings/tile_labeled.yaml rename to configs/datamodule/datasets/embeddings/embeddings_labeled.yaml index 7d6ac31c..be0aba53 100644 --- a/configs/datamodule/datasets/embeddings/tile_labeled.yaml +++ b/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml @@ -1,4 +1,4 @@ -_target_: prostate_cancer.datamodule.datasets.LabeledTileEmbeddingsDataset +_target_: prostate_cancer.datamodule.datasets.LabeledEmbeddingsDataset uris: ??? thresholds: ${thresholds} embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml b/configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml new file mode 100644 index 00000000..618f2a76 --- /dev/null +++ b/configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml @@ -0,0 +1,4 @@ +_target_: prostate_cancer.datamodule.datasets.UnlabeledEmbeddingsDataset +uris: ??? +thresholds: ${thresholds} +embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/tile_unlabeled.yaml b/configs/datamodule/datasets/embeddings/tile_unlabeled.yaml deleted file mode 100644 index 72f79123..00000000 --- a/configs/datamodule/datasets/embeddings/tile_unlabeled.yaml +++ /dev/null @@ -1,4 +0,0 @@ -_target_: prostate_cancer.datamodule.datasets.UnlabeledTileEmbeddingsDataset -uris: ??? -thresholds: ${thresholds} -embeddings_uri: ??? diff --git a/configs/datamodule/tile_embeddings_datamodule.yaml b/configs/datamodule/embeddings_datamodule.yaml similarity index 58% rename from configs/datamodule/tile_embeddings_datamodule.yaml rename to configs/datamodule/embeddings_datamodule.yaml index 2585d948..3cb01190 100644 --- a/configs/datamodule/tile_embeddings_datamodule.yaml +++ b/configs/datamodule/embeddings_datamodule.yaml @@ -1,8 +1,8 @@ defaults: - - datasets@train: embeddings/tile_labeled - - datasets@val: embeddings/tile_labeled - - datasets@test: embeddings/tile_labeled - - datasets@predict: embeddings/tile_unlabeled + - datasets@train: embeddings/embeddings_labeled + - datasets@val: embeddings/embeddings_labeled + - datasets@test: embeddings/embeddings_labeled + - datasets@predict: embeddings/embeddings_unlabeled - samplers@sampler: tile_stratified_weighted_random diff --git a/configs/datamodule/processor/google_vit.yaml b/configs/datamodule/processor/google_vit.yaml deleted file mode 100644 index dbb7a3f4..00000000 --- a/configs/datamodule/processor/google_vit.yaml +++ /dev/null @@ -1,2 +0,0 @@ -_target_: transformers.ViTImageProcessor.from_pretrained -pretrained_model_name_or_path: google/vit-base-patch16-224 \ No newline at end of file diff --git a/configs/datamodule/samplers/bag_of_tiles_stratified_weighted_random.yaml b/configs/datamodule/samplers/bag_of_tiles_stratified_weighted_random.yaml new file mode 100644 index 00000000..0dd20a5d --- /dev/null +++ b/configs/datamodule/samplers/bag_of_tiles_stratified_weighted_random.yaml @@ -0,0 +1,3 @@ +_partial_: true +_target_: prostate_cancer.datamodule.samplers.BagOfTilesStratifiedWeightedRandomSampler +replacement: true \ No newline at end of file diff --git a/configs/datamodule/samplers/slide_stratified_weighted_random.yaml b/configs/datamodule/samplers/slide_stratified_weighted_random.yaml deleted file mode 100644 index 67ce9ece..00000000 --- a/configs/datamodule/samplers/slide_stratified_weighted_random.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_partial_: true -_target_: prostate_cancer.datamodule.samplers.SlideStratifiedWeightedRandomSampler -replacement: true \ No newline at end of file diff --git a/configs/datamodule/slide_embeddings_datamodule.yaml b/configs/datamodule/slide_embeddings_datamodule.yaml deleted file mode 100644 index 845e4e17..00000000 --- a/configs/datamodule/slide_embeddings_datamodule.yaml +++ /dev/null @@ -1,12 +0,0 @@ -defaults: - - datasets@train: embeddings/slide_labeled - - datasets@val: embeddings/slide_labeled - - datasets@test: embeddings/slide_labeled - - datasets@predict: embeddings/slide_unlabeled - - - samplers@sampler: slide_stratified_weighted_random - -_target_: prostate_cancer.datamodule.SlideDataModule - -batch_size: ??? -num_workers: ??? diff --git a/configs/experiment/predict/slide_embeddings/base.yaml b/configs/experiment/predict/bag_of_embeddings/base.yaml similarity index 82% rename from configs/experiment/predict/slide_embeddings/base.yaml rename to configs/experiment/predict/bag_of_embeddings/base.yaml index ca6ab9e1..b4f58ec3 100644 --- a/configs/experiment/predict/slide_embeddings/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/predict/base - - /datamodule/slide_embeddings_datamodule@datamodule + - /datamodule/bag_of_embeddings_datamodule@datamodule - _self_ slide_threshold: ${model_data.slide_threshold} diff --git a/configs/experiment/predict/slide_embeddings/pgp/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml similarity index 80% rename from configs/experiment/predict/slide_embeddings/pgp/base.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/base.yaml index ab464e53..eae7ce26 100644 --- a/configs/experiment/predict/slide_embeddings/pgp/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/predict/slide_embeddings/base + - /experiment/predict/bag_of_embeddings/base - /model/pgp_mil_model@model - /model/checkpoints/pgp_mil@model_data - _self_ diff --git a/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml similarity index 68% rename from configs/experiment/predict/slide_embeddings/pgp/mil_predictions/base.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml index 2919eb71..35419b5f 100644 --- a/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/mil_predictions@callbacks - - /experiment/predict/slide_embeddings/pgp/base + - /experiment/predict/bag_of_embeddings/pgp/base - _self_ use_case: "MIL predictions" diff --git a/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml similarity index 53% rename from configs/experiment/predict/slide_embeddings/pgp/mil_predictions/mmci_tl_val.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml index 4e143054..353cf3c1 100644 --- a/configs/experiment/predict/slide_embeddings/pgp/mil_predictions/mmci_tl_val.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/predict/slide_embeddings/pgp/mil_predictions/base + - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/base - /data/mmci_tl_val@predict_data - _self_ diff --git a/configs/experiment/predict/slide_embeddings/virchow2/base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml similarity index 82% rename from configs/experiment/predict/slide_embeddings/virchow2/base.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml index 3dc72276..90fbdb13 100644 --- a/configs/experiment/predict/slide_embeddings/virchow2/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/predict/slide_embeddings/base + - /experiment/predict/bag_of_embeddings/base - /model/virchow2_mil_model@model - /model/checkpoints/mmci_tl/virchow2_mil@model_data - _self_ diff --git a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base.yaml similarity index 68% rename from configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base.yaml index 013014d2..577459d9 100644 --- a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/mil_prediction_callback@callbacks - - /experiment/predict/slide_embeddings/virchow2/base + - /experiment/predict/bag_of_embeddings/virchow2/base - _self_ use_case: "MIL predictions" diff --git a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml similarity index 51% rename from configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml index f08ac4b0..d8acc5e3 100644 --- a/configs/experiment/predict/slide_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/predict/slide_embeddings/virchow2/mil_predictions/base + - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base - /data/mmci/tl_val@predict_data - _self_ diff --git a/configs/experiment/predict/embeddings/base.yaml b/configs/experiment/predict/embeddings/base.yaml index e7d12d6d..9f2cc6b0 100644 --- a/configs/experiment/predict/embeddings/base.yaml +++ b/configs/experiment/predict/embeddings/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/predict/base - - /datamodule/tile_embeddings_datamodule@datamodule + - /datamodule/embeddings_datamodule@datamodule - _self_ datamodule: diff --git a/configs/experiment/test/embeddings/base.yaml b/configs/experiment/test/embeddings/base.yaml index 47ba01f4..48c2d59e 100644 --- a/configs/experiment/test/embeddings/base.yaml +++ b/configs/experiment/test/embeddings/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/test/base - - /datamodule/tile_embeddings_datamodule@datamodule + - /datamodule/embeddings_datamodule@datamodule - _self_ datamodule: diff --git a/configs/experiment/train/slide_embeddings/base.yaml b/configs/experiment/train/bag_of_embeddings/base.yaml similarity index 90% rename from configs/experiment/train/slide_embeddings/base.yaml rename to configs/experiment/train/bag_of_embeddings/base.yaml index ec8a6cff..44e64164 100644 --- a/configs/experiment/train/slide_embeddings/base.yaml +++ b/configs/experiment/train/bag_of_embeddings/base.yaml @@ -4,7 +4,7 @@ defaults: - /experiment/train/base - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - - /datamodule/slide_embeddings_datamodule@datamodule + - /datamodule/bag_of_embeddings_datamodule@datamodule - _self_ model: diff --git a/configs/experiment/train/slide_embeddings/pgp.yaml b/configs/experiment/train/bag_of_embeddings/pgp.yaml similarity index 84% rename from configs/experiment/train/slide_embeddings/pgp.yaml rename to configs/experiment/train/bag_of_embeddings/pgp.yaml index c8d7f041..91b4197d 100644 --- a/configs/experiment/train/slide_embeddings/pgp.yaml +++ b/configs/experiment/train/bag_of_embeddings/pgp.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/slide_embeddings/base + - /experiment/train/bag_of_embeddings/base - /model/pgp_mil_model@model - _self_ diff --git a/configs/experiment/train/slide_embeddings/virchow2.yaml b/configs/experiment/train/bag_of_embeddings/virchow2.yaml similarity index 85% rename from configs/experiment/train/slide_embeddings/virchow2.yaml rename to configs/experiment/train/bag_of_embeddings/virchow2.yaml index a55911be..79bd968e 100644 --- a/configs/experiment/train/slide_embeddings/virchow2.yaml +++ b/configs/experiment/train/bag_of_embeddings/virchow2.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/slide_embeddings/base + - /experiment/train/bag_of_embeddings/base - /model/virchow2_mil_model@model - _self_ diff --git a/configs/experiment/train/tile_embeddings/base.yaml b/configs/experiment/train/embeddings/base.yaml similarity index 56% rename from configs/experiment/train/tile_embeddings/base.yaml rename to configs/experiment/train/embeddings/base.yaml index 5883db90..1d7f33ce 100644 --- a/configs/experiment/train/tile_embeddings/base.yaml +++ b/configs/experiment/train/embeddings/base.yaml @@ -2,5 +2,5 @@ defaults: - /experiment/train/base - - /datamodule/tile_embeddings_datamodule@datamodule + - /datamodule/embeddings_datamodule@datamodule - _self_ diff --git a/configs/experiment/train/tile_embeddings/pgp/base.yaml b/configs/experiment/train/embeddings/pgp/base.yaml similarity index 83% rename from configs/experiment/train/tile_embeddings/pgp/base.yaml rename to configs/experiment/train/embeddings/pgp/base.yaml index fd2caf84..9c9f7267 100644 --- a/configs/experiment/train/tile_embeddings/pgp/base.yaml +++ b/configs/experiment/train/embeddings/pgp/base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/tile_embeddings/base + - /experiment/train/embeddings/base - /model/pgp_model@model - _self_ diff --git a/configs/experiment/train/tile_embeddings/pgp/mmci.yaml b/configs/experiment/train/embeddings/pgp/mmci.yaml similarity index 69% rename from configs/experiment/train/tile_embeddings/pgp/mmci.yaml rename to configs/experiment/train/embeddings/pgp/mmci.yaml index 683ea921..d843b226 100644 --- a/configs/experiment/train/tile_embeddings/pgp/mmci.yaml +++ b/configs/experiment/train/embeddings/pgp/mmci.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/tile_embeddings/pgp/base + - /experiment/train/embeddings/pgp/base - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - _self_ diff --git a/configs/experiment/train/tile_embeddings/virchow2/base.yaml b/configs/experiment/train/embeddings/virchow2/base.yaml similarity index 84% rename from configs/experiment/train/tile_embeddings/virchow2/base.yaml rename to configs/experiment/train/embeddings/virchow2/base.yaml index 6871ec30..b3c40ba3 100644 --- a/configs/experiment/train/tile_embeddings/virchow2/base.yaml +++ b/configs/experiment/train/embeddings/virchow2/base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/tile_embeddings/base + - /experiment/train/embeddings/base - /model/virchow2_model@model - _self_ diff --git a/configs/experiment/train/tile_embeddings/virchow2/mmci.yaml b/configs/experiment/train/embeddings/virchow2/mmci.yaml similarity index 67% rename from configs/experiment/train/tile_embeddings/virchow2/mmci.yaml rename to configs/experiment/train/embeddings/virchow2/mmci.yaml index 30c17548..983f7e09 100644 --- a/configs/experiment/train/tile_embeddings/virchow2/mmci.yaml +++ b/configs/experiment/train/embeddings/virchow2/mmci.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/tile_embeddings/virchow2/base + - /experiment/train/embeddings/virchow2/base - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - _self_ diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index a0920306..6206fded 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -18,9 +18,9 @@ ) from prostate_cancer.typing import ( - LabeledSlideSampleBatch, + LabeledBagOfTilesSampleBatch, MILModelOutput, - UnlabeledSlideSampleBatch, + UnlabeledBagOfTilesSampleBatch, ) @@ -136,7 +136,7 @@ def forward(self, x: Tensor) -> MILModelOutput: attention_weights.squeeze(-1), ) # (batch_size,), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded) - def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: + def training_step(self, batch: LabeledBagOfTilesSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide bags, tl_labels, sl_labels, _ = batch @@ -166,7 +166,7 @@ def training_step(self, batch: LabeledSlideSampleBatch) -> Tensor: return loss - def validation_step(self, batch: LabeledSlideSampleBatch) -> None: + def validation_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: bags, tl_labels, sl_labels, _ = batch sl_outputs, tl_outputs, mask, _ = self(bags) @@ -201,7 +201,7 @@ def validation_step(self, batch: LabeledSlideSampleBatch) -> None: self.val_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) - def test_step(self, batch: LabeledSlideSampleBatch) -> None: + def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: bags, tl_labels, sl_labels, _ = batch sl_outputs, tl_outputs, mask, _ = self(bags) @@ -216,7 +216,7 @@ def test_step(self, batch: LabeledSlideSampleBatch) -> None: self.test_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) - def predict_step(self, batch: UnlabeledSlideSampleBatch) -> MILModelOutput: + def predict_step(self, batch: UnlabeledBagOfTilesSampleBatch) -> MILModelOutput: sl_preds_raw, tl_preds_raw, mask, attention = self(batch[0]) return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid(), mask, attention diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index e7f88024..e90dd4ec 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -8,11 +8,11 @@ from rationai.masks.mask_builders import ScalarMaskBuilder from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger -from prostate_cancer.typing import MILModelOutput, UnlabeledSlideSampleBatch +from prostate_cancer.typing import MILModelOutput, UnlabeledBagOfTilesSampleBatch if TYPE_CHECKING: - from prostate_cancer.datamodule import SlideDataModule + from prostate_cancer.datamodule import BagOfTilesDataModule def min_max_normalization(tensor: torch.Tensor) -> torch.Tensor: @@ -28,7 +28,7 @@ def get_mask_builder( if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") - datamodule = cast("SlideDataModule", trainer.datamodule) + datamodule = cast("BagOfTilesDataModule", trainer.datamodule) slides = cast("pd.DataFrame", datamodule.predict.slides) slides["name"] = slides["path"].apply(lambda x: Path(x).stem) @@ -54,7 +54,7 @@ def on_predict_batch_end( trainer: Trainer, pl_module: LightningModule, outputs: MILModelOutput, - batch: UnlabeledSlideSampleBatch, + batch: UnlabeledBagOfTilesSampleBatch, batch_idx: int, dataloader_idx: int = 0, ) -> None: diff --git a/prostate_cancer/datamodule/__init__.py b/prostate_cancer/datamodule/__init__.py index 3c622ea3..7cd2bd5c 100644 --- a/prostate_cancer/datamodule/__init__.py +++ b/prostate_cancer/datamodule/__init__.py @@ -1,5 +1,5 @@ -from prostate_cancer.datamodule.slide_data_module import SlideDataModule +from prostate_cancer.datamodule.bag_of_tiles_data_module import BagOfTilesDataModule from prostate_cancer.datamodule.tile_data_module import TileDataModule -__all__ = ["SlideDataModule", "TileDataModule"] +__all__ = ["BagOfTilesDataModule", "TileDataModule"] diff --git a/prostate_cancer/datamodule/slide_data_module.py b/prostate_cancer/datamodule/bag_of_tiles_data_module.py similarity index 77% rename from prostate_cancer/datamodule/slide_data_module.py rename to prostate_cancer/datamodule/bag_of_tiles_data_module.py index b6e72e1a..3879b1ed 100644 --- a/prostate_cancer/datamodule/slide_data_module.py +++ b/prostate_cancer/datamodule/bag_of_tiles_data_module.py @@ -10,19 +10,19 @@ if TYPE_CHECKING: from prostate_cancer.datamodule.datasets import ( - LabeledSlideEmbeddingsDataset, - UnlabeledSlideEmbeddingsDataset, + LabeledBagOfEmbeddingsDataset, + UnlabeledBagOfEmbeddingsDataset, ) from prostate_cancer.typing import ( - LabeledSlideSample, - LabeledSlideSampleBatch, - UnlabeledSlideSample, - UnlabeledSlideSampleBatch, + LabeledBagOfTilesSample, + LabeledBagOfTilesSampleBatch, + UnlabeledBagOfTilesSample, + UnlabeledBagOfTilesSampleBatch, ) -class SlideDataModule(LightningDataModule): +class BagOfTilesDataModule(LightningDataModule): def __init__( self, batch_size: int, @@ -40,30 +40,30 @@ def setup(self, stage: str) -> None: match stage: case "fit": self.train = cast( - "LabeledSlideEmbeddingsDataset", + "LabeledBagOfEmbeddingsDataset", instantiate(self.datasets["train"]), ) self.val = cast( - "LabeledSlideEmbeddingsDataset", + "LabeledBagOfEmbeddingsDataset", instantiate(self.datasets["val"]), ) case "val": self.val = cast( - "LabeledSlideEmbeddingsDataset", + "LabeledBagOfEmbeddingsDataset", instantiate(self.datasets["val"]), ) case "test": self.test = cast( - "LabeledSlideEmbeddingsDataset", + "LabeledBagOfEmbeddingsDataset", instantiate(self.datasets["test"]), ) case "predict": self.predict = cast( - "UnlabeledSlideEmbeddingsDataset", + "UnlabeledBagOfEmbeddingsDataset", instantiate(self.datasets["predict"]), ) - def train_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: + def train_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: if self.sampler_partial: sampler = instantiate(self.sampler_partial)( @@ -85,7 +85,7 @@ def train_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: drop_last=True, ) - def val_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: + def val_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: return DataLoader( self.val, batch_size=self.batch_size, @@ -94,7 +94,7 @@ def val_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: persistent_workers=self.num_workers > 0, ) - def test_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: + def test_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: return DataLoader( self.test, batch_size=self.batch_size, @@ -103,7 +103,7 @@ def test_dataloader(self) -> Iterable[LabeledSlideSampleBatch]: persistent_workers=self.num_workers > 0, ) - def predict_dataloader(self) -> Iterable[UnlabeledSlideSampleBatch]: + def predict_dataloader(self) -> Iterable[UnlabeledBagOfTilesSampleBatch]: return DataLoader( self.predict, batch_size=self.batch_size, @@ -113,7 +113,9 @@ def predict_dataloader(self) -> Iterable[UnlabeledSlideSampleBatch]: ) -def collate_fn_labeled(batch: list[LabeledSlideSample]) -> LabeledSlideSampleBatch: +def collate_fn_labeled( + batch: list[LabeledBagOfTilesSample], +) -> LabeledBagOfTilesSampleBatch: inputs = [] sl_labels = [] tl_labels = [] @@ -131,8 +133,8 @@ def collate_fn_labeled(batch: list[LabeledSlideSample]) -> LabeledSlideSampleBat def collate_fn_unlabeled( - batch: list[UnlabeledSlideSample], -) -> UnlabeledSlideSampleBatch: + batch: list[UnlabeledBagOfTilesSample], +) -> UnlabeledBagOfTilesSampleBatch: inputs = [] metadatas = [] for input, metadata in batch: diff --git a/prostate_cancer/datamodule/datasets/__init__.py b/prostate_cancer/datamodule/datasets/__init__.py index ea76019e..32d3833b 100644 --- a/prostate_cancer/datamodule/datasets/__init__.py +++ b/prostate_cancer/datamodule/datasets/__init__.py @@ -1,22 +1,22 @@ -from prostate_cancer.datamodule.datasets.slide_embeddings_dataset import ( - LabeledSlideEmbeddingsDataset, - UnlabeledSlideEmbeddingsDataset, +from prostate_cancer.datamodule.datasets.bag_of_embeddings_dataset import ( + LabeledBagOfEmbeddingsDataset, + UnlabeledBagOfEmbeddingsDataset, +) +from prostate_cancer.datamodule.datasets.embeddings_dataset import ( + LabeledEmbeddingsDataset, + UnlabeledEmbeddingsDataset, ) from prostate_cancer.datamodule.datasets.tile_dataset import ( LabeledTilesDataset, UnlabeledTilesDataset, ) -from prostate_cancer.datamodule.datasets.tile_embeddings_dataset import ( - LabeledTileEmbeddingsDataset, - UnlabeledTileEmbeddingsDataset, -) __all__ = [ - "LabeledSlideEmbeddingsDataset", - "LabeledTileEmbeddingsDataset", + "LabeledBagOfEmbeddingsDataset", + "LabeledEmbeddingsDataset", "LabeledTilesDataset", - "UnlabeledSlideEmbeddingsDataset", - "UnlabeledTileEmbeddingsDataset", + "UnlabeledBagOfEmbeddingsDataset", + "UnlabeledEmbeddingsDataset", "UnlabeledTilesDataset", ] diff --git a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py similarity index 92% rename from prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py rename to prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index 4be77a16..6a3461db 100644 --- a/prostate_cancer/datamodule/datasets/slide_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -13,13 +13,13 @@ from prostate_cancer.datamodule.datasets.base import filter_tiles_by_thresholds from prostate_cancer.typing import ( - LabeledSlideSample, + LabeledBagOfTilesSample, SlideMetadata, - UnlabeledSlideSample, + UnlabeledBagOfTilesSample, ) -T = TypeVar("T", bound=LabeledSlideSample | UnlabeledSlideSample) +T = TypeVar("T", bound=LabeledBagOfTilesSample | UnlabeledBagOfTilesSample) def get_embedding_ceiling(tiles: pd.DataFrame, thresholds: dict[str, float]) -> int: @@ -27,7 +27,7 @@ def get_embedding_ceiling(tiles: pd.DataFrame, thresholds: dict[str, float]) -> return filtered_tiles["slide_id"].value_counts().max() -class SlideEmbeddingsDataset(Dataset[T], Generic[T]): +class BagOfEmbeddingsDataset(Dataset[T], Generic[T]): def __init__( self, thresholds: dict[str, float], @@ -114,7 +114,7 @@ def __getitem__(self, idx: int) -> T: return slide_embeddings, tl_labels, sl_label, metadata # type: ignore[return-value] -class LabeledSlideEmbeddingsDataset(SlideEmbeddingsDataset[LabeledSlideSample]): +class LabeledBagOfEmbeddingsDataset(BagOfEmbeddingsDataset[LabeledBagOfTilesSample]): def __init__( self, thresholds: dict[str, float], @@ -131,7 +131,9 @@ def __init__( ) -class UnlabeledSlideEmbeddingsDataset(SlideEmbeddingsDataset[UnlabeledSlideSample]): +class UnlabeledBagOfEmbeddingsDataset( + BagOfEmbeddingsDataset[UnlabeledBagOfTilesSample] +): def __init__( self, thresholds: dict[str, float], diff --git a/prostate_cancer/datamodule/datasets/tile_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/embeddings_dataset.py similarity index 95% rename from prostate_cancer/datamodule/datasets/tile_embeddings_dataset.py rename to prostate_cancer/datamodule/datasets/embeddings_dataset.py index fc62f071..444b218f 100644 --- a/prostate_cancer/datamodule/datasets/tile_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/embeddings_dataset.py @@ -22,7 +22,7 @@ T = TypeVar("T", covariant=True) -class TileEmbeddingsDataset(FilterableDataset[T]): +class EmbeddingsDataset(FilterableDataset[T]): def __init__( self, uris: Iterable[str], @@ -101,10 +101,10 @@ def _filter_tiles_embeddings_by_slide( return slide_tiles, slide_embeddings -class LabeledTileEmbeddingsDataset(TileEmbeddingsDataset[LabeledTileSample]): ... +class LabeledEmbeddingsDataset(EmbeddingsDataset[LabeledTileSample]): ... -class UnlabeledTileEmbeddingsDataset(TileEmbeddingsDataset[UnlabeledTileSample]): ... +class UnlabeledEmbeddingsDataset(EmbeddingsDataset[UnlabeledTileSample]): ... # dataset of tile embeddings for a single slide diff --git a/prostate_cancer/datamodule/samplers/__init__.py b/prostate_cancer/datamodule/samplers/__init__.py index 0f8f1fce..cc5d8754 100644 --- a/prostate_cancer/datamodule/samplers/__init__.py +++ b/prostate_cancer/datamodule/samplers/__init__.py @@ -1,5 +1,5 @@ -from prostate_cancer.datamodule.samplers.slide_stratified_weighted_random_sampler import ( - SlideStratifiedWeightedRandomSampler, +from prostate_cancer.datamodule.samplers.bag_of_tiles_stratified_weighted_random_sampler import ( + BagOfTilesStratifiedWeightedRandomSampler, ) from prostate_cancer.datamodule.samplers.tile_stratified_weighted_random_sampler import ( TileStratifiedWeightedRandomSampler, @@ -7,6 +7,6 @@ __all__ = [ - "SlideStratifiedWeightedRandomSampler", + "BagOfTilesStratifiedWeightedRandomSampler", "TileStratifiedWeightedRandomSampler", ] diff --git a/prostate_cancer/datamodule/samplers/slide_stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py similarity index 77% rename from prostate_cancer/datamodule/samplers/slide_stratified_weighted_random_sampler.py rename to prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py index 4019e1b4..e363e6a5 100644 --- a/prostate_cancer/datamodule/samplers/slide_stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py @@ -3,13 +3,13 @@ import pandas as pd from torch.utils.data import WeightedRandomSampler -from prostate_cancer.datamodule.datasets import LabeledSlideEmbeddingsDataset +from prostate_cancer.datamodule.datasets import LabeledBagOfEmbeddingsDataset -class SlideStratifiedWeightedRandomSampler(WeightedRandomSampler): +class BagOfTilesStratifiedWeightedRandomSampler(WeightedRandomSampler): def __init__( self, - dataset: LabeledSlideEmbeddingsDataset, + dataset: LabeledBagOfEmbeddingsDataset, target_col: str, replacement: bool = True, ) -> None: diff --git a/prostate_cancer/modeling/decode_head/__init__.py b/prostate_cancer/modeling/decode_head/__init__.py index b638642a..a964ca41 100644 --- a/prostate_cancer/modeling/decode_head/__init__.py +++ b/prostate_cancer/modeling/decode_head/__init__.py @@ -3,15 +3,12 @@ from prostate_cancer.modeling.decode_head.embedding_classifier import ( BinaryEmbeddingClassifier, ) -from prostate_cancer.modeling.decode_head.mil_tl_classifier import ( - BinaryMILEmbeddingClassifier, -) +from prostate_cancer.modeling.decode_head.vit_classifier import BinaryViTClassifier __all__ = [ "BinaryCNNClassifier", "BinaryClassifier", "BinaryEmbeddingClassifier", - "BinaryMILEmbeddingClassifier", "BinaryViTClassifier", ] diff --git a/prostate_cancer/modeling/decode_head/mil_tl_classifier.py b/prostate_cancer/modeling/decode_head/mil_tl_classifier.py deleted file mode 100644 index 6c72b42d..00000000 --- a/prostate_cancer/modeling/decode_head/mil_tl_classifier.py +++ /dev/null @@ -1,13 +0,0 @@ -from torch import Tensor - -from prostate_cancer.modeling.decode_head.binary_classifier import BinaryClassifier - - -class BinaryMILEmbeddingClassifier(BinaryClassifier): - def forward(self, x: Tensor) -> Tensor: - if x.ndim != 3: - raise ValueError(f"Expected 3D tensor, got {x.ndim}D") - - x = self.dropout(x) - x = self.proj(x) - return x diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 1066e4f9..d310714a 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -30,11 +30,15 @@ class SlideMetadata(TypedDict): ys: Tensor -LabeledSlideSample = tuple[Tensor, Tensor, Tensor, SlideMetadata] -UnlabeledSlideSample = tuple[Tensor, SlideMetadata] - -LabeledSlideSampleBatch: TypeAlias = tuple[Tensor, Tensor, Tensor, list[SlideMetadata]] -UnlabeledSlideSampleBatch: TypeAlias = tuple[Tensor, list[SlideMetadata]] +LabeledBagOfTilesSample = tuple[ + Tensor, Tensor, Tensor, SlideMetadata +] # tiles / embeddings, tl_labels, sl_labels, metadata +UnlabeledBagOfTilesSample = tuple[Tensor, SlideMetadata] # tiles / embeddings, metadata + +LabeledBagOfTilesSampleBatch: TypeAlias = tuple[ + Tensor, Tensor, Tensor, list[SlideMetadata] +] +UnlabeledBagOfTilesSampleBatch: TypeAlias = tuple[Tensor, list[SlideMetadata]] MILModelOutput = tuple[ Tensor, Tensor, Tensor, Tensor From 9b0e8827ed38debed91cd366cb37373070d8201f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 12 Jun 2026 10:20:35 +0000 Subject: [PATCH 081/284] feat: comment --- prostate_cancer/attention_mil_model.py | 38 ++++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 6206fded..3c0ea630 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -98,27 +98,26 @@ def forward(self, x: Tensor) -> MILModelOutput: # Just identity x = self.encoder(x) # (batch_size, num_tiles_padded, embedding_dim) - # TL weights (which tiles to attend to) - raw_attn = self.attention(x) # (batch_size, num_tiles_padded, 1) - - # sigmoid is applied to avoid unimodal spiky distribution - attention_weights = torch.softmax( - raw_attn.sigmoid(), dim=1 - ) # (batch_size, num_tiles_padded, 1) - # Do not attend to padded tiles (true for non-padded elements) mask = ( (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() ) # (batch_size, num_tiles_padded, 1) - attention_weights = ( - attention_weights * mask - ) # (batch_size, num_tiles_padded, 1) + # number of non-padded tiles for each bag + sqrt_bag_size = ( + mask.sum(dim=1, keepdim=True).sqrt().clamp(min=1) + ) # (batch_size, 1, 1) - # proper distribution for the non-padded tiles - attention_weights = attention_weights / attention_weights.sum( - dim=1, keepdim=True - ) # (batch_size, num_tiles_padded, 1) + # TL weights (which tiles to attend to) + raw_attn: Tensor = self.attention(x) # (batch_size, num_tiles_padded, 1) + raw_attn = raw_attn.squeeze(-1) # (batch_size, num_tiles_padded) + + raw_attn = raw_attn.masked_fill(~mask.squeeze(-1), float("-inf")) + + # make it a distribution + attention_weights = torch.softmax( + raw_attn / sqrt_bag_size.squeeze(-1), dim=1 + ).unsqueeze(-1) # (batch_size, num_tiles_padded, 1) # TL predictions tl_preds_raw: Tensor = self.classifier(x) # (batch_size, num_tiles_padded, 1) @@ -141,9 +140,12 @@ def training_step(self, batch: LabeledBagOfTilesSampleBatch) -> Tensor: bags, tl_labels, sl_labels, _ = batch sl_outputs, tl_outputs, mask, _ = self(bags) - sl_loss = self.sl_criterion(sl_outputs, sl_labels) - tl_loss_all = self.tl_criterion(tl_outputs, tl_labels) - tl_loss = (tl_loss_all * mask).sum() / mask.sum() + sl_loss = self.sl_criterion(sl_outputs, sl_labels) # scalar + tl_loss_all = self.tl_criterion( + tl_outputs, tl_labels + ) # (batch_size, num_tiles_padded) + per_bag_tl_loss = (tl_loss_all * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1) + tl_loss = per_bag_tl_loss.mean() loss = sl_loss + tl_loss self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) From cd8c6cfa7338c42cc2200bc16611d125372f6c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 12 Jun 2026 10:23:40 +0000 Subject: [PATCH 082/284] feat: models --- configs/model/checkpoints/mmci_tl/pgp_mil.yaml | 9 +++++++++ configs/model/checkpoints/mmci_tl/virchow2_mil.yaml | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 configs/model/checkpoints/mmci_tl/pgp_mil.yaml diff --git a/configs/model/checkpoints/mmci_tl/pgp_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_mil.yaml new file mode 100644 index 00000000..86ee2b9f --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/pgp_mil.yaml @@ -0,0 +1,9 @@ +defaults: + - base + - _self_ + +tile_threshold: 0.5 +slide_threshold: 0.5 + +checkpoint: "mlflow-artifacts:/65/7cb992adfe4b422790becc5984ff2a6f/artifacts/checkpoints/epoch=8-step=1583/checkpoint.ckpt" +model_name: "PGP (Hybrid-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml index 50b5e202..72215900 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml @@ -5,5 +5,5 @@ defaults: tile_threshold: 0.5 slide_threshold: 0.5 -checkpoint: "mlflow-artifacts:/65/1d58d8b3d3c347a9be88b25822313d98/artifacts/checkpoints/epoch=2-step=452/checkpoint.ckpt" -model_name: "Virchow2 (E-MIL)" +checkpoint: "mlflow-artifacts:/65/5e0c1d4699ca49c4938baf3c65e7f61c/artifacts/checkpoints/epoch=6-step=1176/checkpoint.ckpt" +model_name: "Virchow2 (Hybrid-MIL)" From bbda8d2dc136f4322e4ff8c2e3bc4304300fda4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 12 Jun 2026 10:59:15 +0000 Subject: [PATCH 083/284] fix: bool array --- prostate_cancer/attention_mil_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 3c0ea630..569846a1 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -112,7 +112,7 @@ def forward(self, x: Tensor) -> MILModelOutput: raw_attn: Tensor = self.attention(x) # (batch_size, num_tiles_padded, 1) raw_attn = raw_attn.squeeze(-1) # (batch_size, num_tiles_padded) - raw_attn = raw_attn.masked_fill(~mask.squeeze(-1), float("-inf")) + raw_attn = raw_attn.masked_fill(~mask.bool().squeeze(-1), float("-inf")) # make it a distribution attention_weights = torch.softmax( From ef7068b3354e7a9d069cc8cac6d8e94cfd1b84ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 13 Jun 2026 10:29:36 +0000 Subject: [PATCH 084/284] feat: tile filtering --- .../filter_tiles/mmci_tl_test.yaml | 5 ++ .../filter_tiles/mmci_tl_train.yaml | 5 ++ .../filter_tiles/mmci_tl_val.yaml | 5 ++ configs/preprocessing/filter_tiles.yaml | 5 ++ preprocessing/filter_tiles.py | 55 +++++++++++++++++++ preprocessing/masks/tile_masks.py | 27 ++++++++- 6 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 configs/experiment/preprocessing/filter_tiles/mmci_tl_test.yaml create mode 100644 configs/experiment/preprocessing/filter_tiles/mmci_tl_train.yaml create mode 100644 configs/experiment/preprocessing/filter_tiles/mmci_tl_val.yaml create mode 100644 configs/preprocessing/filter_tiles.yaml create mode 100644 preprocessing/filter_tiles.py diff --git a/configs/experiment/preprocessing/filter_tiles/mmci_tl_test.yaml b/configs/experiment/preprocessing/filter_tiles/mmci_tl_test.yaml new file mode 100644 index 00000000..b72b6113 --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/mmci_tl_test.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/tl_test@data + - _self_ diff --git a/configs/experiment/preprocessing/filter_tiles/mmci_tl_train.yaml b/configs/experiment/preprocessing/filter_tiles/mmci_tl_train.yaml new file mode 100644 index 00000000..6fd82935 --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/mmci_tl_train.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/tl_train@data + - _self_ diff --git a/configs/experiment/preprocessing/filter_tiles/mmci_tl_val.yaml b/configs/experiment/preprocessing/filter_tiles/mmci_tl_val.yaml new file mode 100644 index 00000000..a9502109 --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/mmci_tl_val.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/tl_val@data + - _self_ diff --git a/configs/preprocessing/filter_tiles.yaml b/configs/preprocessing/filter_tiles.yaml new file mode 100644 index 00000000..697f6395 --- /dev/null +++ b/configs/preprocessing/filter_tiles.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +data: ??? # instance from data/ directory + +use_case: "Filter Tiles" diff --git a/preprocessing/filter_tiles.py b/preprocessing/filter_tiles.py new file mode 100644 index 00000000..d21a0f2c --- /dev/null +++ b/preprocessing/filter_tiles.py @@ -0,0 +1,55 @@ +"""Script for tiles filtering based on estimated thresholds of overlaps with masks.""" + +from pathlib import Path + +import hydra +import mlflow +import pandas as pd +from omegaconf import DictConfig +from rationai.mlkit import autolog, with_cli_args +from rationai.mlkit.lightning.loggers import MLFlowLogger +from rationai.tiling.writers import save_mlflow_dataset + + +def filter_and_log( + tiling_uri: str, thresholds: dict[str, int], dataset_name: str +) -> None: + tiling_path = Path(mlflow.artifacts.download_artifacts(tiling_uri)) + slides = pd.read_parquet(tiling_path / "slides.parquet") + tiles = pd.read_parquet(tiling_path / "tiles.parquet") + + for col in tiles.columns: + if col.endswith("percentage"): + t = col.replace("percentage", "t") + assert t in thresholds, f"{t} for {col}" + mask = ( + tiles[col] > thresholds[t] + if "tissue" in col + else tiles[col] <= thresholds[t] + ) + tiles = tiles[mask] + + save_mlflow_dataset(slides, tiles, f"{dataset_name}_normalized") + + +@with_cli_args(["+preprocessing=filter_tiles"]) +@hydra.main(config_path="../configs", config_name="preprocessing", version_base=None) +@autolog +def main(config: DictConfig, logger: MLFlowLogger) -> None: + if hasattr(config.data, "tiles_uri_512") and config.data.tiles_uri_512 is not None: + filter_and_log( + config.data.tiles_uri_512, + config.data.thresholds, + config.data.data_name + "512", + ) + + if hasattr(config.data, "tiles_uri_224") and config.data.tiles_uri_224 is not None: + filter_and_log( + config.data.tiles_uri_224, + config.data.thresholds, + config.data.data_name + "224", + ) + + +if __name__ == "__main__": + main() diff --git a/preprocessing/masks/tile_masks.py b/preprocessing/masks/tile_masks.py index 73853119..aa3337af 100644 --- a/preprocessing/masks/tile_masks.py +++ b/preprocessing/masks/tile_masks.py @@ -24,10 +24,10 @@ def process_slide( tiles: pd.DataFrame, ) -> None: slide_tiles = tiles[tiles["slide_id"] == slide.id] + filename = f"{Path(slide.path).stem}.tiff" # --- Percentage masks for percentage_col in percentage_cols: - filename = f"{Path(slide.path).stem}.tiff" save_dir = output_path / percentage_col builder = ScalarMaskBuilder( @@ -55,7 +55,7 @@ def process_slide( size=(slide.extent_x, slide.extent_y), ) - mask_path = output_path / "outlines" / f"{Path(slide.path).stem}.tiff" + mask_path = output_path / "outlines" / filename write_big_tiff( pyvips.Image.new_from_array(np.array(mask)), mask_path, @@ -64,6 +64,28 @@ def process_slide( ) # --- + # --- survival binary masks + mask_path = output_path / "filled" / filename + builder = ScalarMaskBuilder( + output_path, + filename, + slide.extent_x, + slide.extent_y, + slide.mpp_x, + slide.mpp_y, + slide.tile_extent_x, + slide.stride_x, + ) + + data = torch.ones(len(slide_tiles)) * 255 + xs = torch.tensor(slide_tiles["x"].values) + ys = torch.tensor(slide_tiles["y"].values) + builder.update(data, xs, ys) + + builder.save() + # --- + + @with_cli_args(["+preprocessing=tile_masks"]) @hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) @@ -79,6 +101,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: for percentage_col in [ *config.percentage_cols, "outlines", + "filled", ]: (output_path / percentage_col).mkdir(parents=True, exist_ok=True) From d5fad1b6e119b9db583d96eb766685946c9cbb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 13 Jun 2026 10:30:30 +0000 Subject: [PATCH 085/284] feat: mmci sl --- .../preprocessing/filter_tiles/mmci_sl_group_1.yaml | 5 +++++ .../preprocessing/filter_tiles/mmci_sl_group_2.yaml | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 configs/experiment/preprocessing/filter_tiles/mmci_sl_group_1.yaml create mode 100644 configs/experiment/preprocessing/filter_tiles/mmci_sl_group_2.yaml diff --git a/configs/experiment/preprocessing/filter_tiles/mmci_sl_group_1.yaml b/configs/experiment/preprocessing/filter_tiles/mmci_sl_group_1.yaml new file mode 100644 index 00000000..f711bfba --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/mmci_sl_group_1.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/sl_group_1@data + - _self_ diff --git a/configs/experiment/preprocessing/filter_tiles/mmci_sl_group_2.yaml b/configs/experiment/preprocessing/filter_tiles/mmci_sl_group_2.yaml new file mode 100644 index 00000000..ddc7face --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/mmci_sl_group_2.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/sl_group_2@data + - _self_ From 74c15d4added36f509782d31c7dd1596a779a9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 08:01:45 +0000 Subject: [PATCH 086/284] feat: filtering --- .../filter_embeddings/mmci_sl_group_1.yaml | 5 ++ .../filter_embeddings/mmci_sl_group_2.yaml | 5 ++ .../filter_embeddings/mmci_tl_test.yaml | 5 ++ .../filter_embeddings/mmci_tl_train.yaml | 5 ++ .../filter_embeddings/mmci_tl_val.yaml | 5 ++ configs/preprocessing/filter_embeddings.yaml | 5 ++ preprocessing/embeddings/filter_embeddings.py | 77 +++++++++++++++++++ preprocessing/masks/tile_masks.py | 1 - preprocessing/{ => tiling_v2}/filter_tiles.py | 22 +++--- 9 files changed, 120 insertions(+), 10 deletions(-) create mode 100644 configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_1.yaml create mode 100644 configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_2.yaml create mode 100644 configs/experiment/preprocessing/filter_embeddings/mmci_tl_test.yaml create mode 100644 configs/experiment/preprocessing/filter_embeddings/mmci_tl_train.yaml create mode 100644 configs/experiment/preprocessing/filter_embeddings/mmci_tl_val.yaml create mode 100644 configs/preprocessing/filter_embeddings.yaml create mode 100644 preprocessing/embeddings/filter_embeddings.py rename preprocessing/{ => tiling_v2}/filter_tiles.py (85%) diff --git a/configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_1.yaml b/configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_1.yaml new file mode 100644 index 00000000..f711bfba --- /dev/null +++ b/configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_1.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/sl_group_1@data + - _self_ diff --git a/configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_2.yaml b/configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_2.yaml new file mode 100644 index 00000000..ddc7face --- /dev/null +++ b/configs/experiment/preprocessing/filter_embeddings/mmci_sl_group_2.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/sl_group_2@data + - _self_ diff --git a/configs/experiment/preprocessing/filter_embeddings/mmci_tl_test.yaml b/configs/experiment/preprocessing/filter_embeddings/mmci_tl_test.yaml new file mode 100644 index 00000000..b72b6113 --- /dev/null +++ b/configs/experiment/preprocessing/filter_embeddings/mmci_tl_test.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/tl_test@data + - _self_ diff --git a/configs/experiment/preprocessing/filter_embeddings/mmci_tl_train.yaml b/configs/experiment/preprocessing/filter_embeddings/mmci_tl_train.yaml new file mode 100644 index 00000000..6fd82935 --- /dev/null +++ b/configs/experiment/preprocessing/filter_embeddings/mmci_tl_train.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/tl_train@data + - _self_ diff --git a/configs/experiment/preprocessing/filter_embeddings/mmci_tl_val.yaml b/configs/experiment/preprocessing/filter_embeddings/mmci_tl_val.yaml new file mode 100644 index 00000000..a9502109 --- /dev/null +++ b/configs/experiment/preprocessing/filter_embeddings/mmci_tl_val.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/mmci/tl_val@data + - _self_ diff --git a/configs/preprocessing/filter_embeddings.yaml b/configs/preprocessing/filter_embeddings.yaml new file mode 100644 index 00000000..b8460cac --- /dev/null +++ b/configs/preprocessing/filter_embeddings.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +data: ??? # instance from data/ directory + +use_case: "Filter Embeddings" diff --git a/preprocessing/embeddings/filter_embeddings.py b/preprocessing/embeddings/filter_embeddings.py new file mode 100644 index 00000000..8d450c58 --- /dev/null +++ b/preprocessing/embeddings/filter_embeddings.py @@ -0,0 +1,77 @@ +"""Script for embeddings filtering based on tiles filtering (migration script).""" + +from pathlib import Path +from typing import cast + +import hydra +import mlflow +import pandas as pd +import torch +from omegaconf import DictConfig +from rationai.mlkit import autolog, with_cli_args +from rationai.mlkit.lightning.loggers import MLFlowLogger + +from preprocessing.tiling_v2.filter_tiles import filter_tiles + + +def filter_and_log( + embeddings_uri: str, tiling_uri: str, thresholds: dict[str, int], output_name: str +) -> None: + + embeds_path = Path(mlflow.artifacts.download_artifacts(embeddings_uri)) + tiling_path = Path(mlflow.artifacts.download_artifacts(tiling_uri)) + slides = pd.read_parquet(tiling_path / "slides.parquet") + tiles = pd.read_parquet(tiling_path / "tiles.parquet") + output_dir = Path(output_name) + output_dir.mkdir(parents=True, exist_ok=True) + + for _, slide in slides.iterrows(): + slide_tiles = ( + tiles[tiles["slide_id"] == slide["id"]].copy().reset_index(drop=True) + ) + slide_name = Path(slide.path).stem + slide_embeddings = cast( + "torch.Tensor", + torch.load( + (embeds_path / slide_name).with_suffix(".pt"), + map_location="cpu", + ), + ) + assert len(slide_tiles) == len(slide_embeddings), ( + "Tile and Embedding counts do not match" + ) + + slide_tiles = filter_tiles(slide_tiles, thresholds) + slide_embeddings = slide_embeddings[slide_tiles.index.tolist()] + torch.save(slide_embeddings, (output_dir / slide_name).with_suffix(".pt")) + + +@with_cli_args(["+preprocessing=filter_embeddings"]) +@hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) +@autolog +def main(config: DictConfig, logger: MLFlowLogger) -> None: + if ( + hasattr(config.data, "virchow2_embeddings_uri") + and config.data.virchow2_embeddings_uri is not None + ): + filter_and_log( + config.data.virchow2_embeddings_uri, + config.data.tiles_uri_224, + config.data.thresholds, + config.data.data_name + "virchow2 embeddings", + ) + + if ( + hasattr(config.data, "pgp_embeddings_uri") + and config.data.pgp_embeddings_uri is not None + ): + filter_and_log( + config.data.pgp_embeddings_uri, + config.data.tiles_uri_224, + config.data.thresholds, + config.data.data_name + "pgp embeddings", + ) + + +if __name__ == "__main__": + main() diff --git a/preprocessing/masks/tile_masks.py b/preprocessing/masks/tile_masks.py index aa3337af..31942599 100644 --- a/preprocessing/masks/tile_masks.py +++ b/preprocessing/masks/tile_masks.py @@ -86,7 +86,6 @@ def process_slide( # --- - @with_cli_args(["+preprocessing=tile_masks"]) @hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) @autolog diff --git a/preprocessing/filter_tiles.py b/preprocessing/tiling_v2/filter_tiles.py similarity index 85% rename from preprocessing/filter_tiles.py rename to preprocessing/tiling_v2/filter_tiles.py index d21a0f2c..de289dc2 100644 --- a/preprocessing/filter_tiles.py +++ b/preprocessing/tiling_v2/filter_tiles.py @@ -11,13 +11,7 @@ from rationai.tiling.writers import save_mlflow_dataset -def filter_and_log( - tiling_uri: str, thresholds: dict[str, int], dataset_name: str -) -> None: - tiling_path = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - slides = pd.read_parquet(tiling_path / "slides.parquet") - tiles = pd.read_parquet(tiling_path / "tiles.parquet") - +def filter_tiles(tiles: pd.DataFrame, thresholds: dict[str, int]) -> pd.DataFrame: for col in tiles.columns: if col.endswith("percentage"): t = col.replace("percentage", "t") @@ -29,11 +23,21 @@ def filter_and_log( ) tiles = tiles[mask] - save_mlflow_dataset(slides, tiles, f"{dataset_name}_normalized") + return tiles + + +def filter_and_log( + tiling_uri: str, thresholds: dict[str, int], dataset_name: str +) -> None: + tiling_path = Path(mlflow.artifacts.download_artifacts(tiling_uri)) + slides = pd.read_parquet(tiling_path / "slides.parquet") + tiles = pd.read_parquet(tiling_path / "tiles.parquet") + tiles = filter_tiles(tiles, thresholds) + save_mlflow_dataset(slides, tiles, dataset_name) @with_cli_args(["+preprocessing=filter_tiles"]) -@hydra.main(config_path="../configs", config_name="preprocessing", version_base=None) +@hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) @autolog def main(config: DictConfig, logger: MLFlowLogger) -> None: if hasattr(config.data, "tiles_uri_512") and config.data.tiles_uri_512 is not None: From 8fa58b9b3d0b17562bcca15a1d78341863a61fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 08:09:37 +0000 Subject: [PATCH 087/284] fix: apply fixes --- preprocessing/embeddings/filter_embeddings.py | 4 ++-- preprocessing/masks/tile_masks.py | 4 ++-- preprocessing/tiling_v2/filter_tiles.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/preprocessing/embeddings/filter_embeddings.py b/preprocessing/embeddings/filter_embeddings.py index 8d450c58..4b4f3790 100644 --- a/preprocessing/embeddings/filter_embeddings.py +++ b/preprocessing/embeddings/filter_embeddings.py @@ -58,7 +58,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: config.data.virchow2_embeddings_uri, config.data.tiles_uri_224, config.data.thresholds, - config.data.data_name + "virchow2 embeddings", + config.data.data_name + "_virchow2 embeddings", ) if ( @@ -69,7 +69,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: config.data.pgp_embeddings_uri, config.data.tiles_uri_224, config.data.thresholds, - config.data.data_name + "pgp embeddings", + config.data.data_name + "_pgp embeddings", ) diff --git a/preprocessing/masks/tile_masks.py b/preprocessing/masks/tile_masks.py index 31942599..e19b97ba 100644 --- a/preprocessing/masks/tile_masks.py +++ b/preprocessing/masks/tile_masks.py @@ -65,9 +65,9 @@ def process_slide( # --- # --- survival binary masks - mask_path = output_path / "filled" / filename + save_dir = output_path / "filled" builder = ScalarMaskBuilder( - output_path, + save_dir, filename, slide.extent_x, slide.extent_y, diff --git a/preprocessing/tiling_v2/filter_tiles.py b/preprocessing/tiling_v2/filter_tiles.py index de289dc2..5b99d5cd 100644 --- a/preprocessing/tiling_v2/filter_tiles.py +++ b/preprocessing/tiling_v2/filter_tiles.py @@ -44,14 +44,14 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: filter_and_log( config.data.tiles_uri_512, config.data.thresholds, - config.data.data_name + "512", + config.data.data_name + "_512", ) if hasattr(config.data, "tiles_uri_224") and config.data.tiles_uri_224 is not None: filter_and_log( config.data.tiles_uri_224, config.data.thresholds, - config.data.data_name + "224", + config.data.data_name + "_224", ) From 3043a8492bfc3058107f50586812e37dec648712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 08:36:26 +0000 Subject: [PATCH 088/284] feat: fixed dim --- prostate_cancer/attention_mil_model.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 569846a1..e8b4f012 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -31,25 +31,27 @@ def __init__( super().__init__() match foundation: case "pgp": - input_dim = 1536 + self.input_dim = 1536 case "virchow2": - input_dim = 2560 + self.input_dim = 2560 case _: raise ValueError(f"Unknown foundation model: {foundation}") + self.input_dim_sqrt = torch.tensor(self.input_dim).sqrt() + # if we did not precompute the embeddings, we would obtain it from this module # (idendity replaced with foundation model) self.encoder = nn.Identity() # from a paper self.attention = nn.Sequential( - nn.Linear(input_dim, 512), + nn.Linear(self.input_dim, 512), nn.Tanh(), nn.Linear(512, 1), ) # TL Classifier - self.classifier = nn.Linear(input_dim, 1) + self.classifier = nn.Linear(self.input_dim, 1) self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") self.tl_criterion = nn.BCEWithLogitsLoss( @@ -103,21 +105,16 @@ def forward(self, x: Tensor) -> MILModelOutput: (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() ) # (batch_size, num_tiles_padded, 1) - # number of non-padded tiles for each bag - sqrt_bag_size = ( - mask.sum(dim=1, keepdim=True).sqrt().clamp(min=1) - ) # (batch_size, 1, 1) - # TL weights (which tiles to attend to) raw_attn: Tensor = self.attention(x) # (batch_size, num_tiles_padded, 1) - raw_attn = raw_attn.squeeze(-1) # (batch_size, num_tiles_padded) - - raw_attn = raw_attn.masked_fill(~mask.bool().squeeze(-1), float("-inf")) + raw_attn = raw_attn.masked_fill( + ~mask.bool(), float("-inf") + ) # (batch_size, num_tiles_padded, 1) # make it a distribution attention_weights = torch.softmax( - raw_attn / sqrt_bag_size.squeeze(-1), dim=1 - ).unsqueeze(-1) # (batch_size, num_tiles_padded, 1) + raw_attn / self.input_dim_sqrt, dim=1 + ) # (batch_size, num_tiles_padded, 1) # TL predictions tl_preds_raw: Tensor = self.classifier(x) # (batch_size, num_tiles_padded, 1) From 335da9a30af5848171a05727d77912918ac8b925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 11:52:58 +0000 Subject: [PATCH 089/284] feat: no norm --- prostate_cancer/attention_mil_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index e8b4f012..1075bf90 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -113,7 +113,7 @@ def forward(self, x: Tensor) -> MILModelOutput: # make it a distribution attention_weights = torch.softmax( - raw_attn / self.input_dim_sqrt, dim=1 + raw_attn, dim=1 ) # (batch_size, num_tiles_padded, 1) # TL predictions From b192cafd524a2dfb11f59742ea71d4f6f1c1f501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 11:55:49 +0000 Subject: [PATCH 090/284] fix: log embeds --- preprocessing/embeddings/filter_embeddings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/preprocessing/embeddings/filter_embeddings.py b/preprocessing/embeddings/filter_embeddings.py index 4b4f3790..50612b9c 100644 --- a/preprocessing/embeddings/filter_embeddings.py +++ b/preprocessing/embeddings/filter_embeddings.py @@ -45,6 +45,8 @@ def filter_and_log( slide_embeddings = slide_embeddings[slide_tiles.index.tolist()] torch.save(slide_embeddings, (output_dir / slide_name).with_suffix(".pt")) + mlflow.log_artifacts(str(output_dir)) + @with_cli_args(["+preprocessing=filter_embeddings"]) @hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) From 0c94ce462284319b7c9e2cf6ac50913590cc94f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 12:28:57 +0000 Subject: [PATCH 091/284] fix: nest --- preprocessing/embeddings/filter_embeddings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preprocessing/embeddings/filter_embeddings.py b/preprocessing/embeddings/filter_embeddings.py index 50612b9c..467e14c6 100644 --- a/preprocessing/embeddings/filter_embeddings.py +++ b/preprocessing/embeddings/filter_embeddings.py @@ -45,7 +45,7 @@ def filter_and_log( slide_embeddings = slide_embeddings[slide_tiles.index.tolist()] torch.save(slide_embeddings, (output_dir / slide_name).with_suffix(".pt")) - mlflow.log_artifacts(str(output_dir)) + mlflow.log_artifacts(str(output_dir), artifact_path=output_name) @with_cli_args(["+preprocessing=filter_embeddings"]) From e1730f2286c819ca7e156c72a8a628726f6f09df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 13:21:57 +0000 Subject: [PATCH 092/284] feat: filtered uris --- configs/data/mmci/sl_group_1.yaml | 2 ++ configs/data/mmci/sl_group_2.yaml | 2 ++ configs/data/mmci/tl_test.yaml | 2 ++ configs/data/mmci/tl_train.yaml | 2 ++ configs/data/mmci/tl_val.yaml | 2 ++ .../preprocessing/tile_masks/mmci_sl_group_1_224.yaml | 2 +- .../preprocessing/tile_masks/mmci_sl_group_1_512.yaml | 2 +- .../preprocessing/tile_masks/mmci_sl_group_2_224.yaml | 2 +- .../preprocessing/tile_masks/mmci_sl_group_2_512.yaml | 2 +- .../experiment/preprocessing/tile_masks/mmci_tl_test_224.yaml | 2 +- .../experiment/preprocessing/tile_masks/mmci_tl_test_512.yaml | 2 +- .../experiment/preprocessing/tile_masks/mmci_tl_train_224.yaml | 2 +- .../experiment/preprocessing/tile_masks/mmci_tl_train_512.yaml | 2 +- .../experiment/preprocessing/tile_masks/mmci_tl_val_224.yaml | 2 +- .../experiment/preprocessing/tile_masks/mmci_tl_val_512.yaml | 2 +- 15 files changed, 20 insertions(+), 10 deletions(-) diff --git a/configs/data/mmci/sl_group_1.yaml b/configs/data/mmci/sl_group_1.yaml index 0165b10b..c765c33b 100644 --- a/configs/data/mmci/sl_group_1.yaml +++ b/configs/data/mmci/sl_group_1.yaml @@ -4,6 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/ba747c14ca6f4a6991b18f2fd9beda10/artifacts/MMCI SL Group 1 512" tiles_uri_224: "mlflow-artifacts:/65/05cbc53468d24748938a1e645bfbe0fe/artifacts/MMCI SL Group 1 224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/861f806948cc42ee9cc53db3bd2a981c/artifacts/MMCI SL Group 1_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/861f806948cc42ee9cc53db3bd2a981c/artifacts/MMCI SL Group 1_224" pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI SL Group 1 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI SL Group 1 224" diff --git a/configs/data/mmci/sl_group_2.yaml b/configs/data/mmci/sl_group_2.yaml index e55d0126..e03335fb 100644 --- a/configs/data/mmci/sl_group_2.yaml +++ b/configs/data/mmci/sl_group_2.yaml @@ -4,6 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/a31f80e863f0461d803ae0027ffed3ae/artifacts/MMCI SL Group 2 512" tiles_uri_224: "mlflow-artifacts:/65/4be5807467e74794b103dfd8a2b00e1f/artifacts/MMCI SL Group 2 224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/df359470d57b40da94b2032f4c2a4812/artifacts/MMCI SL Group 2_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/df359470d57b40da94b2032f4c2a4812/artifacts/MMCI SL Group 2_224" pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI 2k Group 2 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI 2k Group 2 224" diff --git a/configs/data/mmci/tl_test.yaml b/configs/data/mmci/tl_test.yaml index 4085591f..172b4972 100644 --- a/configs/data/mmci/tl_test.yaml +++ b/configs/data/mmci/tl_test.yaml @@ -4,6 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/c47cd842fbf14bf399cd4aca43af3127/artifacts/MMCI TL Test 512" tiles_uri_224: "mlflow-artifacts:/65/74c7b6e1a10444688910b8835adf4208/artifacts/MMCI TL Test 224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/6dd3fffc62ca4582a5b89b1c2589fe05/artifacts/MMCI TL Test_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/6dd3fffc62ca4582a5b89b1c2589fe05/artifacts/MMCI TL Test_224" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Test 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Test 224" diff --git a/configs/data/mmci/tl_train.yaml b/configs/data/mmci/tl_train.yaml index 9c0a8a86..2af7f277 100644 --- a/configs/data/mmci/tl_train.yaml +++ b/configs/data/mmci/tl_train.yaml @@ -4,6 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/b587398845c54e1c84e8b60f11c42b15/artifacts/MMCI TL Train 512" tiles_uri_224: "mlflow-artifacts:/65/6576370c8866436a8816655bf77caf1a/artifacts/MMCI TL Train 224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/96380bd55e6343c6ba027033c592c529/artifacts/MMCI TL Train_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/96380bd55e6343c6ba027033c592c529/artifacts/MMCI TL Train_224" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Train 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Train 224" diff --git a/configs/data/mmci/tl_val.yaml b/configs/data/mmci/tl_val.yaml index f1119e76..7a7b88ff 100644 --- a/configs/data/mmci/tl_val.yaml +++ b/configs/data/mmci/tl_val.yaml @@ -4,6 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/a43da470f89848abb3a32e59c504ee1a/artifacts/MMCI TL Val 512" tiles_uri_224: "mlflow-artifacts:/65/2b313f9f1db04ba5803e9093677bb404/artifacts/MMCI TL Val 224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/27031722dc93415dadd95ab911305ad3/artifacts/MMCI TL Val_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/27031722dc93415dadd95ab911305ad3/artifacts/MMCI TL Val_224" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Val 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Val 224" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_224.yaml b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_224.yaml index ea7f0491..39221a34 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_224.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_224.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_224} + - ${data.tiles_filtered_uri_224} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_512.yaml b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_512.yaml index 01b2e8a0..1b9339d7 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_512.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_1_512.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_224.yaml b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_224.yaml index 8d56f4f0..a0ef0631 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_224.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_224.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_224} + - ${data.tiles_filtered_uri_224} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_512.yaml b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_512.yaml index 4746a0dd..04ee150e 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_512.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_sl_group_2_512.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_tl_test_224.yaml b/configs/experiment/preprocessing/tile_masks/mmci_tl_test_224.yaml index ad59a79f..1dcf9100 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_tl_test_224.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_tl_test_224.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_224} + - ${data.tiles_filtered_uri_224} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_tl_test_512.yaml b/configs/experiment/preprocessing/tile_masks/mmci_tl_test_512.yaml index 3e3d7a67..bbe000ba 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_tl_test_512.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_tl_test_512.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_tl_train_224.yaml b/configs/experiment/preprocessing/tile_masks/mmci_tl_train_224.yaml index 1f783837..fae085aa 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_tl_train_224.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_tl_train_224.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_224} + - ${data.tiles_filtered_uri_224} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_tl_train_512.yaml b/configs/experiment/preprocessing/tile_masks/mmci_tl_train_512.yaml index 9bc0e471..9c4fd355 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_tl_train_512.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_tl_train_512.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_tl_val_224.yaml b/configs/experiment/preprocessing/tile_masks/mmci_tl_val_224.yaml index 605376b3..8bbc5487 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_tl_val_224.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_tl_val_224.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_224} + - ${data.tiles_filtered_uri_224} percentage_cols: - "tissue_roi_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/mmci_tl_val_512.yaml b/configs/experiment/preprocessing/tile_masks/mmci_tl_val_512.yaml index 2489b65f..3df30d20 100644 --- a/configs/experiment/preprocessing/tile_masks/mmci_tl_val_512.yaml +++ b/configs/experiment/preprocessing/tile_masks/mmci_tl_val_512.yaml @@ -5,7 +5,7 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} percentage_cols: - "tissue_roi_percentage" From 3ce29c12b47ed970342bb41b5f8a259825854f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 16:33:16 +0000 Subject: [PATCH 093/284] fix: not filter carcinoma --- preprocessing/tiling_v2/filter_tiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preprocessing/tiling_v2/filter_tiles.py b/preprocessing/tiling_v2/filter_tiles.py index 5b99d5cd..1888444a 100644 --- a/preprocessing/tiling_v2/filter_tiles.py +++ b/preprocessing/tiling_v2/filter_tiles.py @@ -13,7 +13,7 @@ def filter_tiles(tiles: pd.DataFrame, thresholds: dict[str, int]) -> pd.DataFrame: for col in tiles.columns: - if col.endswith("percentage"): + if col.endswith("percentage") and "carcinoma" not in col: t = col.replace("percentage", "t") assert t in thresholds, f"{t} for {col}" mask = ( From 8f0eefea7daee47df6049019813284280ab2eef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 14 Jun 2026 17:39:00 +0000 Subject: [PATCH 094/284] feat: new uris --- configs/data/mmci/sl_group_1.yaml | 4 ++-- configs/data/mmci/sl_group_2.yaml | 4 ++-- configs/data/mmci/tl_test.yaml | 4 ++-- configs/data/mmci/tl_train.yaml | 4 ++-- configs/data/mmci/tl_val.yaml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configs/data/mmci/sl_group_1.yaml b/configs/data/mmci/sl_group_1.yaml index c765c33b..d37f8576 100644 --- a/configs/data/mmci/sl_group_1.yaml +++ b/configs/data/mmci/sl_group_1.yaml @@ -4,8 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/ba747c14ca6f4a6991b18f2fd9beda10/artifacts/MMCI SL Group 1 512" tiles_uri_224: "mlflow-artifacts:/65/05cbc53468d24748938a1e645bfbe0fe/artifacts/MMCI SL Group 1 224" -tiles_filtered_uri_512: "mlflow-artifacts:/65/861f806948cc42ee9cc53db3bd2a981c/artifacts/MMCI SL Group 1_512" -tiles_filtered_uri_224: "mlflow-artifacts:/65/861f806948cc42ee9cc53db3bd2a981c/artifacts/MMCI SL Group 1_224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/3383a393dddb464c811ad34e049cae7c/artifacts/MMCI SL Group 1_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/3383a393dddb464c811ad34e049cae7c/artifacts/MMCI SL Group 1_224" pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI SL Group 1 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI SL Group 1 224" diff --git a/configs/data/mmci/sl_group_2.yaml b/configs/data/mmci/sl_group_2.yaml index e03335fb..90b5d104 100644 --- a/configs/data/mmci/sl_group_2.yaml +++ b/configs/data/mmci/sl_group_2.yaml @@ -4,8 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/a31f80e863f0461d803ae0027ffed3ae/artifacts/MMCI SL Group 2 512" tiles_uri_224: "mlflow-artifacts:/65/4be5807467e74794b103dfd8a2b00e1f/artifacts/MMCI SL Group 2 224" -tiles_filtered_uri_512: "mlflow-artifacts:/65/df359470d57b40da94b2032f4c2a4812/artifacts/MMCI SL Group 2_512" -tiles_filtered_uri_224: "mlflow-artifacts:/65/df359470d57b40da94b2032f4c2a4812/artifacts/MMCI SL Group 2_224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/92c9fce24d024c5e84c6b21c3dc696dc/artifacts/MMCI SL Group 2_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/92c9fce24d024c5e84c6b21c3dc696dc/artifacts/MMCI SL Group 2_224" pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI 2k Group 2 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI 2k Group 2 224" diff --git a/configs/data/mmci/tl_test.yaml b/configs/data/mmci/tl_test.yaml index 172b4972..2b426c81 100644 --- a/configs/data/mmci/tl_test.yaml +++ b/configs/data/mmci/tl_test.yaml @@ -4,8 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/c47cd842fbf14bf399cd4aca43af3127/artifacts/MMCI TL Test 512" tiles_uri_224: "mlflow-artifacts:/65/74c7b6e1a10444688910b8835adf4208/artifacts/MMCI TL Test 224" -tiles_filtered_uri_512: "mlflow-artifacts:/65/6dd3fffc62ca4582a5b89b1c2589fe05/artifacts/MMCI TL Test_512" -tiles_filtered_uri_224: "mlflow-artifacts:/65/6dd3fffc62ca4582a5b89b1c2589fe05/artifacts/MMCI TL Test_224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/f541928fe7e3484fa40321a05745f66d/artifacts/MMCI TL Test_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/f541928fe7e3484fa40321a05745f66d/artifacts/MMCI TL Test_224" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Test 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Test 224" diff --git a/configs/data/mmci/tl_train.yaml b/configs/data/mmci/tl_train.yaml index 2af7f277..9e663e17 100644 --- a/configs/data/mmci/tl_train.yaml +++ b/configs/data/mmci/tl_train.yaml @@ -4,8 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/b587398845c54e1c84e8b60f11c42b15/artifacts/MMCI TL Train 512" tiles_uri_224: "mlflow-artifacts:/65/6576370c8866436a8816655bf77caf1a/artifacts/MMCI TL Train 224" -tiles_filtered_uri_512: "mlflow-artifacts:/65/96380bd55e6343c6ba027033c592c529/artifacts/MMCI TL Train_512" -tiles_filtered_uri_224: "mlflow-artifacts:/65/96380bd55e6343c6ba027033c592c529/artifacts/MMCI TL Train_224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/4c8c0a88482f4eef900a86990be51783/artifacts/MMCI TL Train_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/4c8c0a88482f4eef900a86990be51783/artifacts/MMCI TL Train_224" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Train 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Train 224" diff --git a/configs/data/mmci/tl_val.yaml b/configs/data/mmci/tl_val.yaml index 7a7b88ff..c341895f 100644 --- a/configs/data/mmci/tl_val.yaml +++ b/configs/data/mmci/tl_val.yaml @@ -4,8 +4,8 @@ defaults: tiles_uri_512: "mlflow-artifacts:/65/a43da470f89848abb3a32e59c504ee1a/artifacts/MMCI TL Val 512" tiles_uri_224: "mlflow-artifacts:/65/2b313f9f1db04ba5803e9093677bb404/artifacts/MMCI TL Val 224" -tiles_filtered_uri_512: "mlflow-artifacts:/65/27031722dc93415dadd95ab911305ad3/artifacts/MMCI TL Val_512" -tiles_filtered_uri_224: "mlflow-artifacts:/65/27031722dc93415dadd95ab911305ad3/artifacts/MMCI TL Val_224" +tiles_filtered_uri_512: "mlflow-artifacts:/65/1cbc0977226d4a389ee97c136c54e673/artifacts/MMCI TL Val_512" +tiles_filtered_uri_224: "mlflow-artifacts:/65/1cbc0977226d4a389ee97c136c54e673/artifacts/MMCI TL Val_224" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Val 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Val 224" From b66a7a628a928e869752d56b51dd389578f6354b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 15 Jun 2026 06:23:23 +0000 Subject: [PATCH 095/284] feat: mergig --- configs/data/mmci/sl_group_1.yaml | 2 ++ configs/data/mmci/sl_group_2.yaml | 2 ++ configs/data/mmci/tl_test.yaml | 2 ++ configs/data/mmci/tl_train.yaml | 2 ++ configs/data/mmci/tl_val.yaml | 2 ++ .../experiment/preprocessing/merge_embeddings/pgp/base.yaml | 2 +- .../preprocessing/merge_embeddings/virchow2/base.yaml | 2 +- configs/preprocessing/merge_embeddings.yaml | 2 +- preprocessing/embeddings/merge_embeddings.py | 4 ++-- 9 files changed, 15 insertions(+), 5 deletions(-) diff --git a/configs/data/mmci/sl_group_1.yaml b/configs/data/mmci/sl_group_1.yaml index d37f8576..cb6ab651 100644 --- a/configs/data/mmci/sl_group_1.yaml +++ b/configs/data/mmci/sl_group_1.yaml @@ -9,6 +9,8 @@ tiles_filtered_uri_224: "mlflow-artifacts:/65/3383a393dddb464c811ad34e049cae7c/a pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI SL Group 1 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI SL Group 1 224" +pgp_embeddings_filtered_uri: "mlflow-artifacts:/65/c6a940bcf169467aa8228536d49ced02/artifacts/MMCI SL Group 1_pgp embeddings" +virchow2_embeddings_filtered_uri: "mlflow-artifacts:/65/c6a940bcf169467aa8228536d49ced02/artifacts/MMCI SL Group 1_virchow2 embeddings" metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_1_with_extra_corrected.csv" metadata_table_deprecated: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_1_with_extra.csv" diff --git a/configs/data/mmci/sl_group_2.yaml b/configs/data/mmci/sl_group_2.yaml index 90b5d104..477c66fe 100644 --- a/configs/data/mmci/sl_group_2.yaml +++ b/configs/data/mmci/sl_group_2.yaml @@ -9,6 +9,8 @@ tiles_filtered_uri_224: "mlflow-artifacts:/65/92c9fce24d024c5e84c6b21c3dc696dc/a pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI 2k Group 2 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI 2k Group 2 224" +pgp_embeddings_filtered_uri: "mlflow-artifacts:/65/444acc3a1d8f440db36038f55eed22d7/artifacts/MMCI SL Group 2_pgp embeddings" +virchow2_embeddings_filtered_uri: "mlflow-artifacts:/65/444acc3a1d8f440db36038f55eed22d7/artifacts/MMCI SL Group 2_virchow2 embeddings" metadata_table: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_2_with_extra_corrected.csv" metadata_table_deprecated: "mlflow-artifacts:/65/36774d886cf54129b9a02a269b97b56f/artifacts/mmci_2k_group_2_with_extra.csv" diff --git a/configs/data/mmci/tl_test.yaml b/configs/data/mmci/tl_test.yaml index 2b426c81..7c08ee47 100644 --- a/configs/data/mmci/tl_test.yaml +++ b/configs/data/mmci/tl_test.yaml @@ -9,6 +9,8 @@ tiles_filtered_uri_224: "mlflow-artifacts:/65/f541928fe7e3484fa40321a05745f66d/a pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Test 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Test 224" +pgp_embeddings_filtered_uri: "mlflow-artifacts:/65/552ebf9c337a44abaf5d3ded4f501622/artifacts/MMCI TL Test_pgp embeddings" +virchow2_embeddings_filtered_uri: "mlflow-artifacts:/65/552ebf9c337a44abaf5d3ded4f501622/artifacts/MMCI TL Test_virchow2 embeddings" metadata_table: "mlflow-artifacts:/65/bfc694d7d3234720bfb699a6504ae7b2/artifacts/mmci_tl_test_data.csv" diff --git a/configs/data/mmci/tl_train.yaml b/configs/data/mmci/tl_train.yaml index 9e663e17..0d1b1d82 100644 --- a/configs/data/mmci/tl_train.yaml +++ b/configs/data/mmci/tl_train.yaml @@ -9,6 +9,8 @@ tiles_filtered_uri_224: "mlflow-artifacts:/65/4c8c0a88482f4eef900a86990be51783/a pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Train 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Train 224" +pgp_embeddings_filtered_uri: "mlflow-artifacts:/65/967cf253cbb044128afac480569fc7f1/artifacts/MMCI TL Train_pgp embeddings" +virchow2_embeddings_filtered_uri: "mlflow-artifacts:/65/967cf253cbb044128afac480569fc7f1/artifacts/MMCI TL Train_virchow2 embeddings" metadata_table: "mlflow-artifacts:/65/612ba86ea216490db86fd3e09bacf316/artifacts/mmci_tl_train.csv" diff --git a/configs/data/mmci/tl_val.yaml b/configs/data/mmci/tl_val.yaml index c341895f..729f9e28 100644 --- a/configs/data/mmci/tl_val.yaml +++ b/configs/data/mmci/tl_val.yaml @@ -9,6 +9,8 @@ tiles_filtered_uri_224: "mlflow-artifacts:/65/1cbc0977226d4a389ee97c136c54e673/a pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Val 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Val 224" +pgp_embeddings_filtered_uri: "mlflow-artifacts:/65/e8cd43df57b040759df244ba241c054e/artifacts/MMCI TL Val_pgp embeddings" +virchow2_embeddings_filtered_uri: "mlflow-artifacts:/65/e8cd43df57b040759df244ba241c054e/artifacts/MMCI TL Val_virchow2 embeddings" metadata_table: "mlflow-artifacts:/65/fc8b5da06d644e8ab0535f71f798a0e8/artifacts/mmci_tl_val.csv" diff --git a/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml b/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml index 4a1d0914..3bfc8531 100644 --- a/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml +++ b/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml @@ -1,4 +1,4 @@ # @package _global_ encoder: "PGP" -embeddings_uri: ${data.pgp_embeddings_uri} +embeddings_uri: ${data.pgp_embeddings_filtered_uri} diff --git a/configs/experiment/preprocessing/merge_embeddings/virchow2/base.yaml b/configs/experiment/preprocessing/merge_embeddings/virchow2/base.yaml index 211d79ec..c344dc79 100644 --- a/configs/experiment/preprocessing/merge_embeddings/virchow2/base.yaml +++ b/configs/experiment/preprocessing/merge_embeddings/virchow2/base.yaml @@ -1,4 +1,4 @@ # @package _global_ encoder: "Virchow2" -embeddings_uri: ${data.virchow2_embeddings_uri} +filtered_embeddings_uri: ${data.virchow2_embeddings_filtered_uri} diff --git a/configs/preprocessing/merge_embeddings.yaml b/configs/preprocessing/merge_embeddings.yaml index 5c0d989a..9f26f21f 100644 --- a/configs/preprocessing/merge_embeddings.yaml +++ b/configs/preprocessing/merge_embeddings.yaml @@ -1,7 +1,7 @@ # @package _global_ data: ??? -embeddings_uri: ??? +filtered_embeddings_uri: ??? encoder: ??? output_dir: ${prostate_cancer_artifacts}/tiling_parquets_sharded/${data.data_name}/${encoder} rows_per_file: 50000 diff --git a/preprocessing/embeddings/merge_embeddings.py b/preprocessing/embeddings/merge_embeddings.py index 05cb1ae8..66ff3c59 100644 --- a/preprocessing/embeddings/merge_embeddings.py +++ b/preprocessing/embeddings/merge_embeddings.py @@ -76,11 +76,11 @@ def process_and_shard_tiles( @hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) @autolog def main(config: DictConfig, logger: MLFlowLogger) -> None: - tiling_path = Path(mlflow.artifacts.download_artifacts(config.data.tiles_uri_224)) + tiling_path = Path(mlflow.artifacts.download_artifacts(config.data.tiles_filtered_uri_224)) slides = pd.read_parquet(tiling_path / "slides.parquet") tiles = pd.read_parquet(tiling_path / "tiles.parquet") - embeds_dir = Path(mlflow.artifacts.download_artifacts(config.embeddings_uri)) + embeds_dir = Path(mlflow.artifacts.download_artifacts(config.filtered_embeddings_uri)) output_dir = Path(config.output_dir) output_dir.mkdir(parents=True, exist_ok=True) From a179cf55342c74d3712f781fa5e008948d46c5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 15 Jun 2026 06:26:15 +0000 Subject: [PATCH 096/284] fix: lint --- preprocessing/embeddings/merge_embeddings.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/preprocessing/embeddings/merge_embeddings.py b/preprocessing/embeddings/merge_embeddings.py index 66ff3c59..bcad4e77 100644 --- a/preprocessing/embeddings/merge_embeddings.py +++ b/preprocessing/embeddings/merge_embeddings.py @@ -76,11 +76,15 @@ def process_and_shard_tiles( @hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) @autolog def main(config: DictConfig, logger: MLFlowLogger) -> None: - tiling_path = Path(mlflow.artifacts.download_artifacts(config.data.tiles_filtered_uri_224)) + tiling_path = Path( + mlflow.artifacts.download_artifacts(config.data.tiles_filtered_uri_224) + ) slides = pd.read_parquet(tiling_path / "slides.parquet") tiles = pd.read_parquet(tiling_path / "tiles.parquet") - embeds_dir = Path(mlflow.artifacts.download_artifacts(config.filtered_embeddings_uri)) + embeds_dir = Path( + mlflow.artifacts.download_artifacts(config.filtered_embeddings_uri) + ) output_dir = Path(config.output_dir) output_dir.mkdir(parents=True, exist_ok=True) From 3ac86daa24c5820203743ad620062ed8571fc53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 15 Jun 2026 06:29:52 +0000 Subject: [PATCH 097/284] fix: param name --- configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml b/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml index 3bfc8531..e8590816 100644 --- a/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml +++ b/configs/experiment/preprocessing/merge_embeddings/pgp/base.yaml @@ -1,4 +1,4 @@ # @package _global_ encoder: "PGP" -embeddings_uri: ${data.pgp_embeddings_filtered_uri} +filtered_embeddings_uri: ${data.pgp_embeddings_filtered_uri} From 970b39806c7f78281f7c57c27b806ffbc3a5559e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 16 Jun 2026 05:30:48 +0000 Subject: [PATCH 098/284] feat: merged parquets --- configs/data/mmci/sl_group_1.yaml | 2 ++ configs/data/mmci/sl_group_2.yaml | 2 ++ configs/data/mmci/tl_test.yaml | 2 ++ configs/data/mmci/tl_train.yaml | 2 ++ configs/data/mmci/tl_val.yaml | 2 ++ 5 files changed, 10 insertions(+) diff --git a/configs/data/mmci/sl_group_1.yaml b/configs/data/mmci/sl_group_1.yaml index cb6ab651..603ce7c1 100644 --- a/configs/data/mmci/sl_group_1.yaml +++ b/configs/data/mmci/sl_group_1.yaml @@ -6,6 +6,8 @@ tiles_uri_512: "mlflow-artifacts:/65/ba747c14ca6f4a6991b18f2fd9beda10/artifacts/ tiles_uri_224: "mlflow-artifacts:/65/05cbc53468d24748938a1e645bfbe0fe/artifacts/MMCI SL Group 1 224" tiles_filtered_uri_512: "mlflow-artifacts:/65/3383a393dddb464c811ad34e049cae7c/artifacts/MMCI SL Group 1_512" tiles_filtered_uri_224: "mlflow-artifacts:/65/3383a393dddb464c811ad34e049cae7c/artifacts/MMCI SL Group 1_224" +tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/65/df2f05b5bd9f42abbc377e8cedd28ba2/artifacts/MMCI SL Group 1_sharded" +tiles_filtered_w_pgp_uri_224: "mlflow-artifacts:/65/9c894d31a0a44f6697cbcf384e4e6dcd/artifacts/MMCI SL Group 1_sharded" pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI SL Group 1 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI SL Group 1 224" diff --git a/configs/data/mmci/sl_group_2.yaml b/configs/data/mmci/sl_group_2.yaml index 477c66fe..c4d0efca 100644 --- a/configs/data/mmci/sl_group_2.yaml +++ b/configs/data/mmci/sl_group_2.yaml @@ -6,6 +6,8 @@ tiles_uri_512: "mlflow-artifacts:/65/a31f80e863f0461d803ae0027ffed3ae/artifacts/ tiles_uri_224: "mlflow-artifacts:/65/4be5807467e74794b103dfd8a2b00e1f/artifacts/MMCI SL Group 2 224" tiles_filtered_uri_512: "mlflow-artifacts:/65/92c9fce24d024c5e84c6b21c3dc696dc/artifacts/MMCI SL Group 2_512" tiles_filtered_uri_224: "mlflow-artifacts:/65/92c9fce24d024c5e84c6b21c3dc696dc/artifacts/MMCI SL Group 2_224" +tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/65/c746eb1ab1594c7fbddc87065cd62ae2/artifacts/MMCI SL Group 2_sharded" +tiles_filtered_w_pgp_uri_224: "mlflow-artifacts:/65/6f4c4ad5880844e49ea3e2e5b7702997/artifacts/MMCI SL Group 2_sharded" pgp_embeddings_uri: "mlflow-artifacts:/65/e37a7a60c3564dd691f49af6e79d1cf5/artifacts/MMCI 2k Group 2 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/e440d1c48b46440b98f4f803d87e9883/artifacts/MMCI 2k Group 2 224" diff --git a/configs/data/mmci/tl_test.yaml b/configs/data/mmci/tl_test.yaml index 7c08ee47..005a43c7 100644 --- a/configs/data/mmci/tl_test.yaml +++ b/configs/data/mmci/tl_test.yaml @@ -6,6 +6,8 @@ tiles_uri_512: "mlflow-artifacts:/65/c47cd842fbf14bf399cd4aca43af3127/artifacts/ tiles_uri_224: "mlflow-artifacts:/65/74c7b6e1a10444688910b8835adf4208/artifacts/MMCI TL Test 224" tiles_filtered_uri_512: "mlflow-artifacts:/65/f541928fe7e3484fa40321a05745f66d/artifacts/MMCI TL Test_512" tiles_filtered_uri_224: "mlflow-artifacts:/65/f541928fe7e3484fa40321a05745f66d/artifacts/MMCI TL Test_224" +tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/65/20ceeaf225294dbe98b554e678dce688/artifacts/MMCI TL Test_sharded" +tiles_filtered_w_pgp_uri_224: "mlflow-artifacts:/65/e6f6a9738d9349b5ba256ae5cf4668fa/artifacts/MMCI TL Test_sharded" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Test 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Test 224" diff --git a/configs/data/mmci/tl_train.yaml b/configs/data/mmci/tl_train.yaml index 0d1b1d82..1b072ac1 100644 --- a/configs/data/mmci/tl_train.yaml +++ b/configs/data/mmci/tl_train.yaml @@ -6,6 +6,8 @@ tiles_uri_512: "mlflow-artifacts:/65/b587398845c54e1c84e8b60f11c42b15/artifacts/ tiles_uri_224: "mlflow-artifacts:/65/6576370c8866436a8816655bf77caf1a/artifacts/MMCI TL Train 224" tiles_filtered_uri_512: "mlflow-artifacts:/65/4c8c0a88482f4eef900a86990be51783/artifacts/MMCI TL Train_512" tiles_filtered_uri_224: "mlflow-artifacts:/65/4c8c0a88482f4eef900a86990be51783/artifacts/MMCI TL Train_224" +tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/65/110a3b0405fd44308fc048b9c9a6f204/artifacts/MMCI TL Train_sharded" +tiles_filtered_w_pgp_uri_224: "mlflow-artifacts:/65/3ee6d55cc8824966a87399e6e64e55e2/artifacts/MMCI TL Train_sharded" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Train 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Train 224" diff --git a/configs/data/mmci/tl_val.yaml b/configs/data/mmci/tl_val.yaml index 729f9e28..2f81c197 100644 --- a/configs/data/mmci/tl_val.yaml +++ b/configs/data/mmci/tl_val.yaml @@ -6,6 +6,8 @@ tiles_uri_512: "mlflow-artifacts:/65/a43da470f89848abb3a32e59c504ee1a/artifacts/ tiles_uri_224: "mlflow-artifacts:/65/2b313f9f1db04ba5803e9093677bb404/artifacts/MMCI TL Val 224" tiles_filtered_uri_512: "mlflow-artifacts:/65/1cbc0977226d4a389ee97c136c54e673/artifacts/MMCI TL Val_512" tiles_filtered_uri_224: "mlflow-artifacts:/65/1cbc0977226d4a389ee97c136c54e673/artifacts/MMCI TL Val_224" +tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/65/4f7cbb5b066143048e004001cbc86570/artifacts/MMCI TL Val_sharded" +tiles_filtered_w_pgp_uri_224: "mlflow-artifacts:/65/8dfb2066c79c4313bca5db6ba7b442e1/artifacts/MMCI TL Val_sharded" pgp_embeddings_uri: "mlflow-artifacts:/65/177737375ce349ecaee630dbae50e338/artifacts/MMCI TL Val 224" virchow2_embeddings_uri: "mlflow-artifacts:/65/beab5f8f21f14beb85b32a3180fa1798/artifacts/MMCI TL Val 224" From ebb797b17460d223932b9b91e3b3747a9e687e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 16 Jun 2026 13:36:06 +0000 Subject: [PATCH 099/284] fix: tl macro avg --- prostate_cancer/attention_mil_model.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 1075bf90..e385234b 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -170,8 +170,11 @@ def validation_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: sl_outputs, tl_outputs, mask, _ = self(bags) sl_loss = self.sl_criterion(sl_outputs, sl_labels) - tl_loss_all = self.tl_criterion(tl_outputs, tl_labels) - tl_loss = (tl_loss_all * mask).sum() / mask.sum() + tl_loss_all = self.tl_criterion( + tl_outputs, tl_labels + ) # (batch_size, num_tiles_padded) + per_bag_tl_loss = (tl_loss_all * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1) + tl_loss = per_bag_tl_loss.mean() loss = sl_loss + tl_loss self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) From 6634bec58c75c1f7b4eec7c2eb687c4c483e14ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 07:19:39 +0000 Subject: [PATCH 100/284] feat: remove filtering --- .../embeddings/bag_of_embeddings_labeled.yaml | 3 +- .../bag_of_embeddings_unlabeled.yaml | 2 - .../embeddings/embeddings_labeled.yaml | 4 +- .../embeddings/embeddings_unlabeled.yaml | 2 - .../datamodule/datasets/images/labeled.yaml | 1 - .../datamodule/datasets/images/unlabeled.yaml | 1 - .../predict/bag_of_embeddings/base.yaml | 4 - .../predict/bag_of_embeddings/pgp/base.yaml | 3 +- .../bag_of_embeddings/virchow2/base.yaml | 3 +- .../experiment/predict/embeddings/base.yaml | 4 - .../predict/embeddings/pgp/base.yaml | 3 +- .../predict/embeddings/virchow2/base.yaml | 3 +- .../mean_and_std/mmci_tl_train_512.yaml | 2 +- .../mean_and_std/radboud_train_512.yaml | 2 +- .../radboud_val_heuristic_512.yaml | 2 +- .../mean_and_std/radboud_val_ml_512.yaml | 2 +- .../mean_and_std/radboud_val_raw_512.yaml | 2 +- .../tile_survival/radboud_val_512.yaml | 8 -- configs/experiment/test/embeddings/base.yaml | 4 - .../experiment/test/embeddings/pgp/base.yaml | 3 +- .../test/embeddings/virchow2/base.yaml | 3 +- .../train/bag_of_embeddings/base.yaml | 9 -- .../train/bag_of_embeddings/pgp.yaml | 6 +- .../train/bag_of_embeddings/virchow2.yaml | 6 +- .../experiment/train/embeddings/pgp/base.yaml | 6 +- .../train/embeddings/virchow2/base.yaml | 6 +- configs/preprocessing/tile_survival.yaml | 6 -- .../embeddings/tile_embeddings_v2.py | 4 +- preprocessing/masks/tile_survival.py | 68 ------------- preprocessing/stats.py | 3 +- .../datasets/bag_of_embeddings_dataset.py | 67 ++++--------- prostate_cancer/datamodule/datasets/base.py | 98 +++++++++++-------- .../datamodule/datasets/embeddings_dataset.py | 98 +++---------------- .../{tile_dataset.py => tile_datasets.py} | 49 +++------- 34 files changed, 143 insertions(+), 344 deletions(-) delete mode 100644 configs/experiment/preprocessing/tile_survival/radboud_val_512.yaml delete mode 100644 configs/preprocessing/tile_survival.yaml delete mode 100644 preprocessing/masks/tile_survival.py rename prostate_cancer/datamodule/datasets/{tile_dataset.py => tile_datasets.py} (65%) diff --git a/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml b/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml index 90ad214a..5267b269 100644 --- a/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml +++ b/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml @@ -1,4 +1,3 @@ _target_: prostate_cancer.datamodule.datasets.LabeledBagOfEmbeddingsDataset uris: ??? -thresholds: ${thresholds} -embeddings_uri: ??? +carcinoma_roi_t: ${thresholds.carcinoma_roi_t} diff --git a/configs/datamodule/datasets/embeddings/bag_of_embeddings_unlabeled.yaml b/configs/datamodule/datasets/embeddings/bag_of_embeddings_unlabeled.yaml index fabf2a8e..d43e87d9 100644 --- a/configs/datamodule/datasets/embeddings/bag_of_embeddings_unlabeled.yaml +++ b/configs/datamodule/datasets/embeddings/bag_of_embeddings_unlabeled.yaml @@ -1,4 +1,2 @@ _target_: prostate_cancer.datamodule.datasets.UnlabeledBagOfEmbeddingsDataset uris: ??? -thresholds: ${thresholds} -embeddings_uri: ??? diff --git a/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml b/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml index be0aba53..952b0bbc 100644 --- a/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml +++ b/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml @@ -1,6 +1,4 @@ _target_: prostate_cancer.datamodule.datasets.LabeledEmbeddingsDataset uris: ??? -thresholds: ${thresholds} -embeddings_uri: ??? stratified_filter: ??? -carcinoma_roi_t: ${thresholds.carcinoma_roi_t} \ No newline at end of file +carcinoma_roi_t: ${thresholds.carcinoma_roi_t} diff --git a/configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml b/configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml index 618f2a76..d4daf517 100644 --- a/configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml +++ b/configs/datamodule/datasets/embeddings/embeddings_unlabeled.yaml @@ -1,4 +1,2 @@ _target_: prostate_cancer.datamodule.datasets.UnlabeledEmbeddingsDataset uris: ??? -thresholds: ${thresholds} -embeddings_uri: ??? diff --git a/configs/datamodule/datasets/images/labeled.yaml b/configs/datamodule/datasets/images/labeled.yaml index 7899f9b8..3e90939d 100644 --- a/configs/datamodule/datasets/images/labeled.yaml +++ b/configs/datamodule/datasets/images/labeled.yaml @@ -1,5 +1,4 @@ _target_: prostate_cancer.datamodule.datasets.LabeledTilesDataset uris: ??? -thresholds: ${thresholds} carcinoma_roi_t: ${thresholds.carcinoma_roi_t} stratified_filter: ??? diff --git a/configs/datamodule/datasets/images/unlabeled.yaml b/configs/datamodule/datasets/images/unlabeled.yaml index 11946fca..dfe019b3 100644 --- a/configs/datamodule/datasets/images/unlabeled.yaml +++ b/configs/datamodule/datasets/images/unlabeled.yaml @@ -1,3 +1,2 @@ _target_: prostate_cancer.datamodule.datasets.UnlabeledTilesDataset uris: ??? -thresholds: ${thresholds} diff --git a/configs/experiment/predict/bag_of_embeddings/base.yaml b/configs/experiment/predict/bag_of_embeddings/base.yaml index b4f58ec3..fa5dc20b 100644 --- a/configs/experiment/predict/bag_of_embeddings/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/base.yaml @@ -12,7 +12,3 @@ model: datamodule: batch_size: 2 num_workers: 4 - - predict: - uris: - - ${predict_data.tiles_uri_224} diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml index eae7ce26..318d8b41 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml @@ -8,4 +8,5 @@ defaults: datamodule: predict: - embeddings_uri: ${predict_data.pgp_embeddings_uri} + uris: + - ${predict_data.tiles_filtered_w_pgp_uri_224} diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml index 90fbdb13..0748a564 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml @@ -8,4 +8,5 @@ defaults: datamodule: predict: - embeddings_uri: ${predict_data.virchow2_embeddings_uri} + uris: + - ${predict_data.tiles_filtered_w_virchow2_uri_224} diff --git a/configs/experiment/predict/embeddings/base.yaml b/configs/experiment/predict/embeddings/base.yaml index 9f2cc6b0..bd43d86d 100644 --- a/configs/experiment/predict/embeddings/base.yaml +++ b/configs/experiment/predict/embeddings/base.yaml @@ -8,7 +8,3 @@ defaults: datamodule: batch_size: 512 num_workers: 8 - - predict: - uris: - - ${predict_data.tiles_uri_224} diff --git a/configs/experiment/predict/embeddings/pgp/base.yaml b/configs/experiment/predict/embeddings/pgp/base.yaml index a29965a4..4d1c91ba 100644 --- a/configs/experiment/predict/embeddings/pgp/base.yaml +++ b/configs/experiment/predict/embeddings/pgp/base.yaml @@ -8,4 +8,5 @@ defaults: datamodule: predict: - embeddings_uri: ${predict_data.pgp_embeddings_uri} + uris: + - ${predict_data.tiles_filtered_w_pgp_uri_224} diff --git a/configs/experiment/predict/embeddings/virchow2/base.yaml b/configs/experiment/predict/embeddings/virchow2/base.yaml index 5e1db321..816ffcc8 100644 --- a/configs/experiment/predict/embeddings/virchow2/base.yaml +++ b/configs/experiment/predict/embeddings/virchow2/base.yaml @@ -8,4 +8,5 @@ defaults: datamodule: predict: - embeddings_uri: ${predict_data.virchow2_embeddings_uri} + uris: + - ${predict_data.tiles_filtered_w_virchow2_uri_224} diff --git a/configs/experiment/preprocessing/mean_and_std/mmci_tl_train_512.yaml b/configs/experiment/preprocessing/mean_and_std/mmci_tl_train_512.yaml index ea3f6029..1fce7292 100644 --- a/configs/experiment/preprocessing/mean_and_std/mmci_tl_train_512.yaml +++ b/configs/experiment/preprocessing/mean_and_std/mmci_tl_train_512.yaml @@ -5,4 +5,4 @@ defaults: - _self_ uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} diff --git a/configs/experiment/preprocessing/mean_and_std/radboud_train_512.yaml b/configs/experiment/preprocessing/mean_and_std/radboud_train_512.yaml index 32cf7575..9b475f37 100644 --- a/configs/experiment/preprocessing/mean_and_std/radboud_train_512.yaml +++ b/configs/experiment/preprocessing/mean_and_std/radboud_train_512.yaml @@ -5,4 +5,4 @@ defaults: - _self_ uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} diff --git a/configs/experiment/preprocessing/mean_and_std/radboud_val_heuristic_512.yaml b/configs/experiment/preprocessing/mean_and_std/radboud_val_heuristic_512.yaml index cd62405b..9122e3cc 100644 --- a/configs/experiment/preprocessing/mean_and_std/radboud_val_heuristic_512.yaml +++ b/configs/experiment/preprocessing/mean_and_std/radboud_val_heuristic_512.yaml @@ -5,4 +5,4 @@ defaults: - _self_ uris: - - ${data.tiles_uri_512} + - ${data.tiles_uri_512} # will be replaced with filtered when ready diff --git a/configs/experiment/preprocessing/mean_and_std/radboud_val_ml_512.yaml b/configs/experiment/preprocessing/mean_and_std/radboud_val_ml_512.yaml index 9f747ef9..759cd851 100644 --- a/configs/experiment/preprocessing/mean_and_std/radboud_val_ml_512.yaml +++ b/configs/experiment/preprocessing/mean_and_std/radboud_val_ml_512.yaml @@ -5,4 +5,4 @@ defaults: - _self_ uris: - - ${data.tiles_uri_512} + - ${data.tiles_uri_512} # will be replaced with filtered when ready diff --git a/configs/experiment/preprocessing/mean_and_std/radboud_val_raw_512.yaml b/configs/experiment/preprocessing/mean_and_std/radboud_val_raw_512.yaml index 4c4d0c18..1ea78b0c 100644 --- a/configs/experiment/preprocessing/mean_and_std/radboud_val_raw_512.yaml +++ b/configs/experiment/preprocessing/mean_and_std/radboud_val_raw_512.yaml @@ -5,4 +5,4 @@ defaults: - _self_ uris: - - ${data.tiles_uri_512} + - ${data.tiles_uri_512} # will be replaced with filtered when ready diff --git a/configs/experiment/preprocessing/tile_survival/radboud_val_512.yaml b/configs/experiment/preprocessing/tile_survival/radboud_val_512.yaml deleted file mode 100644 index a3657eba..00000000 --- a/configs/experiment/preprocessing/tile_survival/radboud_val_512.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /data/radboud/val@data - - _self_ - -tile_uris: - - ${data.tiles_uri_512} diff --git a/configs/experiment/test/embeddings/base.yaml b/configs/experiment/test/embeddings/base.yaml index 48c2d59e..51fc277d 100644 --- a/configs/experiment/test/embeddings/base.yaml +++ b/configs/experiment/test/embeddings/base.yaml @@ -8,7 +8,3 @@ defaults: datamodule: batch_size: 1024 num_workers: 8 - - test: - uris: - - ${test_data.tiles_uri_224} diff --git a/configs/experiment/test/embeddings/pgp/base.yaml b/configs/experiment/test/embeddings/pgp/base.yaml index 46fb3103..e38576f9 100644 --- a/configs/experiment/test/embeddings/pgp/base.yaml +++ b/configs/experiment/test/embeddings/pgp/base.yaml @@ -8,4 +8,5 @@ defaults: datamodule: test: - embeddings_uri: ${test_data.pgp_embeddings_uri} + uris: + - ${test_data.tiles_filtered_w_pgp_uri_224} diff --git a/configs/experiment/test/embeddings/virchow2/base.yaml b/configs/experiment/test/embeddings/virchow2/base.yaml index 70eff738..31ba413a 100644 --- a/configs/experiment/test/embeddings/virchow2/base.yaml +++ b/configs/experiment/test/embeddings/virchow2/base.yaml @@ -8,4 +8,5 @@ defaults: datamodule: test: - embeddings_uri: ${test_data.virchow2_embeddings_uri} + uris: + - ${test_data.tiles_filtered_w_virchow2_uri_224} diff --git a/configs/experiment/train/bag_of_embeddings/base.yaml b/configs/experiment/train/bag_of_embeddings/base.yaml index 44e64164..8c3a995f 100644 --- a/configs/experiment/train/bag_of_embeddings/base.yaml +++ b/configs/experiment/train/bag_of_embeddings/base.yaml @@ -21,12 +21,3 @@ callbacks: datamodule: batch_size: 4 num_workers: 8 - - # both foundation models share tiles (embeddings are specified per model) - train: - uris: - - ${train_data.tiles_uri_224} - - val: - uris: - - ${val_data.tiles_uri_224} diff --git a/configs/experiment/train/bag_of_embeddings/pgp.yaml b/configs/experiment/train/bag_of_embeddings/pgp.yaml index 91b4197d..a3be9998 100644 --- a/configs/experiment/train/bag_of_embeddings/pgp.yaml +++ b/configs/experiment/train/bag_of_embeddings/pgp.yaml @@ -7,8 +7,10 @@ defaults: datamodule: train: - embeddings_uri: ${train_data.pgp_embeddings_uri} + uris: + - ${train_data.tiles_filtered_w_pgp_uri_224} val: - embeddings_uri: ${val_data.pgp_embeddings_uri} + uris: + - ${val_data.tiles_filtered_w_pgp_uri_224} model_name: "PGP (Hybrid-MIL)" diff --git a/configs/experiment/train/bag_of_embeddings/virchow2.yaml b/configs/experiment/train/bag_of_embeddings/virchow2.yaml index 79bd968e..4a3674e8 100644 --- a/configs/experiment/train/bag_of_embeddings/virchow2.yaml +++ b/configs/experiment/train/bag_of_embeddings/virchow2.yaml @@ -7,8 +7,10 @@ defaults: datamodule: train: - embeddings_uri: ${train_data.virchow2_embeddings_uri} + uris: + - ${train_data.tiles_filtered_w_virchow2_uri_224} val: - embeddings_uri: ${val_data.virchow2_embeddings_uri} + uris: + - ${val_data.tiles_filtered_w_virchow2_uri_224} model_name: "Virchow2 (Hybrid-MIL)" diff --git a/configs/experiment/train/embeddings/pgp/base.yaml b/configs/experiment/train/embeddings/pgp/base.yaml index 9c9f7267..2f3435d3 100644 --- a/configs/experiment/train/embeddings/pgp/base.yaml +++ b/configs/experiment/train/embeddings/pgp/base.yaml @@ -7,8 +7,10 @@ defaults: datamodule: train: - embeddings_uri: ${train_data.pgp_embeddings_uri} + uris: + - ${train_data.tiles_filtered_w_pgp_uri_224} val: - embeddings_uri: ${val_data.pgp_embeddings_uri} + uris: + - ${val_data.tiles_filtered_w_pgp_uri_224} model_name: "PGP" diff --git a/configs/experiment/train/embeddings/virchow2/base.yaml b/configs/experiment/train/embeddings/virchow2/base.yaml index b3c40ba3..c3d11e96 100644 --- a/configs/experiment/train/embeddings/virchow2/base.yaml +++ b/configs/experiment/train/embeddings/virchow2/base.yaml @@ -7,8 +7,10 @@ defaults: datamodule: train: - embeddings_uri: ${train_data.virchow2_embeddings_uri} + uris: + - ${train_data.tiles_filtered_w_virchow2_uri_224} val: - embeddings_uri: ${val_data.virchow2_embeddings_uri} + uris: + - ${val_data.tiles_filtered_w_virchow2_uri_224} model_name: "Virchow2" diff --git a/configs/preprocessing/tile_survival.yaml b/configs/preprocessing/tile_survival.yaml deleted file mode 100644 index 6622ff5c..00000000 --- a/configs/preprocessing/tile_survival.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# @package _global_ - -output_path: ${prostate_cancer_artifacts}/tile_survival/${data.data_name} -max_concurrent: 5 -tile_uris: [] -use_case: Tile Survival diff --git a/preprocessing/embeddings/tile_embeddings_v2.py b/preprocessing/embeddings/tile_embeddings_v2.py index b46e9e39..e8ff41e1 100644 --- a/preprocessing/embeddings/tile_embeddings_v2.py +++ b/preprocessing/embeddings/tile_embeddings_v2.py @@ -43,7 +43,9 @@ async def __call__(self, row: dict[str, Any]) -> dict[str, Any]: @hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) @autolog def main(config: DictConfig, logger: MLFlowLogger) -> None: - folder = Path(mlflow.artifacts.download_artifacts(config.data.tiles_uri_224)) + folder = Path( + mlflow.artifacts.download_artifacts(config.data.tiles_filtered_uri_224) + ) slides = pd.read_parquet(folder / "slides.parquet") tiles = pd.read_parquet(folder / "tiles.parquet") diff --git a/preprocessing/masks/tile_survival.py b/preprocessing/masks/tile_survival.py deleted file mode 100644 index a99ee16a..00000000 --- a/preprocessing/masks/tile_survival.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Script for creating binary tile masks showing which tiles survive filtering.""" - -from pathlib import Path - -import hydra -import pandas as pd -import ray -import torch -from omegaconf import DictConfig -from rationai.masks import process_items -from rationai.masks.mask_builders import ScalarMaskBuilder -from rationai.mlkit import autolog, with_cli_args -from rationai.mlkit.lightning.loggers import MLFlowLogger - -from prostate_cancer.datamodule.datasets.tile_dataset import UnlabeledTilesDataset - - -@ray.remote -def process_slide(slide: tuple[pd.DataFrame, pd.Series], output_path: Path) -> None: - tiles, metadata = slide - filename = Path(metadata.path).with_suffix(".tiff").name - - builder = ScalarMaskBuilder( - output_path, - filename, - metadata.extent_x, - metadata.extent_y, - metadata.mpp_x, - metadata.mpp_y, - metadata.tile_extent_x, - metadata.stride_x, - ) - - data = torch.ones(len(tiles)) * 255 - xs = torch.tensor(tiles["x"].values) - ys = torch.tensor(tiles["y"].values) - builder.update(data, xs, ys) - - builder.save() - # --- - - -@with_cli_args(["+preprocessing=tile_survival"]) -@hydra.main(config_path="../../configs", config_name="preprocessing", version_base=None) -@autolog -def main(config: DictConfig, logger: MLFlowLogger) -> None: - ds = UnlabeledTilesDataset(uris=config.tile_uris, thresholds=config.data.thresholds) - items: list[tuple[pd.DataFrame, pd.Series]] = [ - (slide_ds.slide_tiles.tiles, slide_ds.slide_metadata) # type: ignore[attr-defined] - for slide_ds in ds.datasets - ] - output_path = Path(config.output_path) - - process_items( - items, - process_slide, - fn_kwargs={ - "output_path": output_path, - }, - max_concurrent=config.max_concurrent, - ) - logger.log_artifacts( - local_dir=str(output_path), artifact_path="tile_survival_masks" - ) - - -if __name__ == "__main__": - main() diff --git a/preprocessing/stats.py b/preprocessing/stats.py index 749fa5b7..8d8fb72b 100644 --- a/preprocessing/stats.py +++ b/preprocessing/stats.py @@ -4,12 +4,12 @@ import ray import torch from omegaconf import DictConfig +from prostate_cancer.datamodule.datasets.tile_dataset import SlideTiles from rationai.masks import process_items from rationai.mlkit import autolog, with_cli_args from rationai.mlkit.lightning.loggers import MLFlowLogger from prostate_cancer.datamodule.datasets import UnlabeledTilesDataset -from prostate_cancer.datamodule.datasets.tile_dataset import SlideTiles @ray.remote @@ -67,7 +67,6 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: logger.experiment.log_param(logger.run_id, "dataset_uris", config.uris) dataset = UnlabeledTilesDataset( uris=config.uris, - thresholds=config.thresholds, ) slides = cast("list[SlideTiles]", dataset.datasets) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index 6a3461db..c1ed0585 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -11,7 +11,6 @@ import torch.nn.functional as F from torch.utils.data import Dataset -from prostate_cancer.datamodule.datasets.base import filter_tiles_by_thresholds from prostate_cancer.typing import ( LabeledBagOfTilesSample, SlideMetadata, @@ -22,37 +21,28 @@ T = TypeVar("T", bound=LabeledBagOfTilesSample | UnlabeledBagOfTilesSample) -def get_embedding_ceiling(tiles: pd.DataFrame, thresholds: dict[str, float]) -> int: - filtered_tiles = filter_tiles_by_thresholds(tiles, thresholds) - return filtered_tiles["slide_id"].value_counts().max() - - class BagOfEmbeddingsDataset(Dataset[T], Generic[T]): def __init__( self, - thresholds: dict[str, float], uris: Iterable[str], - embeddings_uri: str, padding: bool = True, - include_labels: bool = True, + carcinoma_roi_t: float | None = None, ) -> None: - self.thresholds = thresholds - self.include_labels = include_labels - self.slides, self.tiles, self.embeddings_folder = self.download_artifacts( - uris, embeddings_uri - ) + self.include_labels = carcinoma_roi_t is not None + self.carcinoma_roi_t = carcinoma_roi_t + self.slides, self.tiles = self.download_artifacts(uris) - if include_labels: - self.tiles["carcinoma"] = self.tiles[ - "carcinoma_roi_percentage" - ] > self.thresholds.get("carcinoma_roi_t") + if self.include_labels: + self.tiles["carcinoma"] = ( + self.tiles["carcinoma_roi_percentage"] > self.carcinoma_roi_t + ) self.padding = padding - self.max_embeddings = get_embedding_ceiling(self.tiles, self.thresholds) + self.max_embeddings = self.tiles["slide_id"].value_counts().max() def download_artifacts( - self, tiling_uris: Iterable[str], embeddings_uri: str - ) -> tuple[pd.DataFrame, pd.DataFrame, Path]: + self, tiling_uris: Iterable[str] + ) -> tuple[pd.DataFrame, pd.DataFrame]: slide_dfs = [] tile_dfs = [] @@ -61,12 +51,9 @@ def download_artifacts( slide_dfs.append(pd.read_parquet(tiling_folder / "slides.parquet")) tile_dfs.append(pd.read_parquet(tiling_folder / "tiles.parquet")) - embeddings_dir = Path(mlflow.artifacts.download_artifacts(embeddings_uri)) - return ( pd.concat(slide_dfs, ignore_index=True), pd.concat(tile_dfs, ignore_index=True), - embeddings_dir, ) def __len__(self) -> int: @@ -75,17 +62,9 @@ def __len__(self) -> int: def __getitem__(self, idx: int) -> T: slide_metadata = self.slides.iloc[idx] slide_name = Path(slide_metadata.path).stem - slide_embeddings = torch.load( - self.embeddings_folder / Path(slide_name).with_suffix(".pt"), - map_location="cpu", - ) - slide_tiles = self.tiles[ - self.tiles["slide_id"] == slide_metadata.id - ].reset_index(drop=True) - assert len(slide_embeddings) == len(slide_tiles), "Size mismatch" - filtered_tiles = filter_tiles_by_thresholds(slide_tiles, self.thresholds) - slide_embeddings = slide_embeddings[filtered_tiles.index.tolist()] + slide_tiles = self.tiles[self.tiles["slide_id"] == slide_metadata.id] + slide_embeddings = torch.tensor(slide_tiles["embedding"]) pad_amount = self.max_embeddings - slide_embeddings.shape[0] assert pad_amount >= 0, "Invalid padding" @@ -97,8 +76,8 @@ def __getitem__(self, idx: int) -> T: slide_id=slide_metadata["id"], slide_name=slide_name, slide_path=slide_metadata["path"], - xs=torch.from_numpy(filtered_tiles["x"].to_numpy()), - ys=torch.from_numpy(filtered_tiles["y"].to_numpy()), + xs=torch.from_numpy(slide_tiles["x"].to_numpy()), + ys=torch.from_numpy(slide_tiles["y"].to_numpy()), ) if not self.include_labels: @@ -107,8 +86,8 @@ def __getitem__(self, idx: int) -> T: sl_label = torch.tensor(slide_metadata["carcinoma"]).float() tl_labels = torch.zeros(len(slide_embeddings)).float() - tl_labels[: len(filtered_tiles)] = torch.tensor( - filtered_tiles["carcinoma"].to_numpy() + tl_labels[: len(slide_tiles)] = torch.tensor( + slide_tiles["carcinoma"].to_numpy() ).float() return slide_embeddings, tl_labels, sl_label, metadata # type: ignore[return-value] @@ -117,17 +96,14 @@ def __getitem__(self, idx: int) -> T: class LabeledBagOfEmbeddingsDataset(BagOfEmbeddingsDataset[LabeledBagOfTilesSample]): def __init__( self, - thresholds: dict[str, float], uris: Iterable[str], - embeddings_uri: str, + carcinoma_roi_t: float, padding: bool = True, ) -> None: super().__init__( - thresholds=thresholds, uris=uris, - embeddings_uri=embeddings_uri, padding=padding, - include_labels=True, + carcinoma_roi_t=carcinoma_roi_t, ) @@ -136,15 +112,10 @@ class UnlabeledBagOfEmbeddingsDataset( ): def __init__( self, - thresholds: dict[str, float], uris: Iterable[str], - embeddings_uri: str, padding: bool = True, ) -> None: super().__init__( - thresholds=thresholds, uris=uris, - embeddings_uri=embeddings_uri, padding=padding, - include_labels=False, ) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index f9cf8774..b41cacc4 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -1,67 +1,61 @@ +from abc import ABC from collections.abc import Iterable from pathlib import Path -from typing import TypeVar +from typing import TypeVar, cast import pandas as pd +from albumentations.core.composition import TransformType from rationai.mlkit.data.datasets import MetaTiledSlides +from torch.utils.data import Dataset + +from prostate_cancer.typing import LabeledTileSample, UnlabeledTileSample T = TypeVar("T", covariant=True) -def get_slide_name(slide: pd.Series) -> str: - return Path(slide.path).stem - - -def filter_tiles_by_thresholds( - tiles: pd.DataFrame, thresholds: dict[str, float] -) -> pd.DataFrame: - for percentage in [ - "tissue_roi_percentage", - "exclude_percentage", - "another_pathology_percentage", - "residual_percentage", - "blur_percentage", - "folding_percentage", - ]: - if percentage in tiles.columns: - t = percentage.replace("percentage", "t") - assert t in thresholds, f"{t} for {percentage}" - mask = ( - tiles[percentage] > thresholds[t] - if "tissue" in percentage - else tiles[percentage] <= thresholds[t] - ) - tiles = tiles[mask] +def get_slide_name(slide_metadata: pd.Series) -> str: + return Path(slide_metadata.path).stem - return tiles +class BaseSingleSlideDataset(Dataset[LabeledTileSample | UnlabeledTileSample], ABC): + def __init__( + self, + slide_metadata: pd.Series, + tiles: pd.DataFrame, + include_label: bool, + ) -> None: + super().__init__() + assert "embedding" in tiles.column, ( + "Embeddings Dataset requires embedding column" + ) + self.include_label = include_label + self.slide_metadata = slide_metadata + self.tiles = tiles + if len(tiles) == 0: + print( + f"Warning: No tiles found for slide {get_slide_name(slide_metadata)}." + ) -class FilterableDataset(MetaTiledSlides[T]): - """This class abstracts the filtering which is needed for all the datasets.""" + +class BaseTileDataset(MetaTiledSlides[T]): + """This class abstracts the functionality shared across embedding and image datasets.""" def __init__( self, uris: Iterable[str], - thresholds: dict[str, float], + single_slide_ds_cls: type[BaseSingleSlideDataset], carcinoma_roi_t: float | None = None, # only for labeled stratified_filter: bool | None = None, # only for labeled + transforms: TransformType | None = None, ) -> None: self.labeled = carcinoma_roi_t is not None and stratified_filter is not None self.stratified_filter = stratified_filter self.carcinoma_roi_t = carcinoma_roi_t - self.thresholds = thresholds + self.transforms = transforms + self.single_slide_ds_cls = single_slide_ds_cls super().__init__(uris=uris) - def prepare_tiles(self, tiles: pd.DataFrame) -> pd.DataFrame: - assert self.labeled, "Only allowed for labeled dataset" - tiles = filter_tiles_by_thresholds(tiles, self.thresholds) - tiles["carcinoma"] = tiles["carcinoma_roi_percentage"] > self.carcinoma_roi_t - if self.stratified_filter: - tiles = self.filter_non_carcinoma(tiles) - - return tiles - def filter_non_carcinoma(self, tiles: pd.DataFrame) -> pd.DataFrame: assert self.labeled, "Only allowed for labeled dataset" tiles_slide_cancer = ( @@ -71,3 +65,29 @@ def filter_non_carcinoma(self, tiles: pd.DataFrame) -> pd.DataFrame: ) return tiles[~((tiles_slide_cancer == 1) & (tiles["carcinoma"] == 0))] + + def generate_datasets(self) -> Iterable[Dataset[T]]: + + if self.labeled: + self.tiles["carcinoma"] = ( + self.tiles["carcinoma_roi_percentage"] > self.carcinoma_roi_t + ) + if self.stratified_filter: + self.tiles = self.filter_non_carcinoma(self.tiles) + + return ( + cast( + "Dataset[T]", + self.single_slide_ds_cls( + slide, + tiles=self.filter_tiles_by_slide(slide["id"]), + include_label=self.labeled, + **( + {"transforms": self.transforms} + if self.transforms is not None + else {} + ), # avoid sending transforms arg to embeddings dataset + ), + ) + for _, slide in self.slides.iterrows() + ) diff --git a/prostate_cancer/datamodule/datasets/embeddings_dataset.py b/prostate_cancer/datamodule/datasets/embeddings_dataset.py index 444b218f..81afe67f 100644 --- a/prostate_cancer/datamodule/datasets/embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/embeddings_dataset.py @@ -1,16 +1,12 @@ from collections.abc import Iterable -from pathlib import Path -from typing import TypeAlias, TypeVar, cast +from typing import TypeAlias, TypeVar -import mlflow -import mlflow.artifacts import pandas as pd import torch -from torch.utils.data import Dataset from prostate_cancer.datamodule.datasets.base import ( - FilterableDataset, - filter_tiles_by_thresholds, + BaseSingleSlideDataset, + BaseTileDataset, get_slide_name, ) from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample @@ -22,84 +18,20 @@ T = TypeVar("T", covariant=True) -class EmbeddingsDataset(FilterableDataset[T]): +class EmbeddingsDataset(BaseTileDataset[T]): def __init__( self, uris: Iterable[str], - embeddings_uri: str, - thresholds: dict[str, float], carcinoma_roi_t: float | None = None, stratified_filter: bool | None = None, ) -> None: - self.embeddings_folder = Path( - mlflow.artifacts.download_artifacts(embeddings_uri) - ) super().__init__( uris=uris, - thresholds=thresholds, + single_slide_ds_cls=TileEmbeddingsSlide, carcinoma_roi_t=carcinoma_roi_t, stratified_filter=stratified_filter, ) - def generate_datasets(self) -> Iterable[Dataset[T]]: - new_tiles = [] - datasets = [] - - if self.labeled: - self.tiles["carcinoma"] = ( - self.tiles["carcinoma_roi_percentage"] > self.carcinoma_roi_t - ) - - for _, slide in self.slides.iterrows(): - tiles, embeddings = self._filter_tiles_embeddings_by_slide(slide) - if len(tiles) == 0: # If there are no tiles, skip the slide - print(f"Slide {get_slide_name(slide)} has no tiles. Skipping slide.") - continue - - new_tiles.append(tiles) - datasets.append( - cast( - "Dataset[T]", - _TileEmbeddingsSlide( - slide_metadata=slide, - embeddings=embeddings, - tiles=tiles, - include_label=self.labeled, - ), - ), - ) - - self.tiles = pd.concat(new_tiles, ignore_index=True) - return datasets - - def _filter_tiles_embeddings_by_slide( - self, slide: SlideDfMetadata - ) -> tuple[pd.DataFrame, torch.Tensor]: - slide_tiles = ( - self.tiles[self.tiles["slide_id"] == slide["id"]] - .copy() - .reset_index(drop=True) - ) - slide_embeddings = cast( - "torch.Tensor", - torch.load( - (self.embeddings_folder / get_slide_name(slide)).with_suffix(".pt"), - map_location="cpu", - ), - ) - - assert len(slide_tiles) == len(slide_embeddings), ( - "Tile and Embedding counts do not match" - ) - - slide_tiles = ( - self.prepare_tiles(slide_tiles) - if self.labeled - else filter_tiles_by_thresholds(slide_tiles, self.thresholds) - ) - slide_embeddings = slide_embeddings[slide_tiles.index.tolist()] - return slide_tiles, slide_embeddings - class LabeledEmbeddingsDataset(EmbeddingsDataset[LabeledTileSample]): ... @@ -107,30 +39,28 @@ class LabeledEmbeddingsDataset(EmbeddingsDataset[LabeledTileSample]): ... class UnlabeledEmbeddingsDataset(EmbeddingsDataset[UnlabeledTileSample]): ... -# dataset of tile embeddings for a single slide -class _TileEmbeddingsSlide(Dataset[LabeledTileSample | UnlabeledTileSample]): +class TileEmbeddingsSlide(BaseSingleSlideDataset): def __init__( self, slide_metadata: pd.Series, - embeddings: torch.Tensor, tiles: pd.DataFrame, include_label: bool, ) -> None: - super().__init__() - self.include_label = include_label - self.slide_metadata = slide_metadata - self.tiles = tiles - self.embeddings = embeddings - assert len(self.tiles) == len(self.embeddings), ( - "Tiles and embeddings not aligned" + super().__init__( + slide_metadata=slide_metadata, + tiles=tiles, + include_label=include_label, + ) + assert "embedding" in tiles.column, ( + "Embeddings Dataset requires embedding column" ) def __len__(self) -> int: return len(self.tiles) def __getitem__(self, idx: int) -> LabeledTileSample | UnlabeledTileSample: - vector = self.embeddings[idx] tile = self.tiles.iloc[idx] + vector = torch.tensor(tile["embedding"]) metadata = TileMetadata( slide=get_slide_name(self.slide_metadata), x=tile["x"], y=tile["y"] ) diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_datasets.py similarity index 65% rename from prostate_cancer/datamodule/datasets/tile_dataset.py rename to prostate_cancer/datamodule/datasets/tile_datasets.py index 5ee543bf..40c65a5d 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_datasets.py @@ -1,17 +1,15 @@ from collections.abc import Iterable -from typing import TypeVar, cast +from typing import TypeVar import pandas as pd import torch from albumentations.core.composition import TransformType from albumentations.pytorch import ToTensorV2 from rationai.mlkit.data.datasets import OpenSlideTilesDataset -from torch.utils.data import Dataset from prostate_cancer.datamodule.datasets.base import ( - FilterableDataset, - filter_tiles_by_thresholds, - get_slide_name, + BaseSingleSlideDataset, + BaseTileDataset, ) from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample @@ -19,11 +17,10 @@ T = TypeVar("T", covariant=True) -class TilesDataset(FilterableDataset[T]): +class TilesDataset(BaseTileDataset[T]): def __init__( self, uris: Iterable[str], - thresholds: dict[str, float], carcinoma_roi_t: float | None = None, stratified_filter: bool | None = None, transforms: TransformType | None = None, @@ -31,28 +28,10 @@ def __init__( self.transforms = transforms super().__init__( uris=uris, - thresholds=thresholds, + single_slide_ds_cls=SlideTiles, carcinoma_roi_t=carcinoma_roi_t, stratified_filter=stratified_filter, - ) - - def generate_datasets(self) -> Iterable[Dataset[T]]: - self.tiles = ( - self.prepare_tiles(self.tiles) - if self.labeled - else filter_tiles_by_thresholds(self.tiles, self.thresholds) - ) - return ( - cast( - "Dataset[T]", - SlideTiles( - slide, - tiles=self.filter_tiles_by_slide(slide["id"]), - include_label=self.labeled, - transforms=self.transforms, - ), - ) - for _, slide in self.slides.iterrows() + transforms=transforms, ) @@ -62,7 +41,7 @@ class LabeledTilesDataset(TilesDataset[LabeledTileSample]): ... class UnlabeledTilesDataset(TilesDataset[UnlabeledTileSample]): ... -class SlideTiles(Dataset[LabeledTileSample | UnlabeledTileSample]): +class SlideTiles(BaseSingleSlideDataset): def __init__( self, slide_metadata: pd.Series, @@ -70,9 +49,11 @@ def __init__( include_label: bool, transforms: TransformType | None = None, ) -> None: - super().__init__() - - self.slide_metadata = slide_metadata + super().__init__( + slide_metadata=slide_metadata, + tiles=tiles, + include_label=include_label, + ) self.slide_tiles = OpenSlideTilesDataset( slide_path=slide_metadata.path, level=slide_metadata.level, @@ -81,14 +62,8 @@ def __init__( tiles=tiles, ) self.transforms = transforms - self.include_label = include_label self.to_tensor = ToTensorV2() - if len(tiles) == 0: - print( - f"Warning: No tiles found for slide {get_slide_name(slide_metadata)}." - ) - def __len__(self) -> int: return len(self.slide_tiles) From cd159f16cdcac685b8d0bdb30c07a72dcfeb25b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 09:05:35 +0000 Subject: [PATCH 101/284] feat: HF dataset --- .../datasets/bag_of_embeddings_dataset.py | 69 ++-- prostate_cancer/datamodule/datasets/base.py | 50 ++- .../datamodule/datasets/embeddings_dataset.py | 25 +- .../datamodule/datasets/tile_datasets.py | 27 +- prostate_cancer/typing.py | 13 + pyproject.toml | 4 +- uv.lock | 302 +++++++++++++++--- 7 files changed, 378 insertions(+), 112 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index c1ed0585..c8149c65 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -1,14 +1,16 @@ """These Datasets were taken from Adam Kukučka Ulcerative Colitis project and modified.""" +from collections import Counter from collections.abc import Iterable from pathlib import Path from typing import Generic, TypeVar import mlflow import mlflow.artifacts -import pandas as pd import torch import torch.nn.functional as F +from datasets import Dataset as HFDataset +from datasets import concatenate_datasets from torch.utils.data import Dataset from prostate_cancer.typing import ( @@ -30,54 +32,77 @@ def __init__( ) -> None: self.include_labels = carcinoma_roi_t is not None self.carcinoma_roi_t = carcinoma_roi_t + self.slides, self.tiles = self.download_artifacts(uris) if self.include_labels: - self.tiles["carcinoma"] = ( - self.tiles["carcinoma_roi_percentage"] > self.carcinoma_roi_t + self.tiles = self.tiles.map( + lambda r: { + "carcinoma": (r["carcinoma_roi_percentage"] > self.carcinoma_roi_t) + } ) self.padding = padding - self.max_embeddings = self.tiles["slide_id"].value_counts().max() + + # compute max tiles per slide (HF version) + slide_ids = self.tiles["slide_id"] + + self.max_embeddings = max(Counter(slide_ids).values()) + + self.tiles_by_slide: dict[bytes, list[int]] = {} + + for i, sid in enumerate(self.tiles["slide_id"]): + self.tiles_by_slide.setdefault(sid, []).append(i) def download_artifacts( self, tiling_uris: Iterable[str] - ) -> tuple[pd.DataFrame, pd.DataFrame]: - slide_dfs = [] - tile_dfs = [] + ) -> tuple[HFDataset, HFDataset]: + + slide_dsets = [] + tile_dsets = [] for tiling_uri in tiling_uris: - tiling_folder = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - slide_dfs.append(pd.read_parquet(tiling_folder / "slides.parquet")) - tile_dfs.append(pd.read_parquet(tiling_folder / "tiles.parquet")) + root = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - return ( - pd.concat(slide_dfs, ignore_index=True), - pd.concat(tile_dfs, ignore_index=True), - ) + # Load ALL parquet files in folders + slide_dsets.append(HFDataset.from_parquet(str(root / "slides/*.parquet"))) + + tile_dsets.append(HFDataset.from_parquet(str(root / "tiles/*.parquet"))) + + slides = concatenate_datasets(slide_dsets) + tiles = concatenate_datasets(tile_dsets) + + return slides, tiles def __len__(self) -> int: return len(self.slides) def __getitem__(self, idx: int) -> T: - slide_metadata = self.slides.iloc[idx] - slide_name = Path(slide_metadata.path).stem + slide_metadata = self.slides[idx] + + slide_name = Path(slide_metadata["path"]).stem + + tile_indices = self.tiles_by_slide[slide_metadata["id"]] + slide_tiles = self.tiles.select(tile_indices) - slide_tiles = self.tiles[self.tiles["slide_id"] == slide_metadata.id] slide_embeddings = torch.tensor(slide_tiles["embedding"]) pad_amount = self.max_embeddings - slide_embeddings.shape[0] assert pad_amount >= 0, "Invalid padding" if self.padding: - slide_embeddings = F.pad(slide_embeddings, (0, 0, 0, pad_amount), value=0.0) + slide_embeddings = F.pad( + slide_embeddings, + (0, 0, 0, pad_amount), + value=0.0, + ) metadata = SlideMetadata( slide_id=slide_metadata["id"], slide_name=slide_name, slide_path=slide_metadata["path"], - xs=torch.from_numpy(slide_tiles["x"].to_numpy()), - ys=torch.from_numpy(slide_tiles["y"].to_numpy()), + xs=torch.tensor(slide_tiles["x"]), + ys=torch.tensor(slide_tiles["y"]), ) if not self.include_labels: @@ -86,9 +111,7 @@ def __getitem__(self, idx: int) -> T: sl_label = torch.tensor(slide_metadata["carcinoma"]).float() tl_labels = torch.zeros(len(slide_embeddings)).float() - tl_labels[: len(slide_tiles)] = torch.tensor( - slide_tiles["carcinoma"].to_numpy() - ).float() + tl_labels[: len(slide_tiles)] = torch.tensor(slide_tiles["carcinoma"]).float() return slide_embeddings, tl_labels, sl_label, metadata # type: ignore[return-value] diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index b41cacc4..8745acc5 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -3,32 +3,33 @@ from pathlib import Path from typing import TypeVar, cast -import pandas as pd from albumentations.core.composition import TransformType +from datasets import Dataset as HFDataset from rationai.mlkit.data.datasets import MetaTiledSlides from torch.utils.data import Dataset -from prostate_cancer.typing import LabeledTileSample, UnlabeledTileSample +from prostate_cancer.typing import LabeledTileSample, UnlabeledTileSample, TilingSlideMetadata T = TypeVar("T", covariant=True) -def get_slide_name(slide_metadata: pd.Series) -> str: - return Path(slide_metadata.path).stem +def get_slide_name(slide_metadata: TilingSlideMetadata) -> str: + return Path(slide_metadata.get("path")).stem class BaseSingleSlideDataset(Dataset[LabeledTileSample | UnlabeledTileSample], ABC): def __init__( self, - slide_metadata: pd.Series, - tiles: pd.DataFrame, + slide_metadata: TilingSlideMetadata, + tiles: HFDataset, include_label: bool, ) -> None: super().__init__() - assert "embedding" in tiles.column, ( + assert "embedding" in tiles.column_names, ( "Embeddings Dataset requires embedding column" ) + self.include_label = include_label self.slide_metadata = slide_metadata self.tiles = tiles @@ -41,6 +42,9 @@ def __init__( class BaseTileDataset(MetaTiledSlides[T]): """This class abstracts the functionality shared across embedding and image datasets.""" + slides: HFDataset + tiles: HFDataset + def __init__( self, uris: Iterable[str], @@ -56,22 +60,34 @@ def __init__( self.single_slide_ds_cls = single_slide_ds_cls super().__init__(uris=uris) - def filter_non_carcinoma(self, tiles: pd.DataFrame) -> pd.DataFrame: + def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" - tiles_slide_cancer = ( - tiles["slide_id"] - .map(dict(zip(self.slides["id"], self.slides["carcinoma"], strict=True))) - .astype(int) + + slide_carcinoma = dict( + zip( + self.slides["id"], + self.slides["carcinoma"], + strict=True, + ) ) - return tiles[~((tiles_slide_cancer == 1) & (tiles["carcinoma"] == 0))] + return tiles.filter( + lambda row: ( + not (slide_carcinoma[row["slide_id"]] == 1 and row["carcinoma"] == 0) + ) + ) def generate_datasets(self) -> Iterable[Dataset[T]]: if self.labeled: - self.tiles["carcinoma"] = ( - self.tiles["carcinoma_roi_percentage"] > self.carcinoma_roi_t + self.tiles = self.tiles.map( + lambda row: { + "carcinoma": ( + row["carcinoma_roi_percentage"] > self.carcinoma_roi_t + ) + } ) + if self.stratified_filter: self.tiles = self.filter_non_carcinoma(self.tiles) @@ -86,8 +102,8 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: {"transforms": self.transforms} if self.transforms is not None else {} - ), # avoid sending transforms arg to embeddings dataset + ), ), ) - for _, slide in self.slides.iterrows() + for slide in self.slides ) diff --git a/prostate_cancer/datamodule/datasets/embeddings_dataset.py b/prostate_cancer/datamodule/datasets/embeddings_dataset.py index 81afe67f..e8302a55 100644 --- a/prostate_cancer/datamodule/datasets/embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/embeddings_dataset.py @@ -1,18 +1,15 @@ from collections.abc import Iterable -from typing import TypeAlias, TypeVar +from typing import TypeVar -import pandas as pd import torch +from datasets import Dataset as HFDataset from prostate_cancer.datamodule.datasets.base import ( BaseSingleSlideDataset, BaseTileDataset, get_slide_name, ) -from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample - - -SlideDfMetadata: TypeAlias = pd.Series +from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample, TilingSlideMetadata T = TypeVar("T", covariant=True) @@ -42,8 +39,8 @@ class UnlabeledEmbeddingsDataset(EmbeddingsDataset[UnlabeledTileSample]): ... class TileEmbeddingsSlide(BaseSingleSlideDataset): def __init__( self, - slide_metadata: pd.Series, - tiles: pd.DataFrame, + slide_metadata: TilingSlideMetadata, + tiles: HFDataset, include_label: bool, ) -> None: super().__init__( @@ -51,7 +48,7 @@ def __init__( tiles=tiles, include_label=include_label, ) - assert "embedding" in tiles.column, ( + assert "embedding" in tiles.column_names, ( "Embeddings Dataset requires embedding column" ) @@ -59,14 +56,18 @@ def __len__(self) -> int: return len(self.tiles) def __getitem__(self, idx: int) -> LabeledTileSample | UnlabeledTileSample: - tile = self.tiles.iloc[idx] + tile = self.tiles[idx] + vector = torch.tensor(tile["embedding"]) + metadata = TileMetadata( - slide=get_slide_name(self.slide_metadata), x=tile["x"], y=tile["y"] + slide=get_slide_name(self.slide_metadata), + x=tile["x"], + y=tile["y"], ) if not self.include_label: return vector, metadata - label = torch.tensor([self.tiles.iloc[idx]["carcinoma"]]).float() + label = torch.tensor([tile["carcinoma"]]).float() return vector, label, metadata diff --git a/prostate_cancer/datamodule/datasets/tile_datasets.py b/prostate_cancer/datamodule/datasets/tile_datasets.py index 40c65a5d..698e0766 100644 --- a/prostate_cancer/datamodule/datasets/tile_datasets.py +++ b/prostate_cancer/datamodule/datasets/tile_datasets.py @@ -1,17 +1,17 @@ from collections.abc import Iterable from typing import TypeVar -import pandas as pd import torch from albumentations.core.composition import TransformType from albumentations.pytorch import ToTensorV2 +from datasets import Dataset as HFDataset from rationai.mlkit.data.datasets import OpenSlideTilesDataset from prostate_cancer.datamodule.datasets.base import ( BaseSingleSlideDataset, BaseTileDataset, ) -from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample +from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample, TilingSlideMetadata T = TypeVar("T", covariant=True) @@ -44,8 +44,8 @@ class UnlabeledTilesDataset(TilesDataset[UnlabeledTileSample]): ... class SlideTiles(BaseSingleSlideDataset): def __init__( self, - slide_metadata: pd.Series, - tiles: pd.DataFrame, + slide_metadata: TilingSlideMetadata, + tiles: HFDataset, include_label: bool, transforms: TransformType | None = None, ) -> None: @@ -55,10 +55,10 @@ def __init__( include_label=include_label, ) self.slide_tiles = OpenSlideTilesDataset( - slide_path=slide_metadata.path, - level=slide_metadata.level, - tile_extent_x=slide_metadata.tile_extent_x, - tile_extent_y=slide_metadata.tile_extent_y, + slide_path=slide_metadata["path"], + level=slide_metadata["level"], + tile_extent_x=slide_metadata["tile_extent_x"], + tile_extent_y=slide_metadata["tile_extent_y"], tiles=tiles, ) self.transforms = transforms @@ -69,10 +69,13 @@ def __len__(self) -> int: def __getitem__(self, idx: int) -> LabeledTileSample | UnlabeledTileSample: image = self.slide_tiles[idx] + + tile_row = self.slide_tiles.tiles[idx] + metadata = TileMetadata( slide=self.slide_tiles.slide_path.stem, - x=self.slide_tiles.tiles.iloc[idx]["x"], - y=self.slide_tiles.tiles.iloc[idx]["y"], + x=tile_row["x"], + y=tile_row["y"], ) if self.transforms is not None: @@ -81,9 +84,7 @@ def __getitem__(self, idx: int) -> LabeledTileSample | UnlabeledTileSample: tensor_image = self.to_tensor(image=image)["image"] if self.include_label: - label = torch.tensor( - [self.slide_tiles.tiles.iloc[idx]["carcinoma"]] - ).float() + label = torch.tensor([tile_row["carcinoma"]]).float() return tensor_image, label, metadata return tensor_image, metadata diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index d310714a..2db2dbc8 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -3,6 +3,19 @@ from torch import Tensor +class TilingSlideMetadata(TypedDict): + tile_x: int + tile_y: int + path: str + slide_id: bytes + level: int + tile_extent_x: int + tile_extent_y: int + mpp_x: float + mpp_y: float + carcinoma: bool + + class TileMetadata(TypedDict): slide: str x: int diff --git a/pyproject.toml b/pyproject.toml index e55ac3dd..23655744 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "triton>=3.3.0", "tqdm==4.67.1", "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", - "rationai-mlkit @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git@549f5be72a162e7debf3a911ea4649f50080b1d0", + "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git@68904dfd216ce19454e08a032f7e4dd978a85707", @@ -32,7 +32,7 @@ dependencies = [ "matplotlib==3.10.1", "scikit-learn==1.8.0", "openpyxl==3.1.5", - "mlflow==2.21.3", + "mlflow>=3.10.1", "deprecated>=1.3.1", "pillow==11.3.0", "huggingface_hub>=1.12.2", diff --git a/uv.lock b/uv.lock index 58464e29..dc59a68c 100644 --- a/uv.lock +++ b/uv.lock @@ -532,6 +532,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/8c/27651e85dab9897c65c4acf7abcd924e85f8726d282804fc7686f1387520/databricks_sdk-0.105.0-py3-none-any.whl", hash = "sha256:f1624e434588e2174052bc3646670f17581f3bb269b9438f9121e9df4a59eebd", size = 866235, upload-time = "2026-04-23T08:53:42.01Z" }, ] +[[package]] +name = "datasets" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/85/ce4f780c32f7e36d71257f1c27e8ba898ebe379cb54f211f5f2013f2c219/datasets-5.0.0.tar.gz", hash = "sha256:83dbbbdb07a33b82192b8c419deb18739b138ee2ce1a322d55ce6b100954ec1a", size = 631708, upload-time = "2026-06-05T13:18:26.124Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/66/73034ad30b59f13439b75e620989dacba4c047256e358ba7c2e9ec98ea22/datasets-5.0.0-py3-none-any.whl", hash = "sha256:7dd34927a0fd7046e98aad5cb9430e699c373238a15befa7b9bf22b991a7fee6", size = 555084, upload-time = "2026-06-05T13:18:24.435Z" }, +] + [[package]] name = "debugpy" version = "1.8.21" @@ -570,6 +595,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + [[package]] name = "distlib" version = "0.4.0" @@ -665,6 +699,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, ] +[[package]] +name = "flask-cors" +version = "6.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/03/4e464a50860f9adf08b5c1d3479cb8ea1f12af2aa69535c7042c6e628135/flask_cors-6.0.5.tar.gz", hash = "sha256:30c5031552cd59f620ac0c8211dac45b345d3b2df310e7721879e4f46ef9c601", size = 101386, upload-time = "2026-06-08T20:20:17.765Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/55/5bb1a2d918e9f02f131e47a59032bae70e48050e986e941511fd737a935c/flask_cors-6.0.5-py3-none-any.whl", hash = "sha256:68fcf75693e961f3af26683b23c4b9a8fb6b64de17d20d0c37b95e8de7ab2ed8", size = 16692, upload-time = "2026-06-08T20:20:16.247Z" }, +] + [[package]] name = "fonttools" version = "4.62.1" @@ -970,6 +1017,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "huey" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/cb/58f229149944602917a976d533d3fe7d54770d6ea18df5931e3f4f313fa0/huey-3.0.3.tar.gz", hash = "sha256:1a17fef95fc8432f75413f1b77439cef5f3493c1ddbfba9151756b31a1b2dad3", size = 263604, upload-time = "2026-06-12T01:53:55.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/82/f85d8918949786420716a5e421525ab12aa084bcbe32d86c9743e50bcf3d/huey-3.0.3-py3-none-any.whl", hash = "sha256:d1c687734778b8282c035a943eead8368c1736bb28abc006596fbbc01bdc96dc", size = 94945, upload-time = "2026-06-12T01:53:53.981Z" }, +] + [[package]] name = "huggingface-hub" version = "1.12.2" @@ -1401,15 +1457,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, ] -[[package]] -name = "markdown" -version = "3.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, -] - [[package]] name = "markdown-it-py" version = "4.0.0" @@ -1523,34 +1570,38 @@ wheels = [ [[package]] name = "mlflow" -version = "2.21.3" +version = "3.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "aiohttp" }, { name = "alembic" }, + { name = "cryptography" }, { name = "docker" }, { name = "flask" }, + { name = "flask-cors" }, { name = "graphene" }, { name = "gunicorn", marker = "sys_platform != 'win32'" }, - { name = "jinja2" }, - { name = "markdown" }, + { name = "huey" }, { name = "matplotlib" }, { name = "mlflow-skinny" }, + { name = "mlflow-tracing" }, { name = "numpy" }, { name = "pandas" }, { name = "pyarrow" }, { name = "scikit-learn" }, { name = "scipy" }, + { name = "skops" }, { name = "sqlalchemy" }, { name = "waitress", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/1c/f7d0f27ff2136b14fed2405cedd9500735caa93b51038908df11604207bd/mlflow-2.21.3.tar.gz", hash = "sha256:5dc45dc5b8e2bcd5727052d14de2ab4bc372ff49aa20f5dc097eef913a1d0c01", size = 27622035, upload-time = "2025-04-03T04:58:19.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/0b/3404a057daceffe9ce18cd08868648a1e9b817270177bdf8a764576b988b/mlflow-3.14.0.tar.gz", hash = "sha256:5a1f818fa003035c724162096ce3ded7bc7bc47a1cae595df6173961983f4718", size = 11792369, upload-time = "2026-06-17T07:57:44.712Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/f1/be329ef23da1933135afc5e493cbe1b7ecf5302890f2a7ac2124b64c01bd/mlflow-2.21.3-py3-none-any.whl", hash = "sha256:cf2fd6b5ea5f9f977e95a73c73e58a750a3712fe397dbb658e966b3d1713fe53", size = 28236278, upload-time = "2025-04-03T04:58:16.013Z" }, + { url = "https://files.pythonhosted.org/packages/de/b9/76dcdef7f7f856b36f18cfcd752c2717d9847812a0aaa36d50a7baed569d/mlflow-3.14.0-py3-none-any.whl", hash = "sha256:dbf77f7cdb5b5c0ec59b4671c61730b1b914b4dff7a2892e267a547cb5454f56", size = 12564161, upload-time = "2026-06-17T07:57:42.348Z" }, ] [[package]] name = "mlflow-skinny" -version = "2.21.3" +version = "3.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, @@ -1561,19 +1612,41 @@ dependencies = [ { name = "gitpython" }, { name = "importlib-metadata" }, { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, { name = "packaging" }, { name = "protobuf" }, { name = "pydantic" }, + { name = "python-dotenv" }, { name = "pyyaml" }, { name = "requests" }, { name = "sqlparse" }, + { name = "starlette" }, { name = "typing-extensions" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/62/5a229297385a906e20ffe7fec0e5c9e72a6f02bddebbab62fd6637e0bfaa/mlflow_skinny-2.21.3.tar.gz", hash = "sha256:4623144ad0a7441f13e0face059165754af72d4e16e4418abb16f56fcde50131", size = 5773036, upload-time = "2025-04-03T04:44:25.02Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/4f/a054cd8860590e4e942aee1aab3c94307878159f945fa844acc9ea787721/mlflow_skinny-3.14.0.tar.gz", hash = "sha256:e50f4506422c7737157ae6643c165122af7898345f2e828fa93c4f10128653cf", size = 2901772, upload-time = "2026-06-17T07:57:44.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/e7/b80f76ce689b9d6f21cdb84abb2b02148a1149e63a6428dd2c629cefd061/mlflow_skinny-3.14.0-py3-none-any.whl", hash = "sha256:a4880e086365871ef9d78e727a34ea5fb1ce615689579998d48e8c65ee1665a9", size = 3462788, upload-time = "2026-06-17T07:57:42.583Z" }, +] + +[[package]] +name = "mlflow-tracing" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "databricks-sdk" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/34/ff5e72919b4eec8fe65e6fc843978a1a512194e6fafbd1761deca48269ad/mlflow_tracing-3.14.0.tar.gz", hash = "sha256:c2f701e001d35964f23fbbdfdda36c818a76c157b912ae83781199fd714be09a", size = 1429017, upload-time = "2026-06-17T07:58:00.647Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/f8/b71f88ca373f248fd7fdf3751f74c7b36a71b7ee2b5f4b803ee053ac963a/mlflow_skinny-2.21.3-py3-none-any.whl", hash = "sha256:a93d928a5123bfbfdcb8ded749156b99813158e8b16bdf137bebb0883cea353d", size = 6145084, upload-time = "2025-04-03T04:44:22.611Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4a/4658a9e514c8f079e40b608661844b9beb21c7530e6ee1e7f830cf81541e/mlflow_tracing-3.14.0-py3-none-any.whl", hash = "sha256:854488dd18068f15e2a56f1cc7b8868c611d09ea39068d0a691a3f07e0048cae", size = 1703863, upload-time = "2026-06-17T07:57:58.687Z" }, ] [[package]] @@ -1674,6 +1747,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] +[[package]] +name = "multiprocess" +version = "0.70.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" }, + { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414, upload-time = "2026-01-19T06:47:35.915Z" }, + { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" }, +] + [[package]] name = "mypy" version = "1.20.2" @@ -2025,6 +2114,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/55/63eac3e1089b768ba014091fdd2ae8a9a440c821ef5e2b786909c94c8836/opentelemetry_proto-1.42.1.tar.gz", hash = "sha256:c6a51e6b4f05ae63565f3a113217f3d2bfaec68f78c02d7a6c85f9010d1cfca6", size = 45839, upload-time = "2026-05-21T16:33:03.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/9d/171c02c84a76940b7e601805b3bb536985aded9168fbcc9ba52f0a730fa2/opentelemetry_proto-1.42.1-py3-none-any.whl", hash = "sha256:dedb74cba2886c59c7789b227a7a670613025a07489040050aedff6e5c0fb43c", size = 71782, upload-time = "2026-05-21T16:32:44.867Z" }, +] + [[package]] name = "opentelemetry-sdk" version = "1.41.1" @@ -2194,6 +2295,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] +[[package]] +name = "prettytable" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -2308,13 +2421,13 @@ requires-dist = [ { name = "ipykernel", specifier = ">=7.2.0" }, { name = "lightning", specifier = "==2.6.0" }, { name = "matplotlib", specifier = "==3.10.1" }, - { name = "mlflow", specifier = "==2.21.3" }, + { name = "mlflow", specifier = ">=3.10.1" }, { name = "numpy", specifier = ">=2.3.2" }, { name = "openpyxl", specifier = "==3.1.5" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "pillow", specifier = "==11.3.0" }, { name = "rationai-masks", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" }, - { name = "rationai-mlkit", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git?rev=549f5be72a162e7debf3a911ea4649f50080b1d0" }, + { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git" }, { name = "rationai-sdk", git = "https://github.com/RationAI/rationai-sdk-python.git?rev=68904dfd216ce19454e08a032f7e4dd978a85707" }, { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" }, { name = "rationai-tiling", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" }, @@ -2393,30 +2506,31 @@ wheels = [ [[package]] name = "pyarrow" -version = "19.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437, upload-time = "2025-02-18T18:55:57.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749, upload-time = "2025-02-18T18:53:00.062Z" }, - { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007, upload-time = "2025-02-18T18:53:06.581Z" }, - { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566, upload-time = "2025-02-18T18:53:11.958Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991, upload-time = "2025-02-18T18:53:17.678Z" }, - { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986, upload-time = "2025-02-18T18:53:26.263Z" }, - { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026, upload-time = "2025-02-18T18:53:33.063Z" }, - { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108, upload-time = "2025-02-18T18:53:38.462Z" }, - { url = "https://files.pythonhosted.org/packages/2b/8d/275c58d4b00781bd36579501a259eacc5c6dfb369be4ddeb672ceb551d2d/pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c", size = 30653552, upload-time = "2025-02-18T18:53:44.357Z" }, - { url = "https://files.pythonhosted.org/packages/a0/9e/e6aca5cc4ef0c7aec5f8db93feb0bde08dbad8c56b9014216205d271101b/pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae", size = 32103413, upload-time = "2025-02-18T18:53:52.971Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fa/a7033f66e5d4f1308c7eb0dfcd2ccd70f881724eb6fd1776657fdf65458f/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4", size = 41134869, upload-time = "2025-02-18T18:53:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/2d/92/34d2569be8e7abdc9d145c98dc410db0071ac579b92ebc30da35f500d630/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2", size = 42192626, upload-time = "2025-02-18T18:54:06.062Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1f/80c617b1084fc833804dc3309aa9d8daacd46f9ec8d736df733f15aebe2c/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6", size = 40496708, upload-time = "2025-02-18T18:54:12.347Z" }, - { url = "https://files.pythonhosted.org/packages/e6/90/83698fcecf939a611c8d9a78e38e7fed7792dcc4317e29e72cf8135526fb/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136", size = 42075728, upload-time = "2025-02-18T18:54:19.364Z" }, - { url = "https://files.pythonhosted.org/packages/40/49/2325f5c9e7a1c125c01ba0c509d400b152c972a47958768e4e35e04d13d8/pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef", size = 25242568, upload-time = "2025-02-18T18:54:25.846Z" }, - { url = "https://files.pythonhosted.org/packages/3f/72/135088d995a759d4d916ec4824cb19e066585b4909ebad4ab196177aa825/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0", size = 30702371, upload-time = "2025-02-18T18:54:30.665Z" }, - { url = "https://files.pythonhosted.org/packages/2e/01/00beeebd33d6bac701f20816a29d2018eba463616bbc07397fdf99ac4ce3/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9", size = 32116046, upload-time = "2025-02-18T18:54:35.995Z" }, - { url = "https://files.pythonhosted.org/packages/1f/c9/23b1ea718dfe967cbd986d16cf2a31fe59d015874258baae16d7ea0ccabc/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3", size = 41091183, upload-time = "2025-02-18T18:54:42.662Z" }, - { url = "https://files.pythonhosted.org/packages/3a/d4/b4a3aa781a2c715520aa8ab4fe2e7fa49d33a1d4e71c8fc6ab7b5de7a3f8/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6", size = 42171896, upload-time = "2025-02-18T18:54:49.808Z" }, - { url = "https://files.pythonhosted.org/packages/23/1b/716d4cd5a3cbc387c6e6745d2704c4b46654ba2668260d25c402626c5ddb/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a", size = 40464851, upload-time = "2025-02-18T18:54:57.073Z" }, - { url = "https://files.pythonhosted.org/packages/ed/bd/54907846383dcc7ee28772d7e646f6c34276a17da740002a5cefe90f04f7/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8", size = 42085744, upload-time = "2025-02-18T18:55:08.562Z" }, +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, ] [[package]] @@ -2621,6 +2735,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + [[package]] name = "pytorch-lightning" version = "2.6.1" @@ -2784,15 +2907,18 @@ dependencies = [ [[package]] name = "rationai-mlkit" -version = "0.3.1" -source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/mlkit.git?rev=549f5be72a162e7debf3a911ea4649f50080b1d0#549f5be72a162e7debf3a911ea4649f50080b1d0" } +version = "0.4.1" +source = { git = "https://github.com/RationAI/mlkit.git#51d1b52cdf4800962cf6f97a1cba1f72d78b8aa4" } dependencies = [ + { name = "datasets" }, { name = "gitpython" }, { name = "hydra-core" }, { name = "lightning" }, { name = "mlflow" }, { name = "openslide-python" }, + { name = "pyarrow" }, { name = "rationai-masks" }, + { name = "ratiopath" }, { name = "torch" }, ] @@ -3305,6 +3431,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "skops" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "prettytable" }, + { name = "scikit-learn" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/9f/46448c4e41a4c5ee4bdb74b3758af48e5ff0faeffe40f4e301bfc7594894/skops-0.14.0.tar.gz", hash = "sha256:6c8c0e047f691a3a582c3258943eecafcbfd79c8c7eef66260f3703e363254f0", size = 608084, upload-time = "2026-04-20T18:23:55.336Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/0e/3ae19fa941522cd98e119762e7181d371c8dba0b2d72bfaf9522692e329c/skops-0.14.0-py3-none-any.whl", hash = "sha256:60a5db78a9db46ccee2139a0ba13ab5afb1c96f4749b382e75a371291bbe3e36", size = 132198, upload-time = "2026-04-20T18:23:54.018Z" }, +] + [[package]] name = "smmap" version = "5.0.3" @@ -3806,6 +3948,76 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, ] +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, +] + [[package]] name = "yarl" version = "1.23.0" From e41cdacc9cf9de8d4f430a56041a9eea1a13a717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 09:08:06 +0000 Subject: [PATCH 102/284] fix: typo --- .../datamodule/datasets/{tile_datasets.py => tile_dataset.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename prostate_cancer/datamodule/datasets/{tile_datasets.py => tile_dataset.py} (100%) diff --git a/prostate_cancer/datamodule/datasets/tile_datasets.py b/prostate_cancer/datamodule/datasets/tile_dataset.py similarity index 100% rename from prostate_cancer/datamodule/datasets/tile_datasets.py rename to prostate_cancer/datamodule/datasets/tile_dataset.py From d2bde8ad17ecb471ab1b7e60ea3390b7ca6560aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 09:12:35 +0000 Subject: [PATCH 103/284] fix: rem assert --- configs/experiment/predict/images/base.yaml | 2 +- configs/experiment/predict/images/google_vit/base.yaml | 2 +- configs/experiment/predict/images/google_vit/stain_base.yaml | 2 +- configs/experiment/test/images/base.yaml | 2 +- configs/experiment/test/images/google_vit/base.yaml | 2 +- configs/experiment/test/images/google_vit/stain_base.yaml | 2 +- configs/experiment/train/images/google_vit/base.yaml | 4 ++-- configs/experiment/train/images/google_vit/base_stain.yaml | 4 ++-- configs/experiment/train/images/resnet50/base.yaml | 4 ++-- configs/experiment/train/images/resnet50/base_stain.yaml | 4 ++-- configs/experiment/train/images/vgg16/base.yaml | 4 ++-- configs/experiment/train/images/vgg16/base_stain.yaml | 4 ++-- prostate_cancer/datamodule/datasets/base.py | 4 ---- 13 files changed, 18 insertions(+), 22 deletions(-) diff --git a/configs/experiment/predict/images/base.yaml b/configs/experiment/predict/images/base.yaml index 9c39bc2a..f9d913ea 100644 --- a/configs/experiment/predict/images/base.yaml +++ b/configs/experiment/predict/images/base.yaml @@ -11,5 +11,5 @@ datamodule: predict: uris: - - ${predict_data.tiles_uri_512} + - ${predict_data.tiles_filtered_uri_512} diff --git a/configs/experiment/predict/images/google_vit/base.yaml b/configs/experiment/predict/images/google_vit/base.yaml index 88e908bc..929753ca 100644 --- a/configs/experiment/predict/images/google_vit/base.yaml +++ b/configs/experiment/predict/images/google_vit/base.yaml @@ -13,4 +13,4 @@ datamodule: predict: uris: - - ${predict_data.tiles_uri_224} + - ${predict_data.tiles_filtered_uri_224} diff --git a/configs/experiment/predict/images/google_vit/stain_base.yaml b/configs/experiment/predict/images/google_vit/stain_base.yaml index d450ce66..5096ee0a 100644 --- a/configs/experiment/predict/images/google_vit/stain_base.yaml +++ b/configs/experiment/predict/images/google_vit/stain_base.yaml @@ -13,4 +13,4 @@ datamodule: predict: uris: - - ${predict_data.tiles_uri_224} + - ${predict_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/base.yaml b/configs/experiment/test/images/base.yaml index 6a842555..803cda61 100644 --- a/configs/experiment/test/images/base.yaml +++ b/configs/experiment/test/images/base.yaml @@ -11,4 +11,4 @@ datamodule: test: uris: - - ${test_data.tiles_uri_512} \ No newline at end of file + - ${test_data.tiles_filtered_uri_512} \ No newline at end of file diff --git a/configs/experiment/test/images/google_vit/base.yaml b/configs/experiment/test/images/google_vit/base.yaml index 3b63267a..7e426a1a 100644 --- a/configs/experiment/test/images/google_vit/base.yaml +++ b/configs/experiment/test/images/google_vit/base.yaml @@ -12,4 +12,4 @@ datamodule: test: uris: - - ${test_data.tiles_uri_224} + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/google_vit/stain_base.yaml b/configs/experiment/test/images/google_vit/stain_base.yaml index 65481cd2..0e415172 100644 --- a/configs/experiment/test/images/google_vit/stain_base.yaml +++ b/configs/experiment/test/images/google_vit/stain_base.yaml @@ -12,4 +12,4 @@ datamodule: test: uris: - - ${test_data.tiles_uri_224} + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/google_vit/base.yaml b/configs/experiment/train/images/google_vit/base.yaml index 56ced7cc..d5530967 100644 --- a/configs/experiment/train/images/google_vit/base.yaml +++ b/configs/experiment/train/images/google_vit/base.yaml @@ -11,7 +11,7 @@ model_name: "Google ViT" datamodule: train: uris: - - ${train_data.tiles_uri_224} + - ${train_data.tiles_filtered_uri_224} val: uris: - - ${val_data.tiles_uri_224} + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/google_vit/base_stain.yaml b/configs/experiment/train/images/google_vit/base_stain.yaml index f347b5b8..80ff80a8 100644 --- a/configs/experiment/train/images/google_vit/base_stain.yaml +++ b/configs/experiment/train/images/google_vit/base_stain.yaml @@ -11,7 +11,7 @@ model_name: "Google ViT (Stain)" datamodule: train: uris: - - ${train_data.tiles_uri_224} + - ${train_data.tiles_filtered_uri_224} val: uris: - - ${val_data.tiles_uri_224} + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/resnet50/base.yaml b/configs/experiment/train/images/resnet50/base.yaml index ae7f751e..bfa6d390 100644 --- a/configs/experiment/train/images/resnet50/base.yaml +++ b/configs/experiment/train/images/resnet50/base.yaml @@ -11,7 +11,7 @@ model_name: "ResNet50" datamodule: train: uris: - - ${train_data.tiles_uri_512} + - ${train_data.tiles_filtered_uri_512} val: uris: - - ${val_data.tiles_uri_512} + - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/resnet50/base_stain.yaml b/configs/experiment/train/images/resnet50/base_stain.yaml index 3da3df8d..34855979 100644 --- a/configs/experiment/train/images/resnet50/base_stain.yaml +++ b/configs/experiment/train/images/resnet50/base_stain.yaml @@ -11,7 +11,7 @@ model_name: "ResNet50 Stain" datamodule: train: uris: - - ${train_data.tiles_uri_512} + - ${train_data.tiles_filtered_uri_512} val: uris: - - ${val_data.tiles_uri_512} + - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/vgg16/base.yaml b/configs/experiment/train/images/vgg16/base.yaml index 9e30378e..38ab882b 100644 --- a/configs/experiment/train/images/vgg16/base.yaml +++ b/configs/experiment/train/images/vgg16/base.yaml @@ -11,7 +11,7 @@ model_name: "VGG-16" datamodule: train: uris: - - ${train_data.tiles_uri_512} + - ${train_data.tiles_filtered_uri_512} val: uris: - - ${val_data.tiles_uri_512} + - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/vgg16/base_stain.yaml b/configs/experiment/train/images/vgg16/base_stain.yaml index 37067f97..cfea58c9 100644 --- a/configs/experiment/train/images/vgg16/base_stain.yaml +++ b/configs/experiment/train/images/vgg16/base_stain.yaml @@ -11,7 +11,7 @@ model_name: "VGG-16 Stain" datamodule: train: uris: - - ${train_data.tiles_uri_512} + - ${train_data.tiles_filtered_uri_512} val: uris: - - ${val_data.tiles_uri_512} + - ${val_data.tiles_filtered_uri_512} diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 8745acc5..c8285d23 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -26,10 +26,6 @@ def __init__( include_label: bool, ) -> None: super().__init__() - assert "embedding" in tiles.column_names, ( - "Embeddings Dataset requires embedding column" - ) - self.include_label = include_label self.slide_metadata = slide_metadata self.tiles = tiles From 3904ee017ed74dca2a0730daa8e0b9a3e623d500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 09:41:23 +0000 Subject: [PATCH 104/284] fix: callbacks --- preprocessing/embeddings/tile_embeddings.py | 1 - .../callbacks/aggregator_callback.py | 9 +- prostate_cancer/callbacks/cam_callback.py | 15 ++-- .../carcinoma_prediction_table_callback.py | 8 +- .../callbacks/estimation_callback.py | 9 +- prostate_cancer/callbacks/heatmap_callback.py | 25 +++--- .../callbacks/mil_prediction_callback.py | 87 ++++++++++++------- .../callbacks/num_positive_callback.py | 5 +- prostate_cancer/datamodule/datasets/base.py | 6 +- .../datamodule/datasets/embeddings_dataset.py | 7 +- .../datamodule/datasets/tile_dataset.py | 7 +- prostate_cancer/typing.py | 11 ++- 12 files changed, 118 insertions(+), 72 deletions(-) diff --git a/preprocessing/embeddings/tile_embeddings.py b/preprocessing/embeddings/tile_embeddings.py index 64143a6f..19e0c502 100644 --- a/preprocessing/embeddings/tile_embeddings.py +++ b/preprocessing/embeddings/tile_embeddings.py @@ -49,7 +49,6 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: for uri in uris: dataset = UnlabeledTilesDataset( uris=(uri,), - thresholds=config.thresholds, transforms=A.Compose( [ A.Normalize( diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index 902215e4..035ffad8 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -4,12 +4,11 @@ import lightning.pytorch as pl import mlflow -import pandas as pd import torch from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from rationai.mlkit.metrics.aggregators import Aggregator -from prostate_cancer.typing import UnlabeledTileSampleBatch +from prostate_cancer.typing import TilingSlideMetadata, UnlabeledTileSampleBatch if TYPE_CHECKING: @@ -29,7 +28,9 @@ def on_predict_dataloader_start( # aggregator cannot be reset, thus, its original state is copied for each slide self.aggregator = deepcopy(self.aggregator_original) datamodule = cast("TileDataModule", trainer.datamodule) - self.slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) + self.slide = cast( + "TilingSlideMetadata", datamodule.predict.slides[dataloader_idx] + ) def on_predict_batch_end( self, @@ -57,7 +58,7 @@ def on_predict_dataloader_end( # Compute the aggregated results pred, _ = self.aggregator.compute() table: dict[str, Any] = { - "slide_name": Path(self.slide.path).stem, + "slide_name": Path(self.slide["path"]).stem, "prediction": pred.item(), } diff --git a/prostate_cancer/callbacks/cam_callback.py b/prostate_cancer/callbacks/cam_callback.py index c6bae6a6..150b08cd 100644 --- a/prostate_cancer/callbacks/cam_callback.py +++ b/prostate_cancer/callbacks/cam_callback.py @@ -4,7 +4,6 @@ import lightning import lightning.pytorch as pl import mlflow -import pandas as pd import pyvips import torch from rationai.masks import write_big_tiff @@ -13,7 +12,7 @@ from torchvision.transforms import Resize from prostate_cancer.cnn_model import CNNProstateModel -from prostate_cancer.typing import LabeledTileSampleBatch +from prostate_cancer.typing import LabeledTileSampleBatch, TilingSlideMetadata if TYPE_CHECKING: @@ -43,16 +42,16 @@ def on_test_dataloader_start( raise ValueError("Trainer should have datamodule attribute") datamodule = cast("TileDataModule", trainer.datamodule) - slide = cast("pd.Series", datamodule.test.slides.iloc[dataloader_idx]) + slide = cast("TilingSlideMetadata", datamodule.test.slides[dataloader_idx]) self.save_dir = "cam_explanations" self.mask_builder = TileMaskBuilder( save_dir=self.save_dir, - filename=Path(slide.path).stem, - extent_x=slide.extent_x, - extent_y=slide.extent_y, - mpp_x=slide.mpp_x, - mpp_y=slide.mpp_y, + filename=Path(slide["path"]).stem, + extent_x=slide["extent_x"], + extent_y=slide["extent_y"], + mpp_x=slide["mpp_x"], + mpp_y=slide["mpp_y"], ) def on_test_dataloader_end( diff --git a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py index ce674d73..272838f9 100644 --- a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py +++ b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py @@ -7,7 +7,7 @@ import torch from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle -from prostate_cancer.typing import UnlabeledTileSampleBatch +from prostate_cancer.typing import TilingSlideMetadata, UnlabeledTileSampleBatch if TYPE_CHECKING: @@ -28,7 +28,9 @@ def on_predict_dataloader_start( raise ValueError("Trainer should have datamodule attribute") datamodule = cast("TileDataModule", trainer.datamodule) - self.slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) + self.slide = cast( + "TilingSlideMetadata", datamodule.predict.slides[dataloader_idx] + ) self.table: list[dict[str, Any]] = [] def on_predict_batch_end( @@ -45,7 +47,7 @@ def on_predict_batch_end( for i, prediction in enumerate(outputs): self.table.append( { - "slide": Path(self.slide.path).stem, + "slide": Path(self.slide["path"]).stem, "x": metadata["x"][i].item(), "y": metadata["y"][i].item(), "prediction": prediction.item(), diff --git a/prostate_cancer/callbacks/estimation_callback.py b/prostate_cancer/callbacks/estimation_callback.py index 824d2c76..ad51daa6 100644 --- a/prostate_cancer/callbacks/estimation_callback.py +++ b/prostate_cancer/callbacks/estimation_callback.py @@ -4,13 +4,12 @@ import lightning.pytorch as pl import mlflow -import pandas as pd import torch from hydra.utils import get_class from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from rationai.mlkit.metrics.aggregators import Aggregator -from prostate_cancer.typing import UnlabeledTileSampleBatch +from prostate_cancer.typing import TilingSlideMetadata, UnlabeledTileSampleBatch if TYPE_CHECKING: @@ -48,7 +47,9 @@ def on_predict_dataloader_start( ) datamodule = cast("TileDataModule", trainer.datamodule) - self.slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) + self.slide = cast( + "TilingSlideMetadata", datamodule.predict.slides[dataloader_idx] + ) def on_predict_batch_end( self, @@ -74,7 +75,7 @@ def on_predict_dataloader_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int ) -> None: # Compute the aggregated results for each kernel - table: dict[str, Any] = {"slide_name": Path(self.slide.path).stem} + table: dict[str, Any] = {"slide_name": Path(self.slide["path"]).stem} for values, aggregator in zip( self.values_product, self.aggregators, strict=True diff --git a/prostate_cancer/callbacks/heatmap_callback.py b/prostate_cancer/callbacks/heatmap_callback.py index e27eebf9..fa058ec7 100644 --- a/prostate_cancer/callbacks/heatmap_callback.py +++ b/prostate_cancer/callbacks/heatmap_callback.py @@ -3,11 +3,14 @@ import lightning.pytorch as pl import mlflow -import pandas as pd from rationai.masks.mask_builders import ScalarMaskBuilder from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle -from prostate_cancer.typing import LabeledTileSampleBatch, UnlabeledTileSampleBatch +from prostate_cancer.typing import ( + LabeledTileSampleBatch, + TilingSlideMetadata, + UnlabeledTileSampleBatch, +) if TYPE_CHECKING: @@ -30,7 +33,9 @@ def _on_dataloader_start( raise ValueError("Trainer should have datamodule attribute") datamodule = cast("TileDataModule", trainer.datamodule) - slide = cast("pd.Series", getattr(datamodule, mode).slides.iloc[dataloader_idx]) + slide = cast( + "TilingSlideMetadata", getattr(datamodule, mode).slides[dataloader_idx] + ) # Create temporary directory tmp_dir = Path("tmp_dir") @@ -39,13 +44,13 @@ def _on_dataloader_start( # Initialize the mask builder self.mask_builder: ScalarMaskBuilder = ScalarMaskBuilder( save_dir=tmp_dir, - filename=Path(slide.path).stem, - extent_x=slide.extent_x, - extent_y=slide.extent_y, - mpp_x=slide.mpp_x, - mpp_y=slide.mpp_y, - extent_tile=slide.tile_extent_x, - stride=slide.stride_x, + filename=Path(slide["path"]).stem, + extent_x=slide["extent_x"], + extent_y=slide["extent_y"], + mpp_x=slide["mpp_x"], + mpp_y=slide["mpp_y"], + extent_tile=slide["tile_extent_x"], + stride=slide["stride_x"], ) def on_test_dataloader_start( diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index e90dd4ec..61c27b55 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -2,8 +2,8 @@ from typing import TYPE_CHECKING, cast import mlflow -import pandas as pd import torch +from datasets import Dataset as HFDataset from lightning import Callback, LightningModule, Trainer from rationai.masks.mask_builders import ScalarMaskBuilder from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger @@ -16,38 +16,48 @@ def min_max_normalization(tensor: torch.Tensor) -> torch.Tensor: - weights_max = tensor.max() - weights_min = tensor.min() - return (tensor - weights_min) / (weights_max - weights_min) + return (tensor - tensor.min()) / (tensor.max() - tensor.min()) class MILPredictionCallback(Callback): - def get_mask_builder( - self, slide_name: str, trainer: Trainer, save_dir: str - ) -> ScalarMaskBuilder: + def setup(self, trainer: Trainer, pl_module: LightningModule, stage: str | None=None) -> None: if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") datamodule = cast("BagOfTilesDataModule", trainer.datamodule) - slides = cast("pd.DataFrame", datamodule.predict.slides) - slides["name"] = slides["path"].apply(lambda x: Path(x).stem) - - _slide = slides[slides["name"] == slide_name] - assert len(_slide) == 1 - slide = _slide.iloc[0] - - kwargs = { - "save_dir": Path(save_dir), - "filename": Path(slide.path).stem, - "extent_x": slide.extent_x, - "extent_y": slide.extent_y, - "mpp_x": slide.mpp_x, - "mpp_y": slide.mpp_y, - "extent_tile": slide.tile_extent_x, - "stride": slide.stride_x, + slides = cast("HFDataset", datamodule.predict.slides) + + self._slide_index = { + Path(path).stem: i for i, path in enumerate(slides["path"]) } - return ScalarMaskBuilder(**kwargs) + self._slides = slides + + def get_mask_builder( + self, + slide_name: str, + trainer: Trainer, + save_dir: str, + ) -> ScalarMaskBuilder: + + if not hasattr(trainer, "datamodule"): + raise ValueError("Trainer should have datamodule attribute") + + slides = self._slides + + slide_idx = self._slide_index[slide_name] + slide = slides[slide_idx] + + return ScalarMaskBuilder( + save_dir=Path(save_dir), + filename=Path(slide["path"]).stem, + extent_x=slide["extent_x"], + extent_y=slide["extent_y"], + mpp_x=slide["mpp_x"], + mpp_y=slide["mpp_y"], + extent_tile=slide["tile_extent_x"], + stride=slide["stride_x"], + ) def on_predict_batch_end( self, @@ -58,22 +68,26 @@ def on_predict_batch_end( batch_idx: int, dataloader_idx: int = 0, ) -> None: + assert isinstance(trainer.logger, MLFlowLogger) + sl_preds, tl_preds, batch_mask, batch_attention = outputs _, metadata_batch = batch - # Log SL predictions trainer.logger.log_table( { - "slide": [metadata["slide_name"] for metadata in metadata_batch], + "slide": [m["slide_name"] for m in metadata_batch], "sl_prediction": sl_preds.tolist(), }, artifact_file="tables/sl_predictions.json", ) - # Log TL predictions and Attention Map for metadata, tl_preds_slide, mask_slide, attention_slide in zip( - metadata_batch, tl_preds, batch_mask, batch_attention, strict=True + metadata_batch, + tl_preds, + batch_mask, + batch_attention, + strict=True, ): for mask_type, data in zip( ["heatmaps", "attention_rescaled"], @@ -81,12 +95,21 @@ def on_predict_batch_end( strict=True, ): mask_builder = self.get_mask_builder( - metadata["slide_name"], trainer, mask_type + metadata["slide_name"], + trainer, + mask_type, ) - data = data[mask_slide.bool()] # take only real tiles (not padding) + + # remove padded tiles + data = data[mask_slide.bool()] + mask_builder.update( - min_max_normalization(data).cpu(), metadata["xs"], metadata["ys"] + min_max_normalization(data).cpu(), + metadata["xs"], + metadata["ys"], ) + mlflow.log_artifact( - str(mask_builder.save()), artifact_path=str(mask_builder.save_dir) + str(mask_builder.save()), + artifact_path=str(mask_builder.save_dir), ) diff --git a/prostate_cancer/callbacks/num_positive_callback.py b/prostate_cancer/callbacks/num_positive_callback.py index 305de94c..542e4f1e 100644 --- a/prostate_cancer/callbacks/num_positive_callback.py +++ b/prostate_cancer/callbacks/num_positive_callback.py @@ -1,12 +1,11 @@ from typing import TYPE_CHECKING, Any, cast import lightning.pytorch as pl -import pandas as pd from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from rationai.mlkit.lightning.loggers import MLFlowLogger from prostate_cancer.datamodule.datasets.base import get_slide_name -from prostate_cancer.typing import UnlabeledTileSampleBatch +from prostate_cancer.typing import TilingSlideMetadata, UnlabeledTileSampleBatch if TYPE_CHECKING: @@ -41,7 +40,7 @@ def on_predict_dataloader_end( raise ValueError("Trainer should have datamodule attribute") datamodule = cast("TileDataModule", trainer.datamodule) - slide = cast("pd.Series", datamodule.predict.slides.iloc[dataloader_idx]) + slide = cast("TilingSlideMetadata", datamodule.predict.slides[dataloader_idx]) table = {"slide": get_slide_name(slide), "num_positive": self.num_positive} assert trainer.logger is not None diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index c8285d23..56df7234 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -8,7 +8,11 @@ from rationai.mlkit.data.datasets import MetaTiledSlides from torch.utils.data import Dataset -from prostate_cancer.typing import LabeledTileSample, UnlabeledTileSample, TilingSlideMetadata +from prostate_cancer.typing import ( + LabeledTileSample, + TilingSlideMetadata, + UnlabeledTileSample, +) T = TypeVar("T", covariant=True) diff --git a/prostate_cancer/datamodule/datasets/embeddings_dataset.py b/prostate_cancer/datamodule/datasets/embeddings_dataset.py index e8302a55..61c95bcd 100644 --- a/prostate_cancer/datamodule/datasets/embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/embeddings_dataset.py @@ -9,7 +9,12 @@ BaseTileDataset, get_slide_name, ) -from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample, TilingSlideMetadata +from prostate_cancer.typing import ( + LabeledTileSample, + TileMetadata, + TilingSlideMetadata, + UnlabeledTileSample, +) T = TypeVar("T", covariant=True) diff --git a/prostate_cancer/datamodule/datasets/tile_dataset.py b/prostate_cancer/datamodule/datasets/tile_dataset.py index 698e0766..c7910a56 100644 --- a/prostate_cancer/datamodule/datasets/tile_dataset.py +++ b/prostate_cancer/datamodule/datasets/tile_dataset.py @@ -11,7 +11,12 @@ BaseSingleSlideDataset, BaseTileDataset, ) -from prostate_cancer.typing import LabeledTileSample, TileMetadata, UnlabeledTileSample, TilingSlideMetadata +from prostate_cancer.typing import ( + LabeledTileSample, + TileMetadata, + TilingSlideMetadata, + UnlabeledTileSample, +) T = TypeVar("T", covariant=True) diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 2db2dbc8..33ec0b05 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -4,15 +4,18 @@ class TilingSlideMetadata(TypedDict): - tile_x: int - tile_y: int + id: bytes path: str - slide_id: bytes - level: int + extent_x: int + extent_y: int tile_extent_x: int tile_extent_y: int + stride_x: int + stride_y: int mpp_x: float mpp_y: float + level: int + downsample: float carcinoma: bool From 1977d60b6e9e908821290fb45450c32ba680f11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 10:45:12 +0000 Subject: [PATCH 105/284] fix: downgrade mlkit --- .../datasets/bag_of_embeddings_dataset.py | 29 +- prostate_cancer/datamodule/datasets/base.py | 40 ++- pyproject.toml | 4 +- uv.lock | 302 +++--------------- 4 files changed, 86 insertions(+), 289 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index c8149c65..9d100e6d 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -5,12 +5,9 @@ from pathlib import Path from typing import Generic, TypeVar -import mlflow -import mlflow.artifacts import torch import torch.nn.functional as F -from datasets import Dataset as HFDataset -from datasets import concatenate_datasets + from torch.utils.data import Dataset from prostate_cancer.typing import ( @@ -19,6 +16,8 @@ UnlabeledBagOfTilesSample, ) +from prostate_cancer.datamodule.datasets.base import download_artifacts + T = TypeVar("T", bound=LabeledBagOfTilesSample | UnlabeledBagOfTilesSample) @@ -33,7 +32,7 @@ def __init__( self.include_labels = carcinoma_roi_t is not None self.carcinoma_roi_t = carcinoma_roi_t - self.slides, self.tiles = self.download_artifacts(uris) + self.slides, self.tiles = download_artifacts(uris) if self.include_labels: self.tiles = self.tiles.map( @@ -54,26 +53,6 @@ def __init__( for i, sid in enumerate(self.tiles["slide_id"]): self.tiles_by_slide.setdefault(sid, []).append(i) - def download_artifacts( - self, tiling_uris: Iterable[str] - ) -> tuple[HFDataset, HFDataset]: - - slide_dsets = [] - tile_dsets = [] - - for tiling_uri in tiling_uris: - root = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - - # Load ALL parquet files in folders - slide_dsets.append(HFDataset.from_parquet(str(root / "slides/*.parquet"))) - - tile_dsets.append(HFDataset.from_parquet(str(root / "tiles/*.parquet"))) - - slides = concatenate_datasets(slide_dsets) - tiles = concatenate_datasets(tile_dsets) - - return slides, tiles - def __len__(self) -> int: return len(self.slides) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 56df7234..5695d6df 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -3,10 +3,10 @@ from pathlib import Path from typing import TypeVar, cast +import mlflow from albumentations.core.composition import TransformType -from datasets import Dataset as HFDataset -from rationai.mlkit.data.datasets import MetaTiledSlides -from torch.utils.data import Dataset +from datasets import Dataset as HFDataset, concatenate_datasets +from torch.utils.data import Dataset, ConcatDataset from prostate_cancer.typing import ( LabeledTileSample, @@ -22,6 +22,25 @@ def get_slide_name(slide_metadata: TilingSlideMetadata) -> str: return Path(slide_metadata.get("path")).stem +def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset]: + + slide_dsets = [] + tile_dsets = [] + + for tiling_uri in tiling_uris: + root = Path(mlflow.artifacts.download_artifacts(tiling_uri)) + + # Load ALL parquet files in folders + slide_dsets.append(HFDataset.from_parquet(str(root / "slides/*.parquet"))) + + tile_dsets.append(HFDataset.from_parquet(str(root / "tiles/*.parquet"))) + + slides = concatenate_datasets(slide_dsets) + tiles = concatenate_datasets(tile_dsets) + + return slides, tiles + + class BaseSingleSlideDataset(Dataset[LabeledTileSample | UnlabeledTileSample], ABC): def __init__( self, @@ -39,7 +58,7 @@ def __init__( ) -class BaseTileDataset(MetaTiledSlides[T]): +class BaseTileDataset(ConcatDataset[T]): """This class abstracts the functionality shared across embedding and image datasets.""" slides: HFDataset @@ -58,7 +77,13 @@ def __init__( self.carcinoma_roi_t = carcinoma_roi_t self.transforms = transforms self.single_slide_ds_cls = single_slide_ds_cls - super().__init__(uris=uris) + + self.slides, self.tiles = download_artifacts(uris) + self.tiles_by_slide: dict[bytes, list[int]] = {} + for i, sid in enumerate(self.tiles["slide_id"]): + self.tiles_by_slide.setdefault(sid, []).append(i) + + super().__init__(self.generate_datasets()) def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" @@ -77,6 +102,11 @@ def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: ) ) + def filter_tiles_by_slide(self, id: bytes) -> HFDataset: + tile_indices = self.tiles_by_slide[id] + slide_tiles = self.tiles.select(tile_indices) + return slide_tiles + def generate_datasets(self) -> Iterable[Dataset[T]]: if self.labeled: diff --git a/pyproject.toml b/pyproject.toml index 23655744..c921d63f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "triton>=3.3.0", "tqdm==4.67.1", "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", - "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git", + "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git@549f5be72a162e7debf3a911ea4649f50080b1d0", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git@68904dfd216ce19454e08a032f7e4dd978a85707", @@ -32,7 +32,7 @@ dependencies = [ "matplotlib==3.10.1", "scikit-learn==1.8.0", "openpyxl==3.1.5", - "mlflow>=3.10.1", + "mlflow<3.0.0", "deprecated>=1.3.1", "pillow==11.3.0", "huggingface_hub>=1.12.2", diff --git a/uv.lock b/uv.lock index dc59a68c..bef98ee5 100644 --- a/uv.lock +++ b/uv.lock @@ -532,31 +532,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/8c/27651e85dab9897c65c4acf7abcd924e85f8726d282804fc7686f1387520/databricks_sdk-0.105.0-py3-none-any.whl", hash = "sha256:f1624e434588e2174052bc3646670f17581f3bb269b9438f9121e9df4a59eebd", size = 866235, upload-time = "2026-04-23T08:53:42.01Z" }, ] -[[package]] -name = "datasets" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dill" }, - { name = "filelock" }, - { name = "fsspec", extra = ["http"] }, - { name = "httpx" }, - { name = "huggingface-hub" }, - { name = "multiprocess" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "pyarrow" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "tqdm" }, - { name = "xxhash" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/85/ce4f780c32f7e36d71257f1c27e8ba898ebe379cb54f211f5f2013f2c219/datasets-5.0.0.tar.gz", hash = "sha256:83dbbbdb07a33b82192b8c419deb18739b138ee2ce1a322d55ce6b100954ec1a", size = 631708, upload-time = "2026-06-05T13:18:26.124Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/66/73034ad30b59f13439b75e620989dacba4c047256e358ba7c2e9ec98ea22/datasets-5.0.0-py3-none-any.whl", hash = "sha256:7dd34927a0fd7046e98aad5cb9430e699c373238a15befa7b9bf22b991a7fee6", size = 555084, upload-time = "2026-06-05T13:18:24.435Z" }, -] - [[package]] name = "debugpy" version = "1.8.21" @@ -595,15 +570,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] -[[package]] -name = "dill" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, -] - [[package]] name = "distlib" version = "0.4.0" @@ -699,19 +665,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, ] -[[package]] -name = "flask-cors" -version = "6.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flask" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/47/03/4e464a50860f9adf08b5c1d3479cb8ea1f12af2aa69535c7042c6e628135/flask_cors-6.0.5.tar.gz", hash = "sha256:30c5031552cd59f620ac0c8211dac45b345d3b2df310e7721879e4f46ef9c601", size = 101386, upload-time = "2026-06-08T20:20:17.765Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/55/5bb1a2d918e9f02f131e47a59032bae70e48050e986e941511fd737a935c/flask_cors-6.0.5-py3-none-any.whl", hash = "sha256:68fcf75693e961f3af26683b23c4b9a8fb6b64de17d20d0c37b95e8de7ab2ed8", size = 16692, upload-time = "2026-06-08T20:20:16.247Z" }, -] - [[package]] name = "fonttools" version = "4.62.1" @@ -1017,15 +970,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] -[[package]] -name = "huey" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/cb/58f229149944602917a976d533d3fe7d54770d6ea18df5931e3f4f313fa0/huey-3.0.3.tar.gz", hash = "sha256:1a17fef95fc8432f75413f1b77439cef5f3493c1ddbfba9151756b31a1b2dad3", size = 263604, upload-time = "2026-06-12T01:53:55.49Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/82/f85d8918949786420716a5e421525ab12aa084bcbe32d86c9743e50bcf3d/huey-3.0.3-py3-none-any.whl", hash = "sha256:d1c687734778b8282c035a943eead8368c1736bb28abc006596fbbc01bdc96dc", size = 94945, upload-time = "2026-06-12T01:53:53.981Z" }, -] - [[package]] name = "huggingface-hub" version = "1.12.2" @@ -1457,6 +1401,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, ] +[[package]] +name = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + [[package]] name = "markdown-it-py" version = "4.0.0" @@ -1570,38 +1523,34 @@ wheels = [ [[package]] name = "mlflow" -version = "3.14.0" +version = "2.22.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp" }, { name = "alembic" }, - { name = "cryptography" }, { name = "docker" }, { name = "flask" }, - { name = "flask-cors" }, { name = "graphene" }, { name = "gunicorn", marker = "sys_platform != 'win32'" }, - { name = "huey" }, + { name = "jinja2" }, + { name = "markdown" }, { name = "matplotlib" }, { name = "mlflow-skinny" }, - { name = "mlflow-tracing" }, { name = "numpy" }, { name = "pandas" }, { name = "pyarrow" }, { name = "scikit-learn" }, { name = "scipy" }, - { name = "skops" }, { name = "sqlalchemy" }, { name = "waitress", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/0b/3404a057daceffe9ce18cd08868648a1e9b817270177bdf8a764576b988b/mlflow-3.14.0.tar.gz", hash = "sha256:5a1f818fa003035c724162096ce3ded7bc7bc47a1cae595df6173961983f4718", size = 11792369, upload-time = "2026-06-17T07:57:44.712Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/fa/c70189287a11b6ae32323957694d88c8811448b784203287ba7e384a87fc/mlflow-2.22.5.tar.gz", hash = "sha256:687c0fee93d25aee1b9537d0a83951daaa1f83bdf60658495e27992304cdcd51", size = 28378119, upload-time = "2026-05-12T08:50:29.355Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/b9/76dcdef7f7f856b36f18cfcd752c2717d9847812a0aaa36d50a7baed569d/mlflow-3.14.0-py3-none-any.whl", hash = "sha256:dbf77f7cdb5b5c0ec59b4671c61730b1b914b4dff7a2892e267a547cb5454f56", size = 12564161, upload-time = "2026-06-17T07:57:42.348Z" }, + { url = "https://files.pythonhosted.org/packages/33/58/0d0f8080d78ba609ba43daa6b96150ec578c7186865e70947e1aeca7f6db/mlflow-2.22.5-py3-none-any.whl", hash = "sha256:5b95b5960e6726d0f9f7115b8593def2b339447d95d8a180caf84a3da121b407", size = 29004244, upload-time = "2026-05-12T08:50:25.36Z" }, ] [[package]] name = "mlflow-skinny" -version = "3.14.0" +version = "2.22.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, @@ -1612,41 +1561,19 @@ dependencies = [ { name = "gitpython" }, { name = "importlib-metadata" }, { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, { name = "packaging" }, { name = "protobuf" }, { name = "pydantic" }, - { name = "python-dotenv" }, { name = "pyyaml" }, { name = "requests" }, { name = "sqlparse" }, - { name = "starlette" }, { name = "typing-extensions" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/4f/a054cd8860590e4e942aee1aab3c94307878159f945fa844acc9ea787721/mlflow_skinny-3.14.0.tar.gz", hash = "sha256:e50f4506422c7737157ae6643c165122af7898345f2e828fa93c4f10128653cf", size = 2901772, upload-time = "2026-06-17T07:57:44.252Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/e7/b80f76ce689b9d6f21cdb84abb2b02148a1149e63a6428dd2c629cefd061/mlflow_skinny-3.14.0-py3-none-any.whl", hash = "sha256:a4880e086365871ef9d78e727a34ea5fb1ce615689579998d48e8c65ee1665a9", size = 3462788, upload-time = "2026-06-17T07:57:42.583Z" }, -] - -[[package]] -name = "mlflow-tracing" -version = "3.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "databricks-sdk" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/34/ff5e72919b4eec8fe65e6fc843978a1a512194e6fafbd1761deca48269ad/mlflow_tracing-3.14.0.tar.gz", hash = "sha256:c2f701e001d35964f23fbbdfdda36c818a76c157b912ae83781199fd714be09a", size = 1429017, upload-time = "2026-06-17T07:58:00.647Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/e8/f04144318835c64d281c3c78a775630950696ad0d445c2c6827d2964f65f/mlflow_skinny-2.22.5.tar.gz", hash = "sha256:7aec51d79ae559c17bedec19005ed1293d9b56785b787a34c6e1fd6f755de0c4", size = 5892685, upload-time = "2026-05-12T08:50:12.26Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/4a/4658a9e514c8f079e40b608661844b9beb21c7530e6ee1e7f830cf81541e/mlflow_tracing-3.14.0-py3-none-any.whl", hash = "sha256:854488dd18068f15e2a56f1cc7b8868c611d09ea39068d0a691a3f07e0048cae", size = 1703863, upload-time = "2026-06-17T07:57:58.687Z" }, + { url = "https://files.pythonhosted.org/packages/c0/39/206466eb32b1530846e5e026512e23c15a6c07a7da2ccae2a6152826d6cd/mlflow_skinny-2.22.5-py3-none-any.whl", hash = "sha256:c0e76ccd93f0ac97b0ed907ea7a404f1f53a260f52bf0b19425fc201c92b0188", size = 6270924, upload-time = "2026-05-12T08:50:09.201Z" }, ] [[package]] @@ -1747,22 +1674,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] -[[package]] -name = "multiprocess" -version = "0.70.19" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dill" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" }, - { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" }, - { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" }, - { url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414, upload-time = "2026-01-19T06:47:35.915Z" }, - { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" }, -] - [[package]] name = "mypy" version = "1.20.2" @@ -2114,18 +2025,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, ] -[[package]] -name = "opentelemetry-proto" -version = "1.42.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/55/63eac3e1089b768ba014091fdd2ae8a9a440c821ef5e2b786909c94c8836/opentelemetry_proto-1.42.1.tar.gz", hash = "sha256:c6a51e6b4f05ae63565f3a113217f3d2bfaec68f78c02d7a6c85f9010d1cfca6", size = 45839, upload-time = "2026-05-21T16:33:03.937Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/9d/171c02c84a76940b7e601805b3bb536985aded9168fbcc9ba52f0a730fa2/opentelemetry_proto-1.42.1-py3-none-any.whl", hash = "sha256:dedb74cba2886c59c7789b227a7a670613025a07489040050aedff6e5c0fb43c", size = 71782, upload-time = "2026-05-21T16:32:44.867Z" }, -] - [[package]] name = "opentelemetry-sdk" version = "1.41.1" @@ -2295,18 +2194,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] -[[package]] -name = "prettytable" -version = "3.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, -] - [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -2421,13 +2308,13 @@ requires-dist = [ { name = "ipykernel", specifier = ">=7.2.0" }, { name = "lightning", specifier = "==2.6.0" }, { name = "matplotlib", specifier = "==3.10.1" }, - { name = "mlflow", specifier = ">=3.10.1" }, + { name = "mlflow", specifier = "<3.0.0" }, { name = "numpy", specifier = ">=2.3.2" }, { name = "openpyxl", specifier = "==3.1.5" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "pillow", specifier = "==11.3.0" }, { name = "rationai-masks", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" }, - { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git" }, + { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git?rev=549f5be72a162e7debf3a911ea4649f50080b1d0" }, { name = "rationai-sdk", git = "https://github.com/RationAI/rationai-sdk-python.git?rev=68904dfd216ce19454e08a032f7e4dd978a85707" }, { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" }, { name = "rationai-tiling", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" }, @@ -2506,31 +2393,30 @@ wheels = [ [[package]] name = "pyarrow" -version = "24.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, - { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, - { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, - { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, - { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, - { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, - { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, - { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, - { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, - { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, - { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, - { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, - { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, +version = "19.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437, upload-time = "2025-02-18T18:55:57.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749, upload-time = "2025-02-18T18:53:00.062Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007, upload-time = "2025-02-18T18:53:06.581Z" }, + { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566, upload-time = "2025-02-18T18:53:11.958Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991, upload-time = "2025-02-18T18:53:17.678Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986, upload-time = "2025-02-18T18:53:26.263Z" }, + { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026, upload-time = "2025-02-18T18:53:33.063Z" }, + { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108, upload-time = "2025-02-18T18:53:38.462Z" }, + { url = "https://files.pythonhosted.org/packages/2b/8d/275c58d4b00781bd36579501a259eacc5c6dfb369be4ddeb672ceb551d2d/pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c", size = 30653552, upload-time = "2025-02-18T18:53:44.357Z" }, + { url = "https://files.pythonhosted.org/packages/a0/9e/e6aca5cc4ef0c7aec5f8db93feb0bde08dbad8c56b9014216205d271101b/pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae", size = 32103413, upload-time = "2025-02-18T18:53:52.971Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fa/a7033f66e5d4f1308c7eb0dfcd2ccd70f881724eb6fd1776657fdf65458f/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4", size = 41134869, upload-time = "2025-02-18T18:53:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/2d/92/34d2569be8e7abdc9d145c98dc410db0071ac579b92ebc30da35f500d630/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2", size = 42192626, upload-time = "2025-02-18T18:54:06.062Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1f/80c617b1084fc833804dc3309aa9d8daacd46f9ec8d736df733f15aebe2c/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6", size = 40496708, upload-time = "2025-02-18T18:54:12.347Z" }, + { url = "https://files.pythonhosted.org/packages/e6/90/83698fcecf939a611c8d9a78e38e7fed7792dcc4317e29e72cf8135526fb/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136", size = 42075728, upload-time = "2025-02-18T18:54:19.364Z" }, + { url = "https://files.pythonhosted.org/packages/40/49/2325f5c9e7a1c125c01ba0c509d400b152c972a47958768e4e35e04d13d8/pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef", size = 25242568, upload-time = "2025-02-18T18:54:25.846Z" }, + { url = "https://files.pythonhosted.org/packages/3f/72/135088d995a759d4d916ec4824cb19e066585b4909ebad4ab196177aa825/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0", size = 30702371, upload-time = "2025-02-18T18:54:30.665Z" }, + { url = "https://files.pythonhosted.org/packages/2e/01/00beeebd33d6bac701f20816a29d2018eba463616bbc07397fdf99ac4ce3/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9", size = 32116046, upload-time = "2025-02-18T18:54:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/1f/c9/23b1ea718dfe967cbd986d16cf2a31fe59d015874258baae16d7ea0ccabc/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3", size = 41091183, upload-time = "2025-02-18T18:54:42.662Z" }, + { url = "https://files.pythonhosted.org/packages/3a/d4/b4a3aa781a2c715520aa8ab4fe2e7fa49d33a1d4e71c8fc6ab7b5de7a3f8/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6", size = 42171896, upload-time = "2025-02-18T18:54:49.808Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/716d4cd5a3cbc387c6e6745d2704c4b46654ba2668260d25c402626c5ddb/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a", size = 40464851, upload-time = "2025-02-18T18:54:57.073Z" }, + { url = "https://files.pythonhosted.org/packages/ed/bd/54907846383dcc7ee28772d7e646f6c34276a17da740002a5cefe90f04f7/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8", size = 42085744, upload-time = "2025-02-18T18:55:08.562Z" }, ] [[package]] @@ -2735,15 +2621,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, ] -[[package]] -name = "python-dotenv" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, -] - [[package]] name = "pytorch-lightning" version = "2.6.1" @@ -2907,18 +2784,15 @@ dependencies = [ [[package]] name = "rationai-mlkit" -version = "0.4.1" -source = { git = "https://github.com/RationAI/mlkit.git#51d1b52cdf4800962cf6f97a1cba1f72d78b8aa4" } +version = "0.3.1" +source = { git = "https://github.com/RationAI/mlkit.git?rev=549f5be72a162e7debf3a911ea4649f50080b1d0#549f5be72a162e7debf3a911ea4649f50080b1d0" } dependencies = [ - { name = "datasets" }, { name = "gitpython" }, { name = "hydra-core" }, { name = "lightning" }, { name = "mlflow" }, { name = "openslide-python" }, - { name = "pyarrow" }, { name = "rationai-masks" }, - { name = "ratiopath" }, { name = "torch" }, ] @@ -3431,22 +3305,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "skops" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "packaging" }, - { name = "prettytable" }, - { name = "scikit-learn" }, - { name = "scipy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c8/9f/46448c4e41a4c5ee4bdb74b3758af48e5ff0faeffe40f4e301bfc7594894/skops-0.14.0.tar.gz", hash = "sha256:6c8c0e047f691a3a582c3258943eecafcbfd79c8c7eef66260f3703e363254f0", size = 608084, upload-time = "2026-04-20T18:23:55.336Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/0e/3ae19fa941522cd98e119762e7181d371c8dba0b2d72bfaf9522692e329c/skops-0.14.0-py3-none-any.whl", hash = "sha256:60a5db78a9db46ccee2139a0ba13ab5afb1c96f4749b382e75a371291bbe3e36", size = 132198, upload-time = "2026-04-20T18:23:54.018Z" }, -] - [[package]] name = "smmap" version = "5.0.3" @@ -3948,76 +3806,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, ] -[[package]] -name = "xxhash" -version = "3.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, - { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, - { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, - { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, - { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, - { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, - { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, - { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, - { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, - { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, - { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, - { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, - { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, - { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, - { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, - { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, - { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, - { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, - { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, - { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, - { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, - { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, - { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, - { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, - { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, - { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, - { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, - { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, - { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, - { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, - { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, - { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, - { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, - { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, - { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, - { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, - { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, - { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, - { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, - { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, - { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, - { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, - { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, - { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, - { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, - { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, - { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, - { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, - { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, - { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, - { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, - { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, - { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, -] - [[package]] name = "yarl" version = "1.23.0" From 7bc7c0806f0d77aa6a77ac27df92bf76532da54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 10:48:34 +0000 Subject: [PATCH 106/284] fix: datasets --- pyproject.toml | 1 + uv.lock | 127 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c921d63f..120b1d90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,7 @@ dependencies = [ "transformers>=5.7.0", "ray[data]>=2.52.1", "ipykernel>=7.2.0", + "datasets==4.0.0", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index bef98ee5..03606e8d 100644 --- a/uv.lock +++ b/uv.lock @@ -532,6 +532,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/8c/27651e85dab9897c65c4acf7abcd924e85f8726d282804fc7686f1387520/databricks_sdk-0.105.0-py3-none-any.whl", hash = "sha256:f1624e434588e2174052bc3646670f17581f3bb269b9438f9121e9df4a59eebd", size = 866235, upload-time = "2026-04-23T08:53:42.01Z" }, ] +[[package]] +name = "datasets" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e3/9d/348ed92110ba5f9b70b51ca1078d4809767a835aa2b7ce7e74ad2b98323d/datasets-4.0.0.tar.gz", hash = "sha256:9657e7140a9050db13443ba21cb5de185af8af944479b00e7ff1e00a61c8dbf1", size = 569566, upload-time = "2025-07-09T14:35:52.431Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/62/eb8157afb21bd229c864521c1ab4fa8e9b4f1b06bafdd8c4668a7a31b5dd/datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d", size = 494825, upload-time = "2025-07-09T14:35:50.658Z" }, +] + [[package]] name = "debugpy" version = "1.8.21" @@ -570,6 +594,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] +[[package]] +name = "dill" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847, upload-time = "2024-01-27T23:42:16.145Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252, upload-time = "2024-01-27T23:42:14.239Z" }, +] + [[package]] name = "distlib" version = "0.4.0" @@ -749,11 +782,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2026.3.0" +version = "2025.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/f4/5721faf47b8c499e776bc34c6a8fc17efdf7fdef0b00f398128bc5dcb4ac/fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972", size = 298491, upload-time = "2025-03-07T21:47:56.461Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" }, + { url = "https://files.pythonhosted.org/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3", size = 193615, upload-time = "2025-03-07T21:47:54.809Z" }, ] [package.optional-dependencies] @@ -1674,6 +1707,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] +[[package]] +name = "multiprocess" +version = "0.70.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603, upload-time = "2024-01-28T18:52:34.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824, upload-time = "2024-01-28T18:52:26.062Z" }, + { url = "https://files.pythonhosted.org/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519, upload-time = "2024-01-28T18:52:28.115Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741, upload-time = "2024-01-28T18:52:29.395Z" }, + { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628, upload-time = "2024-01-28T18:52:30.853Z" }, + { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351, upload-time = "2024-01-28T18:52:31.981Z" }, +] + [[package]] name = "mypy" version = "1.20.2" @@ -2266,6 +2315,7 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "albumentations" }, + { name = "datasets" }, { name = "deprecated" }, { name = "huggingface-hub" }, { name = "ipykernel" }, @@ -2303,6 +2353,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "albumentations", specifier = ">=2.0.8" }, + { name = "datasets" }, { name = "deprecated", specifier = ">=1.3.1" }, { name = "huggingface-hub", specifier = ">=1.12.2" }, { name = "ipykernel", specifier = ">=7.2.0" }, @@ -3806,6 +3857,76 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, ] +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, +] + [[package]] name = "yarl" version = "1.23.0" From 586d0c34b7f22438a1c9cb45ec1b508b09e7aa6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 10:54:51 +0000 Subject: [PATCH 107/284] fix: proper glob --- prostate_cancer/datamodule/datasets/base.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 5695d6df..41f35245 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -23,23 +23,31 @@ def get_slide_name(slide_metadata: TilingSlideMetadata) -> str: def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset]: - slide_dsets = [] tile_dsets = [] for tiling_uri in tiling_uris: root = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - # Load ALL parquet files in folders - slide_dsets.append(HFDataset.from_parquet(str(root / "slides/*.parquet"))) + slide_files = list((root / "slides").glob("*.parquet")) + tile_files = list((root / "tiles").glob("*.parquet")) + + if slide_files: + slide_dsets.append(HFDataset.from_parquet([str(f) for f in slide_files])) + + if tile_files: + tile_dsets.append(HFDataset.from_parquet([str(f) for f in tile_files])) - tile_dsets.append(HFDataset.from_parquet(str(root / "tiles/*.parquet"))) + if not slide_dsets or not tile_dsets: + raise ValueError("No parquet files found in MLflow artifacts") slides = concatenate_datasets(slide_dsets) tiles = concatenate_datasets(tile_dsets) return slides, tiles + return slides, tiles + class BaseSingleSlideDataset(Dataset[LabeledTileSample | UnlabeledTileSample], ABC): def __init__( From d38c8e2c42853076c7c4479c04af3d3a45a8a097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 11:00:56 +0000 Subject: [PATCH 108/284] fix: proper glob --- prostate_cancer/datamodule/datasets/base.py | 33 ++++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 41f35245..9f87d8f5 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -22,31 +22,48 @@ def get_slide_name(slide_metadata: TilingSlideMetadata) -> str: return Path(slide_metadata.get("path")).stem -def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset]: +def download_artifacts(tiling_uris: Iterable[str]) -> Tuple[HFDataset, HFDataset]: slide_dsets = [] tile_dsets = [] for tiling_uri in tiling_uris: root = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - slide_files = list((root / "slides").glob("*.parquet")) - tile_files = list((root / "tiles").glob("*.parquet")) + # ------------------------------------------------------- + # CASE 1: FLAT FILES in root + # ------------------------------------------------------- + flat_slides = root / "slides.parquet" + flat_tiles = root / "tiles.parquet" + + if flat_slides.exists(): + slide_dsets.append(HFDataset.from_parquet(str(flat_slides))) + + if flat_tiles.exists(): + tile_dsets.append(HFDataset.from_parquet(str(flat_tiles))) + + slide_folder = root / "slides" + tile_folder = root / "tiles" + + slide_files = list(slide_folder.glob("*.parquet")) if slide_folder.exists() else [] + tile_files = list(tile_folder.glob("*.parquet")) if tile_folder.exists() else [] if slide_files: - slide_dsets.append(HFDataset.from_parquet([str(f) for f in slide_files])) + slide_dsets.append(HFDataset.from_parquet([str(p) for p in slide_files])) if tile_files: - tile_dsets.append(HFDataset.from_parquet([str(f) for f in tile_files])) + tile_dsets.append(HFDataset.from_parquet([str(p) for p in tile_files])) + + if not slide_dsets: + raise ValueError("No slide parquet files found in MLflow artifacts") - if not slide_dsets or not tile_dsets: - raise ValueError("No parquet files found in MLflow artifacts") + if not tile_dsets: + raise ValueError("No tile parquet files found in MLflow artifacts") slides = concatenate_datasets(slide_dsets) tiles = concatenate_datasets(tile_dsets) return slides, tiles - return slides, tiles class BaseSingleSlideDataset(Dataset[LabeledTileSample | UnlabeledTileSample], ABC): From aa69e1fc508f71fdd61e02192d866ef6bf9a393d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 11:02:42 +0000 Subject: [PATCH 109/284] fix: type --- prostate_cancer/datamodule/datasets/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 9f87d8f5..828b1979 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -22,7 +22,7 @@ def get_slide_name(slide_metadata: TilingSlideMetadata) -> str: return Path(slide_metadata.get("path")).stem -def download_artifacts(tiling_uris: Iterable[str]) -> Tuple[HFDataset, HFDataset]: +def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset]: slide_dsets = [] tile_dsets = [] From d0a0c2297bb06304cfd0e2edd454c298e1ae0c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 11:06:04 +0000 Subject: [PATCH 110/284] fix: rem comment --- prostate_cancer/datamodule/datasets/base.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 828b1979..2e8f4195 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -29,9 +29,6 @@ def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset for tiling_uri in tiling_uris: root = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - # ------------------------------------------------------- - # CASE 1: FLAT FILES in root - # ------------------------------------------------------- flat_slides = root / "slides.parquet" flat_tiles = root / "tiles.parquet" From 68a9ee8cec1bcf1f49ca6cab5f748e2a1a580fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 13:53:07 +0000 Subject: [PATCH 111/284] feat: classic --- .../callbacks/mil_prediction_callback.py | 4 +- prostate_cancer/datamodule/datasets/base.py | 25 +-- pyproject.toml | 6 +- uv.lock | 191 +++++++++++++----- 4 files changed, 155 insertions(+), 71 deletions(-) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 61c27b55..09c04de2 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -20,7 +20,9 @@ def min_max_normalization(tensor: torch.Tensor) -> torch.Tensor: class MILPredictionCallback(Callback): - def setup(self, trainer: Trainer, pl_module: LightningModule, stage: str | None=None) -> None: + def setup( + self, trainer: Trainer, pl_module: LightningModule, stage: str | None = None + ) -> None: if not hasattr(trainer, "datamodule"): raise ValueError("Trainer should have datamodule attribute") diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 2e8f4195..6b5b5ae9 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -6,7 +6,8 @@ import mlflow from albumentations.core.composition import TransformType from datasets import Dataset as HFDataset, concatenate_datasets -from torch.utils.data import Dataset, ConcatDataset +from torch.utils.data import Dataset +from rationai.mlkit.data.datasets import MetaTiledSlides from prostate_cancer.typing import ( LabeledTileSample, @@ -41,7 +42,9 @@ def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset slide_folder = root / "slides" tile_folder = root / "tiles" - slide_files = list(slide_folder.glob("*.parquet")) if slide_folder.exists() else [] + slide_files = ( + list(slide_folder.glob("*.parquet")) if slide_folder.exists() else [] + ) tile_files = list(tile_folder.glob("*.parquet")) if tile_folder.exists() else [] if slide_files: @@ -62,7 +65,6 @@ def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset return slides, tiles - class BaseSingleSlideDataset(Dataset[LabeledTileSample | UnlabeledTileSample], ABC): def __init__( self, @@ -80,12 +82,9 @@ def __init__( ) -class BaseTileDataset(ConcatDataset[T]): +class BaseTileDataset(MetaTiledSlides[T]): """This class abstracts the functionality shared across embedding and image datasets.""" - slides: HFDataset - tiles: HFDataset - def __init__( self, uris: Iterable[str], @@ -100,12 +99,7 @@ def __init__( self.transforms = transforms self.single_slide_ds_cls = single_slide_ds_cls - self.slides, self.tiles = download_artifacts(uris) - self.tiles_by_slide: dict[bytes, list[int]] = {} - for i, sid in enumerate(self.tiles["slide_id"]): - self.tiles_by_slide.setdefault(sid, []).append(i) - - super().__init__(self.generate_datasets()) + super().__init__(uris=uris) def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" @@ -124,11 +118,6 @@ def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: ) ) - def filter_tiles_by_slide(self, id: bytes) -> HFDataset: - tile_indices = self.tiles_by_slide[id] - slide_tiles = self.tiles.select(tile_indices) - return slide_tiles - def generate_datasets(self) -> Iterable[Dataset[T]]: if self.labeled: diff --git a/pyproject.toml b/pyproject.toml index 120b1d90..8298b169 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "triton>=3.3.0", "tqdm==4.67.1", "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", - "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git@549f5be72a162e7debf3a911ea4649f50080b1d0", + "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git@68904dfd216ce19454e08a032f7e4dd978a85707", @@ -32,7 +32,7 @@ dependencies = [ "matplotlib==3.10.1", "scikit-learn==1.8.0", "openpyxl==3.1.5", - "mlflow<3.0.0", + "mlflow>=3.0.0", "deprecated>=1.3.1", "pillow==11.3.0", "huggingface_hub>=1.12.2", @@ -41,7 +41,7 @@ dependencies = [ "transformers>=5.7.0", "ray[data]>=2.52.1", "ipykernel>=7.2.0", - "datasets==4.0.0", + "datasets>=4.6.1", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 03606e8d..1ad187fe 100644 --- a/uv.lock +++ b/uv.lock @@ -534,12 +534,13 @@ wheels = [ [[package]] name = "datasets" -version = "4.0.0" +version = "5.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, { name = "filelock" }, { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, { name = "huggingface-hub" }, { name = "multiprocess" }, { name = "numpy" }, @@ -551,9 +552,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e3/9d/348ed92110ba5f9b70b51ca1078d4809767a835aa2b7ce7e74ad2b98323d/datasets-4.0.0.tar.gz", hash = "sha256:9657e7140a9050db13443ba21cb5de185af8af944479b00e7ff1e00a61c8dbf1", size = 569566, upload-time = "2025-07-09T14:35:52.431Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/85/ce4f780c32f7e36d71257f1c27e8ba898ebe379cb54f211f5f2013f2c219/datasets-5.0.0.tar.gz", hash = "sha256:83dbbbdb07a33b82192b8c419deb18739b138ee2ce1a322d55ce6b100954ec1a", size = 631708, upload-time = "2026-06-05T13:18:26.124Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/62/eb8157afb21bd229c864521c1ab4fa8e9b4f1b06bafdd8c4668a7a31b5dd/datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d", size = 494825, upload-time = "2025-07-09T14:35:50.658Z" }, + { url = "https://files.pythonhosted.org/packages/05/66/73034ad30b59f13439b75e620989dacba4c047256e358ba7c2e9ec98ea22/datasets-5.0.0-py3-none-any.whl", hash = "sha256:7dd34927a0fd7046e98aad5cb9430e699c373238a15befa7b9bf22b991a7fee6", size = 555084, upload-time = "2026-06-05T13:18:24.435Z" }, ] [[package]] @@ -698,6 +699,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, ] +[[package]] +name = "flask-cors" +version = "6.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/03/4e464a50860f9adf08b5c1d3479cb8ea1f12af2aa69535c7042c6e628135/flask_cors-6.0.5.tar.gz", hash = "sha256:30c5031552cd59f620ac0c8211dac45b345d3b2df310e7721879e4f46ef9c601", size = 101386, upload-time = "2026-06-08T20:20:17.765Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/55/5bb1a2d918e9f02f131e47a59032bae70e48050e986e941511fd737a935c/flask_cors-6.0.5-py3-none-any.whl", hash = "sha256:68fcf75693e961f3af26683b23c4b9a8fb6b64de17d20d0c37b95e8de7ab2ed8", size = 16692, upload-time = "2026-06-08T20:20:16.247Z" }, +] + [[package]] name = "fonttools" version = "4.62.1" @@ -1003,6 +1017,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "huey" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/cb/58f229149944602917a976d533d3fe7d54770d6ea18df5931e3f4f313fa0/huey-3.0.3.tar.gz", hash = "sha256:1a17fef95fc8432f75413f1b77439cef5f3493c1ddbfba9151756b31a1b2dad3", size = 263604, upload-time = "2026-06-12T01:53:55.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/82/f85d8918949786420716a5e421525ab12aa084bcbe32d86c9743e50bcf3d/huey-3.0.3-py3-none-any.whl", hash = "sha256:d1c687734778b8282c035a943eead8368c1736bb28abc006596fbbc01bdc96dc", size = 94945, upload-time = "2026-06-12T01:53:53.981Z" }, +] + [[package]] name = "huggingface-hub" version = "1.12.2" @@ -1434,15 +1457,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, ] -[[package]] -name = "markdown" -version = "3.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, -] - [[package]] name = "markdown-it-py" version = "4.0.0" @@ -1556,34 +1570,38 @@ wheels = [ [[package]] name = "mlflow" -version = "2.22.5" +version = "3.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "aiohttp" }, { name = "alembic" }, + { name = "cryptography" }, { name = "docker" }, { name = "flask" }, + { name = "flask-cors" }, { name = "graphene" }, { name = "gunicorn", marker = "sys_platform != 'win32'" }, - { name = "jinja2" }, - { name = "markdown" }, + { name = "huey" }, { name = "matplotlib" }, { name = "mlflow-skinny" }, + { name = "mlflow-tracing" }, { name = "numpy" }, { name = "pandas" }, { name = "pyarrow" }, { name = "scikit-learn" }, { name = "scipy" }, + { name = "skops" }, { name = "sqlalchemy" }, { name = "waitress", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/fa/c70189287a11b6ae32323957694d88c8811448b784203287ba7e384a87fc/mlflow-2.22.5.tar.gz", hash = "sha256:687c0fee93d25aee1b9537d0a83951daaa1f83bdf60658495e27992304cdcd51", size = 28378119, upload-time = "2026-05-12T08:50:29.355Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/0b/3404a057daceffe9ce18cd08868648a1e9b817270177bdf8a764576b988b/mlflow-3.14.0.tar.gz", hash = "sha256:5a1f818fa003035c724162096ce3ded7bc7bc47a1cae595df6173961983f4718", size = 11792369, upload-time = "2026-06-17T07:57:44.712Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/58/0d0f8080d78ba609ba43daa6b96150ec578c7186865e70947e1aeca7f6db/mlflow-2.22.5-py3-none-any.whl", hash = "sha256:5b95b5960e6726d0f9f7115b8593def2b339447d95d8a180caf84a3da121b407", size = 29004244, upload-time = "2026-05-12T08:50:25.36Z" }, + { url = "https://files.pythonhosted.org/packages/de/b9/76dcdef7f7f856b36f18cfcd752c2717d9847812a0aaa36d50a7baed569d/mlflow-3.14.0-py3-none-any.whl", hash = "sha256:dbf77f7cdb5b5c0ec59b4671c61730b1b914b4dff7a2892e267a547cb5454f56", size = 12564161, upload-time = "2026-06-17T07:57:42.348Z" }, ] [[package]] name = "mlflow-skinny" -version = "2.22.5" +version = "3.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, @@ -1594,19 +1612,41 @@ dependencies = [ { name = "gitpython" }, { name = "importlib-metadata" }, { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, { name = "packaging" }, { name = "protobuf" }, { name = "pydantic" }, + { name = "python-dotenv" }, { name = "pyyaml" }, { name = "requests" }, { name = "sqlparse" }, + { name = "starlette" }, { name = "typing-extensions" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/e8/f04144318835c64d281c3c78a775630950696ad0d445c2c6827d2964f65f/mlflow_skinny-2.22.5.tar.gz", hash = "sha256:7aec51d79ae559c17bedec19005ed1293d9b56785b787a34c6e1fd6f755de0c4", size = 5892685, upload-time = "2026-05-12T08:50:12.26Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/4f/a054cd8860590e4e942aee1aab3c94307878159f945fa844acc9ea787721/mlflow_skinny-3.14.0.tar.gz", hash = "sha256:e50f4506422c7737157ae6643c165122af7898345f2e828fa93c4f10128653cf", size = 2901772, upload-time = "2026-06-17T07:57:44.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/e7/b80f76ce689b9d6f21cdb84abb2b02148a1149e63a6428dd2c629cefd061/mlflow_skinny-3.14.0-py3-none-any.whl", hash = "sha256:a4880e086365871ef9d78e727a34ea5fb1ce615689579998d48e8c65ee1665a9", size = 3462788, upload-time = "2026-06-17T07:57:42.583Z" }, +] + +[[package]] +name = "mlflow-tracing" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "databricks-sdk" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/34/ff5e72919b4eec8fe65e6fc843978a1a512194e6fafbd1761deca48269ad/mlflow_tracing-3.14.0.tar.gz", hash = "sha256:c2f701e001d35964f23fbbdfdda36c818a76c157b912ae83781199fd714be09a", size = 1429017, upload-time = "2026-06-17T07:58:00.647Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/39/206466eb32b1530846e5e026512e23c15a6c07a7da2ccae2a6152826d6cd/mlflow_skinny-2.22.5-py3-none-any.whl", hash = "sha256:c0e76ccd93f0ac97b0ed907ea7a404f1f53a260f52bf0b19425fc201c92b0188", size = 6270924, upload-time = "2026-05-12T08:50:09.201Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4a/4658a9e514c8f079e40b608661844b9beb21c7530e6ee1e7f830cf81541e/mlflow_tracing-3.14.0-py3-none-any.whl", hash = "sha256:854488dd18068f15e2a56f1cc7b8868c611d09ea39068d0a691a3f07e0048cae", size = 1703863, upload-time = "2026-06-17T07:57:58.687Z" }, ] [[package]] @@ -2074,6 +2114,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/55/63eac3e1089b768ba014091fdd2ae8a9a440c821ef5e2b786909c94c8836/opentelemetry_proto-1.42.1.tar.gz", hash = "sha256:c6a51e6b4f05ae63565f3a113217f3d2bfaec68f78c02d7a6c85f9010d1cfca6", size = 45839, upload-time = "2026-05-21T16:33:03.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/9d/171c02c84a76940b7e601805b3bb536985aded9168fbcc9ba52f0a730fa2/opentelemetry_proto-1.42.1-py3-none-any.whl", hash = "sha256:dedb74cba2886c59c7789b227a7a670613025a07489040050aedff6e5c0fb43c", size = 71782, upload-time = "2026-05-21T16:32:44.867Z" }, +] + [[package]] name = "opentelemetry-sdk" version = "1.41.1" @@ -2243,6 +2295,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] +[[package]] +name = "prettytable" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -2353,19 +2417,19 @@ dev = [ [package.metadata] requires-dist = [ { name = "albumentations", specifier = ">=2.0.8" }, - { name = "datasets" }, + { name = "datasets", specifier = ">=4.6.1" }, { name = "deprecated", specifier = ">=1.3.1" }, { name = "huggingface-hub", specifier = ">=1.12.2" }, { name = "ipykernel", specifier = ">=7.2.0" }, { name = "lightning", specifier = "==2.6.0" }, { name = "matplotlib", specifier = "==3.10.1" }, - { name = "mlflow", specifier = "<3.0.0" }, + { name = "mlflow", specifier = ">=3.0.0" }, { name = "numpy", specifier = ">=2.3.2" }, { name = "openpyxl", specifier = "==3.1.5" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "pillow", specifier = "==11.3.0" }, { name = "rationai-masks", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" }, - { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git?rev=549f5be72a162e7debf3a911ea4649f50080b1d0" }, + { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git" }, { name = "rationai-sdk", git = "https://github.com/RationAI/rationai-sdk-python.git?rev=68904dfd216ce19454e08a032f7e4dd978a85707" }, { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" }, { name = "rationai-tiling", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" }, @@ -2444,30 +2508,31 @@ wheels = [ [[package]] name = "pyarrow" -version = "19.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437, upload-time = "2025-02-18T18:55:57.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749, upload-time = "2025-02-18T18:53:00.062Z" }, - { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007, upload-time = "2025-02-18T18:53:06.581Z" }, - { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566, upload-time = "2025-02-18T18:53:11.958Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991, upload-time = "2025-02-18T18:53:17.678Z" }, - { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986, upload-time = "2025-02-18T18:53:26.263Z" }, - { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026, upload-time = "2025-02-18T18:53:33.063Z" }, - { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108, upload-time = "2025-02-18T18:53:38.462Z" }, - { url = "https://files.pythonhosted.org/packages/2b/8d/275c58d4b00781bd36579501a259eacc5c6dfb369be4ddeb672ceb551d2d/pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c", size = 30653552, upload-time = "2025-02-18T18:53:44.357Z" }, - { url = "https://files.pythonhosted.org/packages/a0/9e/e6aca5cc4ef0c7aec5f8db93feb0bde08dbad8c56b9014216205d271101b/pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae", size = 32103413, upload-time = "2025-02-18T18:53:52.971Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fa/a7033f66e5d4f1308c7eb0dfcd2ccd70f881724eb6fd1776657fdf65458f/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4", size = 41134869, upload-time = "2025-02-18T18:53:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/2d/92/34d2569be8e7abdc9d145c98dc410db0071ac579b92ebc30da35f500d630/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2", size = 42192626, upload-time = "2025-02-18T18:54:06.062Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1f/80c617b1084fc833804dc3309aa9d8daacd46f9ec8d736df733f15aebe2c/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6", size = 40496708, upload-time = "2025-02-18T18:54:12.347Z" }, - { url = "https://files.pythonhosted.org/packages/e6/90/83698fcecf939a611c8d9a78e38e7fed7792dcc4317e29e72cf8135526fb/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136", size = 42075728, upload-time = "2025-02-18T18:54:19.364Z" }, - { url = "https://files.pythonhosted.org/packages/40/49/2325f5c9e7a1c125c01ba0c509d400b152c972a47958768e4e35e04d13d8/pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef", size = 25242568, upload-time = "2025-02-18T18:54:25.846Z" }, - { url = "https://files.pythonhosted.org/packages/3f/72/135088d995a759d4d916ec4824cb19e066585b4909ebad4ab196177aa825/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0", size = 30702371, upload-time = "2025-02-18T18:54:30.665Z" }, - { url = "https://files.pythonhosted.org/packages/2e/01/00beeebd33d6bac701f20816a29d2018eba463616bbc07397fdf99ac4ce3/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9", size = 32116046, upload-time = "2025-02-18T18:54:35.995Z" }, - { url = "https://files.pythonhosted.org/packages/1f/c9/23b1ea718dfe967cbd986d16cf2a31fe59d015874258baae16d7ea0ccabc/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3", size = 41091183, upload-time = "2025-02-18T18:54:42.662Z" }, - { url = "https://files.pythonhosted.org/packages/3a/d4/b4a3aa781a2c715520aa8ab4fe2e7fa49d33a1d4e71c8fc6ab7b5de7a3f8/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6", size = 42171896, upload-time = "2025-02-18T18:54:49.808Z" }, - { url = "https://files.pythonhosted.org/packages/23/1b/716d4cd5a3cbc387c6e6745d2704c4b46654ba2668260d25c402626c5ddb/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a", size = 40464851, upload-time = "2025-02-18T18:54:57.073Z" }, - { url = "https://files.pythonhosted.org/packages/ed/bd/54907846383dcc7ee28772d7e646f6c34276a17da740002a5cefe90f04f7/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8", size = 42085744, upload-time = "2025-02-18T18:55:08.562Z" }, +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, ] [[package]] @@ -2672,6 +2737,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + [[package]] name = "pytorch-lightning" version = "2.6.1" @@ -2835,15 +2909,18 @@ dependencies = [ [[package]] name = "rationai-mlkit" -version = "0.3.1" -source = { git = "https://github.com/RationAI/mlkit.git?rev=549f5be72a162e7debf3a911ea4649f50080b1d0#549f5be72a162e7debf3a911ea4649f50080b1d0" } +version = "0.4.1" +source = { git = "https://github.com/RationAI/mlkit.git#51d1b52cdf4800962cf6f97a1cba1f72d78b8aa4" } dependencies = [ + { name = "datasets" }, { name = "gitpython" }, { name = "hydra-core" }, { name = "lightning" }, { name = "mlflow" }, { name = "openslide-python" }, + { name = "pyarrow" }, { name = "rationai-masks" }, + { name = "ratiopath" }, { name = "torch" }, ] @@ -3356,6 +3433,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "skops" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "prettytable" }, + { name = "scikit-learn" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/9f/46448c4e41a4c5ee4bdb74b3758af48e5ff0faeffe40f4e301bfc7594894/skops-0.14.0.tar.gz", hash = "sha256:6c8c0e047f691a3a582c3258943eecafcbfd79c8c7eef66260f3703e363254f0", size = 608084, upload-time = "2026-04-20T18:23:55.336Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/0e/3ae19fa941522cd98e119762e7181d371c8dba0b2d72bfaf9522692e329c/skops-0.14.0-py3-none-any.whl", hash = "sha256:60a5db78a9db46ccee2139a0ba13ab5afb1c96f4749b382e75a371291bbe3e36", size = 132198, upload-time = "2026-04-20T18:23:54.018Z" }, +] + [[package]] name = "smmap" version = "5.0.3" From ae70f9a54e49bbdbb5454bd68a1921eb01a88e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 16:18:07 +0000 Subject: [PATCH 112/284] feat: replace log table --- .../callbacks/aggregator_callback.py | 25 +++++++++++++------ .../datasets/bag_of_embeddings_dataset.py | 4 +-- prostate_cancer/datamodule/datasets/base.py | 5 ++-- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index 035ffad8..bb26a6af 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -4,6 +4,7 @@ import lightning.pytorch as pl import mlflow +import pandas as pd import torch from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle from rationai.mlkit.metrics.aggregators import Aggregator @@ -31,6 +32,11 @@ def on_predict_dataloader_start( self.slide = cast( "TilingSlideMetadata", datamodule.predict.slides[dataloader_idx] ) + self.table: dict[str, Any] = { + "slide_name": [], + "prediction": [], + "target": [], + } def on_predict_batch_end( self, @@ -57,15 +63,18 @@ def on_predict_dataloader_end( ) -> None: # Compute the aggregated results pred, _ = self.aggregator.compute() - table: dict[str, Any] = { - "slide_name": Path(self.slide["path"]).stem, - "prediction": pred.item(), - } + self.table["slide_name"].append(Path(self.slide["path"]).stem) + self.table["prediction"].append(pred.item()) if "carcinoma" in self.slide: - table["target"] = self.slide["carcinoma"] + self.table["target"].append(self.slide["carcinoma"]) - mlflow.log_table( - table, - artifact_file="tables/aggregated_predictions.json", + def on_predict_epoch_end( + self, trainer: pl.Trainer, pl_module: pl.LightningModule + ) -> None: + df = pd.DataFrame(self.table) + df.to_json("aggregated_predictions.json", orient="split") + mlflow.log_artifact( + "aggregated_predictions.json", + artifact_path="tables", ) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index 9d100e6d..0c5c7dec 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -7,17 +7,15 @@ import torch import torch.nn.functional as F - from torch.utils.data import Dataset +from prostate_cancer.datamodule.datasets.base import download_artifacts from prostate_cancer.typing import ( LabeledBagOfTilesSample, SlideMetadata, UnlabeledBagOfTilesSample, ) -from prostate_cancer.datamodule.datasets.base import download_artifacts - T = TypeVar("T", bound=LabeledBagOfTilesSample | UnlabeledBagOfTilesSample) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 6b5b5ae9..3b93b7be 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -5,9 +5,10 @@ import mlflow from albumentations.core.composition import TransformType -from datasets import Dataset as HFDataset, concatenate_datasets -from torch.utils.data import Dataset +from datasets import Dataset as HFDataset +from datasets import concatenate_datasets from rationai.mlkit.data.datasets import MetaTiledSlides +from torch.utils.data import Dataset from prostate_cancer.typing import ( LabeledTileSample, From 4a27cbb20c914431c917684430c231ed040aa245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 17 Jun 2026 18:03:25 +0000 Subject: [PATCH 113/284] fix: setup --- .../callbacks/aggregator_callback.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index bb26a6af..52d3f964 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -1,6 +1,6 @@ from copy import deepcopy from pathlib import Path -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, cast import lightning.pytorch as pl import mlflow @@ -21,6 +21,18 @@ def __init__(self, aggregator: Aggregator) -> None: super().__init__() self.aggregator_original = aggregator + def setup( + self, + trainer: pl.Trainer, + pl_module: pl.LightningModule, + stage: str | None = None, + ) -> None: + self.table = { + "slide_name": [], + "prediction": [], + "target": [], + } + def on_predict_dataloader_start( self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int ) -> None: @@ -32,11 +44,6 @@ def on_predict_dataloader_start( self.slide = cast( "TilingSlideMetadata", datamodule.predict.slides[dataloader_idx] ) - self.table: dict[str, Any] = { - "slide_name": [], - "prediction": [], - "target": [], - } def on_predict_batch_end( self, From c19c25e0b6f19fa8c5c77c0b60ecae87500e9a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 06:48:42 +0000 Subject: [PATCH 114/284] feat: replace log table --- postprocessing/slide_level_eval.py | 3 ++ .../callbacks/aggregator_callback.py | 4 +- .../carcinoma_prediction_table_callback.py | 41 ++++++++++++------- .../callbacks/estimation_callback.py | 32 ++++++++++++--- .../callbacks/mil_prediction_callback.py | 27 ++++++++---- .../callbacks/nested_metrics_callback.py | 10 ++--- .../callbacks/num_positive_callback.py | 22 +++++++--- 7 files changed, 98 insertions(+), 41 deletions(-) diff --git a/postprocessing/slide_level_eval.py b/postprocessing/slide_level_eval.py index 2ec18db5..25e14882 100644 --- a/postprocessing/slide_level_eval.py +++ b/postprocessing/slide_level_eval.py @@ -87,6 +87,9 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: mp_path = os.path.join(tmpdir, "mispredictions.csv") store_mispredictions(df, mp_path) logger.log_artifact(mp_path, artifact_path="tables") + sm_path = os.path.join(tmpdir, "slide_metrics.json") + pd.DataFrame(results).to_json(sm_path, orient="split") + logger.log_artifact(sm_path) if __name__ == "__main__": diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index 52d3f964..2e78007d 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -1,6 +1,6 @@ from copy import deepcopy from pathlib import Path -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Any, cast import lightning.pytorch as pl import mlflow @@ -27,7 +27,7 @@ def setup( pl_module: pl.LightningModule, stage: str | None = None, ) -> None: - self.table = { + self.table: dict[str, Any] = { "slide_name": [], "prediction": [], "target": [], diff --git a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py index 272838f9..6ff3c4de 100644 --- a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py +++ b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py @@ -21,6 +21,19 @@ def __init__(self, threshold: float) -> None: super().__init__() self.threshold = threshold + def setup( + self, + trainer: pl.Trainer, + pl_module: pl.LightningModule, + stage: str | None = None, + ) -> None: + self.table: dict[str, Any] = { + "slide": [], + "x": [], + "y": [], + "prediction": [], + } + def on_predict_dataloader_start( self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int ) -> None: @@ -31,7 +44,6 @@ def on_predict_dataloader_start( self.slide = cast( "TilingSlideMetadata", datamodule.predict.slides[dataloader_idx] ) - self.table: list[dict[str, Any]] = [] def on_predict_batch_end( self, @@ -45,18 +57,17 @@ def on_predict_batch_end( _, metadata = batch for i, prediction in enumerate(outputs): - self.table.append( - { - "slide": Path(self.slide["path"]).stem, - "x": metadata["x"][i].item(), - "y": metadata["y"][i].item(), - "prediction": prediction.item(), - "binary_prediction": prediction.item() >= self.threshold, - } - ) - - def on_predict_dataloader_end( - self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int + self.table["slide"].append(Path(self.slide["path"]).stem) + self.table["x"].append(metadata["x"][i].item()) + self.table["y"].append(metadata["y"][i].item()) + self.table["prediction"].append(prediction.item()) + + def on_predict_epoch_end( + self, trainer: pl.Trainer, pl_module: pl.LightningModule ) -> None: - table = pd.DataFrame(self.table) - mlflow.log_table(table, "tables/carcinoma_prediction_table.json") + df = pd.DataFrame(self.table) + df.to_json("carcinoma_prediction_table.json", orient="split") + mlflow.log_artifact( + "carcinoma_prediction_table.json", + artifact_path="tables", + ) diff --git a/prostate_cancer/callbacks/estimation_callback.py b/prostate_cancer/callbacks/estimation_callback.py index ad51daa6..13bfa67d 100644 --- a/prostate_cancer/callbacks/estimation_callback.py +++ b/prostate_cancer/callbacks/estimation_callback.py @@ -4,6 +4,7 @@ import lightning.pytorch as pl import mlflow +import pandas as pd import torch from hydra.utils import get_class from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle @@ -30,6 +31,20 @@ def __init__( self.param_names = list(to_estimate.keys()) self.values_product = list(product(*self.to_estimate.values())) + self.table: dict[str, Any] = { + "slide_name": [], + "target": [], + } + + for values in self.values_product: + keys = [ + f"{self.param_names[i]}={values[i]}" + for i in range(len(self.to_estimate)) + ] + + key_str = "_".join(keys) + self.table[f"pred_{key_str}"] = [] + def on_predict_dataloader_start( self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int ) -> None: @@ -75,7 +90,7 @@ def on_predict_dataloader_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int ) -> None: # Compute the aggregated results for each kernel - table: dict[str, Any] = {"slide_name": Path(self.slide["path"]).stem} + self.table["slide_name"].append(Path(self.slide["path"]).stem) for values, aggregator in zip( self.values_product, self.aggregators, strict=True @@ -86,12 +101,17 @@ def on_predict_dataloader_end( for i in range(len(self.to_estimate)) ] key_str = "_".join(keys) - table[f"pred_{key_str}"] = pred.item() + self.table[f"pred_{key_str}"].append(pred.item()) if "carcinoma" in self.slide: - table["target"] = self.slide["carcinoma"] + self.table["target"].append(self.slide["carcinoma"]) - mlflow.log_table( - table, - artifact_file="tables/aggregated_predictions.json", + def on_predict_epoch_end( + self, trainer: pl.Trainer, pl_module: pl.LightningModule + ) -> None: + df = pd.DataFrame(self.table) + df.to_json("aggregated_predictions.json", orient="split") + mlflow.log_artifact( + "aggregated_predictions.json", + artifact_path="tables", ) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 09c04de2..2a642545 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -1,7 +1,8 @@ from pathlib import Path -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Any, cast import mlflow +import pandas as pd import torch from datasets import Dataset as HFDataset from lightning import Callback, LightningModule, Trainer @@ -35,6 +36,11 @@ def setup( self._slides = slides + self.table: dict[str, Any] = { + "slide": [], + "sl_prediction": [], + } + def get_mask_builder( self, slide_name: str, @@ -76,13 +82,8 @@ def on_predict_batch_end( sl_preds, tl_preds, batch_mask, batch_attention = outputs _, metadata_batch = batch - trainer.logger.log_table( - { - "slide": [m["slide_name"] for m in metadata_batch], - "sl_prediction": sl_preds.tolist(), - }, - artifact_file="tables/sl_predictions.json", - ) + self.table["slide"].extend([m["slide_name"] for m in metadata_batch]) + self.table["sl_prediction"].extend(sl_preds.tolist()) for metadata, tl_preds_slide, mask_slide, attention_slide in zip( metadata_batch, @@ -115,3 +116,13 @@ def on_predict_batch_end( str(mask_builder.save()), artifact_path=str(mask_builder.save_dir), ) + + def on_predict_epoch_end( + self, trainer: Trainer, pl_module: LightningModule + ) -> None: + df = pd.DataFrame(self.table) + df.to_json("sl_predictions.json", orient="split") + mlflow.log_artifact( + "sl_predictions.json", + artifact_path="tables", + ) diff --git a/prostate_cancer/callbacks/nested_metrics_callback.py b/prostate_cancer/callbacks/nested_metrics_callback.py index cddb7c48..c17cdf0b 100644 --- a/prostate_cancer/callbacks/nested_metrics_callback.py +++ b/prostate_cancer/callbacks/nested_metrics_callback.py @@ -1,6 +1,8 @@ from typing import Any import lightning.pytorch as pl +import mlflow +import pandas as pd from lightning import Callback from rationai.mlkit.lightning.loggers import MLFlowLogger from rationai.mlkit.metrics import NestedMetricCollection @@ -53,9 +55,7 @@ def on_test_epoch_end( ) -> None: assert isinstance(trainer.logger, MLFlowLogger) - # Compute slide-level test metrics and log them - trainer.logger.log_table( - self.nested_test_metrics.compute(), "nested_metrics.json" - ) - + metrics = self.nested_test_metrics.compute() + pd.DataFrame(metrics).to_json("nested_metrics.json", orient="split") + mlflow.log_artifact("nested_metrics.json") self.nested_test_metrics.reset() diff --git a/prostate_cancer/callbacks/num_positive_callback.py b/prostate_cancer/callbacks/num_positive_callback.py index 542e4f1e..d4ab5a6c 100644 --- a/prostate_cancer/callbacks/num_positive_callback.py +++ b/prostate_cancer/callbacks/num_positive_callback.py @@ -1,8 +1,9 @@ from typing import TYPE_CHECKING, Any, cast import lightning.pytorch as pl +import mlflow +import pandas as pd from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle -from rationai.mlkit.lightning.loggers import MLFlowLogger from prostate_cancer.datamodule.datasets.base import get_slide_name from prostate_cancer.typing import TilingSlideMetadata, UnlabeledTileSampleBatch @@ -16,6 +17,10 @@ class NumPositiveCallback(MultiloaderLifecycle): def __init__(self, threshold: float) -> None: super().__init__() self.threshold = threshold + self.table: dict[str, Any] = { + "slide": [], + "num_positive": [], + } def on_predict_dataloader_start( self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int @@ -41,8 +46,15 @@ def on_predict_dataloader_end( datamodule = cast("TileDataModule", trainer.datamodule) slide = cast("TilingSlideMetadata", datamodule.predict.slides[dataloader_idx]) - table = {"slide": get_slide_name(slide), "num_positive": self.num_positive} + self.table["slide"].append(get_slide_name(slide)) + self.table["num_positive"].append(self.num_positive) - assert trainer.logger is not None - assert isinstance(trainer.logger, MLFlowLogger) - trainer.logger.log_table(table, artifact_file="num_positive_preds.json") + def on_predict_epoch_end( + self, trainer: pl.Trainer, pl_module: pl.LightningModule + ) -> None: + df = pd.DataFrame(self.table) + df.to_json("num_positive_preds.json", orient="split") + mlflow.log_artifact( + "num_positive_preds.json", + artifact_path="tables", + ) From 211a3092fb5d50b9bd4cb087c55a6d504789983c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 07:05:02 +0000 Subject: [PATCH 115/284] fix: meta --- prostate_cancer/datamodule/datasets/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 3b93b7be..0449054f 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -129,9 +129,15 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: ) } ) + self._meta.tiles = self.tiles + self._meta._slide_id_to_indices = self._meta._build_tile_index(self.tiles) if self.stratified_filter: self.tiles = self.filter_non_carcinoma(self.tiles) + self._meta.tiles = self.tiles + self._meta._slide_id_to_indices = self._meta._build_tile_index( + self.tiles + ) return ( cast( From 6eff997513cb7f48b6dc3bbf1198ff6eea0fd73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 07:50:52 +0000 Subject: [PATCH 116/284] fix: filtering --- prostate_cancer/datamodule/datasets/base.py | 59 +++++++++------------ 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 0449054f..6ec71c0d 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -102,49 +102,42 @@ def __init__( super().__init__(uris=uris) - def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: + def filter_non_carcinoma(self, tiles: HFDataset, carcinoma: bool) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" - slide_carcinoma = dict( - zip( - self.slides["id"], - self.slides["carcinoma"], - strict=True, - ) - ) + # from negative slides, take all tiles + if not carcinoma: + return tiles - return tiles.filter( - lambda row: ( - not (slide_carcinoma[row["slide_id"]] == 1 and row["carcinoma"] == 0) - ) - ) + # from positive slides, take only positive tiles + indices = [i for i, row in enumerate(tiles) if row["carcinoma"] == 1] + + return tiles.select(indices) def generate_datasets(self) -> Iterable[Dataset[T]]: - if self.labeled: - self.tiles = self.tiles.map( - lambda row: { - "carcinoma": ( - row["carcinoma_roi_percentage"] > self.carcinoma_roi_t - ) - } - ) - self._meta.tiles = self.tiles - self._meta._slide_id_to_indices = self._meta._build_tile_index(self.tiles) - - if self.stratified_filter: - self.tiles = self.filter_non_carcinoma(self.tiles) - self._meta.tiles = self.tiles - self._meta._slide_id_to_indices = self._meta._build_tile_index( - self.tiles + for slide in self.slides: + slide_tiles = self.filter_tiles_by_slide(slide["id"]) + + if self.labeled: + slide_tiles = slide_tiles.map( + lambda row: { + "carcinoma": ( + row["carcinoma_roi_percentage"] > self.carcinoma_roi_t + ) + } ) - return ( - cast( + if self.stratified_filter: + slide_tiles = self.filter_non_carcinoma( + slide_tiles, slide["carcinoma"] + ) + + yield cast( "Dataset[T]", self.single_slide_ds_cls( slide, - tiles=self.filter_tiles_by_slide(slide["id"]), + tiles=slide_tiles, include_label=self.labeled, **( {"transforms": self.transforms} @@ -153,5 +146,3 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: ), ), ) - for slide in self.slides - ) From b78209cee2386afd83d9588f537161f5f52bac92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 10:20:48 +0000 Subject: [PATCH 117/284] feat: freeze --- prostate_cancer/datamodule/datasets/base.py | 60 ++++++++++--------- ...tile_stratified_weighted_random_sampler.py | 25 +++++--- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 6ec71c0d..7084e083 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -102,47 +102,49 @@ def __init__( super().__init__(uris=uris) - def filter_non_carcinoma(self, tiles: HFDataset, carcinoma: bool) -> HFDataset: + def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" - # from negative slides, take all tiles - if not carcinoma: - return tiles - - # from positive slides, take only positive tiles - indices = [i for i, row in enumerate(tiles) if row["carcinoma"] == 1] + slide_carcinoma = dict( + zip( + self.slides["id"], + self.slides["carcinoma"], + strict=True, + ) + ) - return tiles.select(indices) + return tiles.filter( + lambda row: ( + not (slide_carcinoma[row["slide_id"]] and (not row["carcinoma"])) + ) + ) def generate_datasets(self) -> Iterable[Dataset[T]]: + tiles = self.tiles - for slide in self.slides: - slide_tiles = self.filter_tiles_by_slide(slide["id"]) + if self.labeled: + tiles = tiles.map( + lambda row: { + "carcinoma": row["carcinoma_roi_percentage"] > self.carcinoma_roi_t + } + ) - if self.labeled: - slide_tiles = slide_tiles.map( - lambda row: { - "carcinoma": ( - row["carcinoma_roi_percentage"] > self.carcinoma_roi_t - ) - } - ) + if self.stratified_filter: + tiles = self.filter_non_carcinoma(tiles) - if self.stratified_filter: - slide_tiles = self.filter_non_carcinoma( - slide_tiles, slide["carcinoma"] - ) + self.tiles = tiles + self._meta.tiles = tiles + self._meta._slide_id_to_indices = self._meta._build_tile_index(tiles) - yield cast( + return ( + cast( "Dataset[T]", self.single_slide_ds_cls( slide, - tiles=slide_tiles, + tiles=self.filter_tiles_by_slide(slide["id"]), include_label=self.labeled, - **( - {"transforms": self.transforms} - if self.transforms is not None - else {} - ), + **({"transforms": self.transforms} if self.transforms else {}), ), ) + for slide in self.slides + ) diff --git a/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py index b6927fb1..acce571f 100644 --- a/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py @@ -1,6 +1,6 @@ from collections.abc import Sequence -import pandas as pd +from datasets import Dataset as HFDataset from torch.utils.data import WeightedRandomSampler from prostate_cancer.datamodule.datasets import LabeledTilesDataset @@ -8,15 +8,26 @@ class TileStratifiedWeightedRandomSampler(WeightedRandomSampler): def __init__( - self, dataset: LabeledTilesDataset, target_col: str, replacement: bool = True + self, + dataset: LabeledTilesDataset, + target_col: str, + replacement: bool = True, ) -> None: + + weights = self._get_weights(dataset.tiles, target_col) + super().__init__( - self._get_weights(dataset.tiles, target_col), + weights, num_samples=len(dataset), replacement=replacement, ) - def _get_weights(self, df: pd.DataFrame, target_col: str) -> Sequence[float]: - value_counts = df[target_col].value_counts() - weights = 1 / df[target_col].map(value_counts) - return weights.tolist() + def _get_weights(self, ds: HFDataset, target_col: str) -> Sequence[float]: + labels = ds[target_col] + + counts = {} + for v in labels: + counts[v] = counts.get(v, 0) + 1 + + weights = [1.0 / counts[v] for v in labels] + return weights From 37e290aeedce6170efd3f9336822b33e47114bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 10:42:23 +0000 Subject: [PATCH 118/284] fix: filter --- prostate_cancer/datamodule/datasets/base.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 7084e083..4ce70423 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -83,6 +83,10 @@ def __init__( ) +def filter_tiles(tiles: HFDataset, slide_id: bytes) -> HFDataset: + return tiles.filter(lambda r: r["slide_id"] == slide_id) + + class BaseTileDataset(MetaTiledSlides[T]): """This class abstracts the functionality shared across embedding and image datasets.""" @@ -132,19 +136,17 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: if self.stratified_filter: tiles = self.filter_non_carcinoma(tiles) + # after this, global tiles are enhanced with carcinoma and possibly filtered (if labeled stratified case) self.tiles = tiles self._meta.tiles = tiles - self._meta._slide_id_to_indices = self._meta._build_tile_index(tiles) - return ( - cast( + for slide in self.slides: + yield cast( "Dataset[T]", self.single_slide_ds_cls( slide, - tiles=self.filter_tiles_by_slide(slide["id"]), + tiles=filter_tiles(self.tiles, slide["id"]), include_label=self.labeled, **({"transforms": self.transforms} if self.transforms else {}), ), ) - for slide in self.slides - ) From 49454680407a528fbf2443a9070626970ad45279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 13:53:37 +0000 Subject: [PATCH 119/284] fix: index --- prostate_cancer/datamodule/datasets/base.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 4ce70423..7db25222 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -106,6 +106,14 @@ def __init__( super().__init__(uris=uris) + def _build_slide_index(self, tiles: HFDataset) -> dict[bytes, list[int]]: + index: dict[bytes, list[int]] = defaultdict(list) + + for i, slide_id in enumerate(tiles["slide_id"]): + index[slide_id].append(i) + + return dict(index) + def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" @@ -139,14 +147,21 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: # after this, global tiles are enhanced with carcinoma and possibly filtered (if labeled stratified case) self.tiles = tiles self._meta.tiles = tiles + slide_index = self._build_slide_index(tiles) for slide in self.slides: + slide_tiles = tiles.select(slide_index.get(slide["id"], [])) + yield cast( "Dataset[T]", self.single_slide_ds_cls( slide, - tiles=filter_tiles(self.tiles, slide["id"]), + tiles=slide_tiles, include_label=self.labeled, - **({"transforms": self.transforms} if self.transforms else {}), + **( + {"transforms": self.transforms} + if self.transforms is not None + else {} + ), ), ) From 82275366ceb23a318b7cc3774abe80f75c23f4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 14:50:09 +0000 Subject: [PATCH 120/284] fix: missing import --- prostate_cancer/datamodule/datasets/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 7db25222..8eedf97e 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -2,6 +2,7 @@ from collections.abc import Iterable from pathlib import Path from typing import TypeVar, cast +from collections import defaultdict import mlflow from albumentations.core.composition import TransformType From 4a0f6ba17ce1866263ea4997498dd1226b6ca9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 17:16:01 +0000 Subject: [PATCH 121/284] fix: sampler bag --- prostate_cancer/datamodule/datasets/base.py | 6 +++--- ...of_tiles_stratified_weighted_random_sampler.py | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 8eedf97e..7c52fc8f 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -1,8 +1,8 @@ from abc import ABC +from collections import defaultdict from collections.abc import Iterable from pathlib import Path from typing import TypeVar, cast -from collections import defaultdict import mlflow from albumentations.core.composition import TransformType @@ -114,7 +114,7 @@ def _build_slide_index(self, tiles: HFDataset) -> dict[bytes, list[int]]: index[slide_id].append(i) return dict(index) - + def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" @@ -152,7 +152,7 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: for slide in self.slides: slide_tiles = tiles.select(slide_index.get(slide["id"], [])) - + yield cast( "Dataset[T]", self.single_slide_ds_cls( diff --git a/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py index e363e6a5..d4993995 100644 --- a/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py @@ -1,6 +1,6 @@ from collections.abc import Sequence -import pandas as pd +from datasets import Dataset as HFDataset from torch.utils.data import WeightedRandomSampler from prostate_cancer.datamodule.datasets import LabeledBagOfEmbeddingsDataset @@ -19,7 +19,12 @@ def __init__( replacement=replacement, ) - def _get_weights(self, df: pd.DataFrame, target_col: str) -> Sequence[float]: - value_counts = df[target_col].value_counts() - weights = 1 / df[target_col].map(value_counts) - return weights.tolist() + def _get_weights(self, ds: HFDataset, target_col: str) -> Sequence[float]: + labels = ds[target_col] + + counts = {} + for v in labels: + counts[v] = counts.get(v, 0) + 1 + + weights = [1.0 / counts[v] for v in labels] + return weights From 21d7ebf358d32fa0aed9762bb939a9dd9c862114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 17:52:26 +0000 Subject: [PATCH 122/284] fix: mypy --- .../bag_of_tiles_stratified_weighted_random_sampler.py | 2 +- .../samplers/tile_stratified_weighted_random_sampler.py | 2 +- pyproject.toml | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py index d4993995..78f1eb2f 100644 --- a/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py @@ -22,7 +22,7 @@ def __init__( def _get_weights(self, ds: HFDataset, target_col: str) -> Sequence[float]: labels = ds[target_col] - counts = {} + counts: dict[bool, int] = {} for v in labels: counts[v] = counts.get(v, 0) + 1 diff --git a/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py index acce571f..344b78bb 100644 --- a/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/tile_stratified_weighted_random_sampler.py @@ -25,7 +25,7 @@ def __init__( def _get_weights(self, ds: HFDataset, target_col: str) -> Sequence[float]: labels = ds[target_col] - counts = {} + counts: dict[bool, int] = {} for v in labels: counts[v] = counts.get(v, 0) + 1 diff --git a/pyproject.toml b/pyproject.toml index 8298b169..549c8190 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,4 +45,8 @@ dependencies = [ ] [dependency-groups] -dev = ["mypy", "ruff", "pre-commit"] +dev = [ + "mypy", + "ruff", + "pre-commit", +] From a7f4177bc7a969fe29c3a13c5a365c07b8aa1e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 18 Jun 2026 18:17:07 +0000 Subject: [PATCH 123/284] fix: lint --- postprocessing/slide_level_eval.py | 3 +-- preprocessing/stats.py | 2 +- prostate_cancer/callbacks/aggregator_callback.py | 3 +-- prostate_cancer/callbacks/estimation_callback.py | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/postprocessing/slide_level_eval.py b/postprocessing/slide_level_eval.py index 25e14882..7c897b29 100644 --- a/postprocessing/slide_level_eval.py +++ b/postprocessing/slide_level_eval.py @@ -77,7 +77,6 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: df = read_json_table(config.preds_uri) df["pred_binary"] = df[config.pred_column] >= config.t results, cm = evaluate(df) - logger.log_table(results, "slide_metrics.json") logger.log_metrics(results) with tempfile.TemporaryDirectory() as tmpdir: @@ -88,7 +87,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: store_mispredictions(df, mp_path) logger.log_artifact(mp_path, artifact_path="tables") sm_path = os.path.join(tmpdir, "slide_metrics.json") - pd.DataFrame(results).to_json(sm_path, orient="split") + pd.DataFrame([results]).to_json(sm_path, orient="split") logger.log_artifact(sm_path) diff --git a/preprocessing/stats.py b/preprocessing/stats.py index 8d8fb72b..21566daf 100644 --- a/preprocessing/stats.py +++ b/preprocessing/stats.py @@ -4,12 +4,12 @@ import ray import torch from omegaconf import DictConfig -from prostate_cancer.datamodule.datasets.tile_dataset import SlideTiles from rationai.masks import process_items from rationai.mlkit import autolog, with_cli_args from rationai.mlkit.lightning.loggers import MLFlowLogger from prostate_cancer.datamodule.datasets import UnlabeledTilesDataset +from prostate_cancer.datamodule.datasets.tile_dataset import SlideTiles @ray.remote diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index 2e78007d..977f8328 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -73,8 +73,7 @@ def on_predict_dataloader_end( self.table["slide_name"].append(Path(self.slide["path"]).stem) self.table["prediction"].append(pred.item()) - if "carcinoma" in self.slide: - self.table["target"].append(self.slide["carcinoma"]) + self.table["target"].append(self.slide.get("carcinoma", None)) def on_predict_epoch_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule diff --git a/prostate_cancer/callbacks/estimation_callback.py b/prostate_cancer/callbacks/estimation_callback.py index 13bfa67d..e06c3d98 100644 --- a/prostate_cancer/callbacks/estimation_callback.py +++ b/prostate_cancer/callbacks/estimation_callback.py @@ -103,8 +103,7 @@ def on_predict_dataloader_end( key_str = "_".join(keys) self.table[f"pred_{key_str}"].append(pred.item()) - if "carcinoma" in self.slide: - self.table["target"].append(self.slide["carcinoma"]) + self.table["target"].append(self.slide.get("carcinoma", None)) def on_predict_epoch_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule From 3fd03f77ffc6dbdc58749b59f7accf7820986833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 20 Jun 2026 15:45:53 +0000 Subject: [PATCH 124/284] fix: patch mlflow --- prostate_cancer/__main__.py | 3 +++ prostate_cancer/_mlflow_compat.py | 37 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 prostate_cancer/_mlflow_compat.py diff --git a/prostate_cancer/__main__.py b/prostate_cancer/__main__.py index ba8ecec2..fb06feaf 100644 --- a/prostate_cancer/__main__.py +++ b/prostate_cancer/__main__.py @@ -8,6 +8,8 @@ from rationai.mlkit import Trainer, autolog from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger +from prostate_cancer._mlflow_compat import apply_mlflow_compat_patch + OmegaConf.register_new_resolver( "random_seed", lambda: randint(0, 2**31), use_cache=True @@ -21,6 +23,7 @@ @hydra.main(config_path="../configs", config_name="ml", version_base=None) @autolog def main(config: DictConfig, logger: MLFlowLogger) -> None: + apply_mlflow_compat_patch() seed_everything(config.seed, workers=True) torch.set_float32_matmul_precision(precision="medium") diff --git a/prostate_cancer/_mlflow_compat.py b/prostate_cancer/_mlflow_compat.py new file mode 100644 index 00000000..343d45e6 --- /dev/null +++ b/prostate_cancer/_mlflow_compat.py @@ -0,0 +1,37 @@ +"""Compatibility shim: 3.x MLflow client against a <3.0 tracking server. + +A 3.x client's RunsArtifactRepository.list_artifacts() also lists "logged +models" via /api/2.0/mlflow/logged-models/search. A 2.x server doesn't expose +that route and returns 404, which crashes checkpoint logging. The classic +run-artifact checkpoint upload still works against the old server, so we make +the logged-model lookup a no-op when it fails. + +Remove this once client and server are on matching major versions. +""" + +import logging +from typing import Any + +from mlflow.exceptions import MlflowException +from mlflow.store.artifact.runs_artifact_repo import RunsArtifactRepository + + +_log = logging.getLogger(__name__) + + +def apply_mlflow_compat_patch() -> None: + if getattr(RunsArtifactRepository, "_list_model_artifacts_patched", False): + return + + _original = RunsArtifactRepository._list_model_artifacts + + def _safe_list_model_artifacts(self: Any, *args: Any, **kwargs: Any) -> Any: + try: + return _original(self, *args, **kwargs) + except MlflowException as exc: + _log.debug("Skipping logged-model listing (MLflow server < 3.0): %s", exc) + return [] + + RunsArtifactRepository._list_model_artifacts = _safe_list_model_artifacts # type: ignore[method-assign] + RunsArtifactRepository._list_model_artifacts_patched = True # type: ignore[attr-defined] + From 9266145d74bfa9df7dcfa7d1fdcfecf37a4ce925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 20 Jun 2026 15:48:33 +0000 Subject: [PATCH 125/284] fix: lint --- prostate_cancer/_mlflow_compat.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/prostate_cancer/_mlflow_compat.py b/prostate_cancer/_mlflow_compat.py index 343d45e6..1c53eb4b 100644 --- a/prostate_cancer/_mlflow_compat.py +++ b/prostate_cancer/_mlflow_compat.py @@ -32,6 +32,5 @@ def _safe_list_model_artifacts(self: Any, *args: Any, **kwargs: Any) -> Any: _log.debug("Skipping logged-model listing (MLflow server < 3.0): %s", exc) return [] - RunsArtifactRepository._list_model_artifacts = _safe_list_model_artifacts # type: ignore[method-assign] - RunsArtifactRepository._list_model_artifacts_patched = True # type: ignore[attr-defined] - + RunsArtifactRepository._list_model_artifacts = _safe_list_model_artifacts # type: ignore[method-assign] + RunsArtifactRepository._list_model_artifacts_patched = True # type: ignore[attr-defined] From 9cbd268daca7a7a71e01fc92cc51a834902013f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 20 Jun 2026 17:00:36 +0000 Subject: [PATCH 126/284] fix: downgrade --- pyproject.toml | 2 +- uv.lock | 144 ++++++++++++++++++++++++------------------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 549c8190..0c78e67e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ dependencies = [ "matplotlib==3.10.1", "scikit-learn==1.8.0", "openpyxl==3.1.5", - "mlflow>=3.0.0", + "mlflow==3.12.0", "deprecated>=1.3.1", "pillow==11.3.0", "huggingface_hub>=1.12.2", diff --git a/uv.lock b/uv.lock index 1ad187fe..672ff55d 100644 --- a/uv.lock +++ b/uv.lock @@ -407,41 +407,41 @@ wheels = [ [[package]] name = "cryptography" -version = "47.0.0" +version = "46.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214, upload-time = "2026-04-24T19:53:03.864Z" }, - { url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617, upload-time = "2026-04-24T19:53:06.909Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186, upload-time = "2026-04-24T19:53:09.053Z" }, - { url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244, upload-time = "2026-04-24T19:53:11.217Z" }, - { url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906, upload-time = "2026-04-24T19:53:13.532Z" }, - { url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842, upload-time = "2026-04-24T19:53:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313, upload-time = "2026-04-24T19:53:17.755Z" }, - { url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964, upload-time = "2026-04-24T19:53:20.062Z" }, - { url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817, upload-time = "2026-04-24T19:53:22.498Z" }, - { url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544, upload-time = "2026-04-24T19:53:24.356Z" }, - { url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536, upload-time = "2026-04-24T19:53:26.665Z" }, - { url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106, upload-time = "2026-04-24T19:53:28.686Z" }, - { url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581, upload-time = "2026-04-24T19:53:31.058Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309, upload-time = "2026-04-24T19:53:33.054Z" }, - { url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072, upload-time = "2026-04-24T19:54:06.411Z" }, - { url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767, upload-time = "2026-04-24T19:54:08.519Z" }, - { url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350, upload-time = "2026-04-24T19:54:10.795Z" }, - { url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394, upload-time = "2026-04-24T19:54:13.275Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777, upload-time = "2026-04-24T19:54:15.18Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771, upload-time = "2026-04-24T19:54:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753, upload-time = "2026-04-24T19:54:19.963Z" }, - { url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911, upload-time = "2026-04-24T19:54:21.818Z" }, - { url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411, upload-time = "2026-04-24T19:54:24.376Z" }, - { url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262, upload-time = "2026-04-24T19:54:26.946Z" }, - { url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506, upload-time = "2026-04-24T19:54:28.926Z" }, - { url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060, upload-time = "2026-04-24T19:54:30.869Z" }, - { url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487, upload-time = "2026-04-24T19:54:33.494Z" }, - { url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737, upload-time = "2026-04-24T19:54:35.408Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, + { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, + { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, + { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, ] [[package]] @@ -1019,11 +1019,11 @@ wheels = [ [[package]] name = "huey" -version = "3.0.3" +version = "2.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/cb/58f229149944602917a976d533d3fe7d54770d6ea18df5931e3f4f313fa0/huey-3.0.3.tar.gz", hash = "sha256:1a17fef95fc8432f75413f1b77439cef5f3493c1ddbfba9151756b31a1b2dad3", size = 263604, upload-time = "2026-06-12T01:53:55.49Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/29/3428d52eb8e85025e264a291641a9f9d6407cc1e51d1b630f6ac5815999a/huey-2.6.0.tar.gz", hash = "sha256:8d11f8688999d65266af1425b831f6e3773e99415027177b8734b0ffd5e251f6", size = 221068, upload-time = "2026-01-06T03:01:02.055Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/82/f85d8918949786420716a5e421525ab12aa084bcbe32d86c9743e50bcf3d/huey-3.0.3-py3-none-any.whl", hash = "sha256:d1c687734778b8282c035a943eead8368c1736bb28abc006596fbbc01bdc96dc", size = 94945, upload-time = "2026-06-12T01:53:53.981Z" }, + { url = "https://files.pythonhosted.org/packages/1a/34/fae9ac8f1c3a552fd3f7ff652b94c78d219dedc5fce0c0a4232457760a00/huey-2.6.0-py3-none-any.whl", hash = "sha256:1b9df9d370b49c6d5721ba8a01ac9a787cf86b3bdc584e4679de27b920395c3f", size = 76951, upload-time = "2026-01-06T03:01:00.808Z" }, ] [[package]] @@ -1570,7 +1570,7 @@ wheels = [ [[package]] name = "mlflow" -version = "3.14.0" +version = "3.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1594,14 +1594,14 @@ dependencies = [ { name = "sqlalchemy" }, { name = "waitress", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/0b/3404a057daceffe9ce18cd08868648a1e9b817270177bdf8a764576b988b/mlflow-3.14.0.tar.gz", hash = "sha256:5a1f818fa003035c724162096ce3ded7bc7bc47a1cae595df6173961983f4718", size = 11792369, upload-time = "2026-06-17T07:57:44.712Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/e3/b2148a6d6f38731d3dda49a7e46cf6932a458aa0aa5414b80e6e7251fa1d/mlflow-3.12.0.tar.gz", hash = "sha256:227ee31c6abf7ae3b3c38d4ca87c356e107578740c1efee89da43f2a5b9e3b47", size = 9939137, upload-time = "2026-05-05T10:28:58.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/b9/76dcdef7f7f856b36f18cfcd752c2717d9847812a0aaa36d50a7baed569d/mlflow-3.14.0-py3-none-any.whl", hash = "sha256:dbf77f7cdb5b5c0ec59b4671c61730b1b914b4dff7a2892e267a547cb5454f56", size = 12564161, upload-time = "2026-06-17T07:57:42.348Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f8/47f28975c1c1b70d351fa19c5aef21cef5ae1e1aca36bd1858798384bdbb/mlflow-3.12.0-py3-none-any.whl", hash = "sha256:e1c28ed4c48557cc52c766f17f1ca5826753ddf241d43f30f99c45f7ea6b3ce0", size = 10625639, upload-time = "2026-05-05T10:28:55.777Z" }, ] [[package]] name = "mlflow-skinny" -version = "3.14.0" +version = "3.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, @@ -1625,14 +1625,14 @@ dependencies = [ { name = "typing-extensions" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/4f/a054cd8860590e4e942aee1aab3c94307878159f945fa844acc9ea787721/mlflow_skinny-3.14.0.tar.gz", hash = "sha256:e50f4506422c7737157ae6643c165122af7898345f2e828fa93c4f10128653cf", size = 2901772, upload-time = "2026-06-17T07:57:44.252Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/c0/9cbe24b4abcbadb3a3cdab65bfd552b6b75de64374b477abac89190d25d0/mlflow_skinny-3.12.0.tar.gz", hash = "sha256:74d27066bc9553d281e0c31d25f07deb39dbe99d190e4f7c257703e5c8ee6d10", size = 2723866, upload-time = "2026-05-05T10:28:46.388Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/e7/b80f76ce689b9d6f21cdb84abb2b02148a1149e63a6428dd2c629cefd061/mlflow_skinny-3.14.0-py3-none-any.whl", hash = "sha256:a4880e086365871ef9d78e727a34ea5fb1ce615689579998d48e8c65ee1665a9", size = 3462788, upload-time = "2026-06-17T07:57:42.583Z" }, + { url = "https://files.pythonhosted.org/packages/95/05/2df60fab37881c490e9364ea697a6c3a78d3b593fde2d9332a75f8cdf1f8/mlflow_skinny-3.12.0-py3-none-any.whl", hash = "sha256:0498f3697abcabcc6204c432ef179840f6a7a34ce123837c98c1913064fda6dd", size = 3261903, upload-time = "2026-05-05T10:28:44.24Z" }, ] [[package]] name = "mlflow-tracing" -version = "3.14.0" +version = "3.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, @@ -1644,9 +1644,9 @@ dependencies = [ { name = "protobuf" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/34/ff5e72919b4eec8fe65e6fc843978a1a512194e6fafbd1761deca48269ad/mlflow_tracing-3.14.0.tar.gz", hash = "sha256:c2f701e001d35964f23fbbdfdda36c818a76c157b912ae83781199fd714be09a", size = 1429017, upload-time = "2026-06-17T07:58:00.647Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/13/d32fe4cca53dde68f09fd38c545ea709e8565fd7c2ffd7c5eff99e504aaf/mlflow_tracing-3.12.0.tar.gz", hash = "sha256:8702a34a1d4f1517ba904d716f5a8fca4675e6526f7d164d02bdaabececa2d80", size = 1352412, upload-time = "2026-05-05T10:28:51.115Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/4a/4658a9e514c8f079e40b608661844b9beb21c7530e6ee1e7f830cf81541e/mlflow_tracing-3.14.0-py3-none-any.whl", hash = "sha256:854488dd18068f15e2a56f1cc7b8868c611d09ea39068d0a691a3f07e0048cae", size = 1703863, upload-time = "2026-06-17T07:57:58.687Z" }, + { url = "https://files.pythonhosted.org/packages/a5/bf/e22b778addbe19a7a912400c37a197ee9cdebc1641e3b0a3882c30da6ee4/mlflow_tracing-3.12.0-py3-none-any.whl", hash = "sha256:c6072553f47b42505dc7ee62946688a4a0dde8f06b78fbc60e946397b20e1518", size = 1618720, upload-time = "2026-05-05T10:28:48.999Z" }, ] [[package]] @@ -2423,7 +2423,7 @@ requires-dist = [ { name = "ipykernel", specifier = ">=7.2.0" }, { name = "lightning", specifier = "==2.6.0" }, { name = "matplotlib", specifier = "==3.10.1" }, - { name = "mlflow", specifier = ">=3.0.0" }, + { name = "mlflow", specifier = "==3.12.0" }, { name = "numpy", specifier = ">=2.3.2" }, { name = "openpyxl", specifier = "==3.1.5" }, { name = "pandas", specifier = ">=2.2.3" }, @@ -2508,31 +2508,31 @@ wheels = [ [[package]] name = "pyarrow" -version = "24.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, - { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, - { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, - { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, - { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, - { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, - { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, - { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, - { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, - { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, - { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, - { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, - { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, +version = "23.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/4b/4166bb5abbfe6f750fc60ad337c43ecf61340fa52ab386da6e8dbf9e63c4/pyarrow-23.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f", size = 34214575, upload-time = "2026-02-16T10:09:56.225Z" }, + { url = "https://files.pythonhosted.org/packages/e1/da/3f941e3734ac8088ea588b53e860baeddac8323ea40ce22e3d0baa865cc9/pyarrow-23.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7", size = 35832540, upload-time = "2026-02-16T10:10:03.428Z" }, + { url = "https://files.pythonhosted.org/packages/88/7c/3d841c366620e906d54430817531b877ba646310296df42ef697308c2705/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9", size = 44470940, upload-time = "2026-02-16T10:10:10.704Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a5/da83046273d990f256cb79796a190bbf7ec999269705ddc609403f8c6b06/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05", size = 47586063, upload-time = "2026-02-16T10:10:17.95Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/b7d2ebcff47a514f47f9da1e74b7949138c58cfeb108cdd4ee62f43f0cf3/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67", size = 48173045, upload-time = "2026-02-16T10:10:25.363Z" }, + { url = "https://files.pythonhosted.org/packages/43/b2/b40961262213beaba6acfc88698eb773dfce32ecdf34d19291db94c2bd73/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730", size = 50621741, upload-time = "2026-02-16T10:10:33.477Z" }, + { url = "https://files.pythonhosted.org/packages/f6/70/1fdda42d65b28b078e93d75d371b2185a61da89dda4def8ba6ba41ebdeb4/pyarrow-23.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0", size = 27620678, upload-time = "2026-02-16T10:10:39.31Z" }, + { url = "https://files.pythonhosted.org/packages/47/10/2cbe4c6f0fb83d2de37249567373d64327a5e4d8db72f486db42875b08f6/pyarrow-23.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8", size = 34210066, upload-time = "2026-02-16T10:10:45.487Z" }, + { url = "https://files.pythonhosted.org/packages/cb/4f/679fa7e84dadbaca7a65f7cdba8d6c83febbd93ca12fa4adf40ba3b6362b/pyarrow-23.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f", size = 35825526, upload-time = "2026-02-16T10:10:52.266Z" }, + { url = "https://files.pythonhosted.org/packages/f9/63/d2747d930882c9d661e9398eefc54f15696547b8983aaaf11d4a2e8b5426/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677", size = 44473279, upload-time = "2026-02-16T10:11:01.557Z" }, + { url = "https://files.pythonhosted.org/packages/b3/93/10a48b5e238de6d562a411af6467e71e7aedbc9b87f8d3a35f1560ae30fb/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2", size = 47585798, upload-time = "2026-02-16T10:11:09.401Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/476943001c54ef078dbf9542280e22741219a184a0632862bca4feccd666/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37", size = 48179446, upload-time = "2026-02-16T10:11:17.781Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b6/5dd0c47b335fcd8edba9bfab78ad961bd0fd55ebe53468cc393f45e0be60/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2", size = 50623972, upload-time = "2026-02-16T10:11:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/d5/09/a532297c9591a727d67760e2e756b83905dd89adb365a7f6e9c72578bcc1/pyarrow-23.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a", size = 27540749, upload-time = "2026-02-16T10:12:23.297Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8e/38749c4b1303e6ae76b3c80618f84861ae0c55dd3c2273842ea6f8258233/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1", size = 34471544, upload-time = "2026-02-16T10:11:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/a3/73/f237b2bc8c669212f842bcfd842b04fc8d936bfc9d471630569132dc920d/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500", size = 35949911, upload-time = "2026-02-16T10:11:39.813Z" }, + { url = "https://files.pythonhosted.org/packages/0c/86/b912195eee0903b5611bf596833def7d146ab2d301afeb4b722c57ffc966/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41", size = 44520337, upload-time = "2026-02-16T10:11:47.764Z" }, + { url = "https://files.pythonhosted.org/packages/69/c2/f2a717fb824f62d0be952ea724b4f6f9372a17eed6f704b5c9526f12f2f1/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07", size = 47548944, upload-time = "2026-02-16T10:11:56.607Z" }, + { url = "https://files.pythonhosted.org/packages/84/a7/90007d476b9f0dc308e3bc57b832d004f848fd6c0da601375d20d92d1519/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83", size = 48236269, upload-time = "2026-02-16T10:12:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3f/b16fab3e77709856eb6ac328ce35f57a6d4a18462c7ca5186ef31b45e0e0/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125", size = 50604794, upload-time = "2026-02-16T10:12:11.797Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a1/22df0620a9fac31d68397a75465c344e83c3dfe521f7612aea33e27ab6c0/pyarrow-23.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8", size = 27660642, upload-time = "2026-02-16T10:12:17.746Z" }, ] [[package]] @@ -3516,15 +3516,15 @@ wheels = [ [[package]] name = "starlette" -version = "1.0.0" +version = "0.52.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, + { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, ] [[package]] From 0e775bc8fdae309ea9d48925fe905ef8fa771f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 21 Jun 2026 07:43:40 +0000 Subject: [PATCH 127/284] feat: resolve comments --- .../datamodule/datasets/bag_of_embeddings_dataset.py | 2 +- prostate_cancer/datamodule/datasets/base.py | 8 ++++---- prostate_cancer/typing.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index 0c5c7dec..a7c337eb 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -46,7 +46,7 @@ def __init__( self.max_embeddings = max(Counter(slide_ids).values()) - self.tiles_by_slide: dict[bytes, list[int]] = {} + self.tiles_by_slide: dict[str, list[int]] = {} for i, sid in enumerate(self.tiles["slide_id"]): self.tiles_by_slide.setdefault(sid, []).append(i) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 7c52fc8f..fb5ab9ca 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -22,7 +22,7 @@ def get_slide_name(slide_metadata: TilingSlideMetadata) -> str: - return Path(slide_metadata.get("path")).stem + return Path(slide_metadata["path"]).stem def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset]: @@ -84,7 +84,7 @@ def __init__( ) -def filter_tiles(tiles: HFDataset, slide_id: bytes) -> HFDataset: +def filter_tiles(tiles: HFDataset, slide_id: str) -> HFDataset: return tiles.filter(lambda r: r["slide_id"] == slide_id) @@ -107,8 +107,8 @@ def __init__( super().__init__(uris=uris) - def _build_slide_index(self, tiles: HFDataset) -> dict[bytes, list[int]]: - index: dict[bytes, list[int]] = defaultdict(list) + def _build_slide_index(self, tiles: HFDataset) -> dict[str, list[int]]: + index: dict[str, list[int]] = defaultdict(list) for i, slide_id in enumerate(tiles["slide_id"]): index[slide_id].append(i) diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index 33ec0b05..deadd360 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -4,7 +4,7 @@ class TilingSlideMetadata(TypedDict): - id: bytes + id: str path: str extent_x: int extent_y: int From 4367cc6c2cd05b734bcc21cad5293d16d09562de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 21 Jun 2026 15:44:44 +0000 Subject: [PATCH 128/284] feat: try builtin --- prostate_cancer/datamodule/datasets/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index fb5ab9ca..683b0492 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -148,10 +148,10 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: # after this, global tiles are enhanced with carcinoma and possibly filtered (if labeled stratified case) self.tiles = tiles self._meta.tiles = tiles - slide_index = self._build_slide_index(tiles) + self._meta._slide_id_to_indices = self._meta._build_tile_index(tiles) for slide in self.slides: - slide_tiles = tiles.select(slide_index.get(slide["id"], [])) + slide_tiles = self._meta.filter_tiles_by_slide(slide) yield cast( "Dataset[T]", @@ -165,4 +165,4 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: else {} ), ), - ) + ) \ No newline at end of file From beef126326f1c6dca297782ffcc77a4bdf1cc581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 21 Jun 2026 15:59:40 +0000 Subject: [PATCH 129/284] fix: lint and indexing --- prostate_cancer/datamodule/datasets/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 683b0492..ea9ac7b4 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -151,7 +151,7 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: self._meta._slide_id_to_indices = self._meta._build_tile_index(tiles) for slide in self.slides: - slide_tiles = self._meta.filter_tiles_by_slide(slide) + slide_tiles = self._meta.filter_tiles_by_slide(slide["id"]) yield cast( "Dataset[T]", @@ -165,4 +165,4 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: else {} ), ), - ) \ No newline at end of file + ) From 9b507c6491ded2c1222aa4a1682fbf53f24a8989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 21 Jun 2026 18:15:04 +0000 Subject: [PATCH 130/284] feat: try gene --- prostate_cancer/datamodule/datasets/base.py | 29 +++++---------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index ea9ac7b4..9e28e76c 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -1,5 +1,4 @@ from abc import ABC -from collections import defaultdict from collections.abc import Iterable from pathlib import Path from typing import TypeVar, cast @@ -84,10 +83,6 @@ def __init__( ) -def filter_tiles(tiles: HFDataset, slide_id: str) -> HFDataset: - return tiles.filter(lambda r: r["slide_id"] == slide_id) - - class BaseTileDataset(MetaTiledSlides[T]): """This class abstracts the functionality shared across embedding and image datasets.""" @@ -107,14 +102,6 @@ def __init__( super().__init__(uris=uris) - def _build_slide_index(self, tiles: HFDataset) -> dict[str, list[int]]: - index: dict[str, list[int]] = defaultdict(list) - - for i, slide_id in enumerate(tiles["slide_id"]): - index[slide_id].append(i) - - return dict(index) - def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: assert self.labeled, "Only allowed for labeled dataset" @@ -150,19 +137,15 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: self._meta.tiles = tiles self._meta._slide_id_to_indices = self._meta._build_tile_index(tiles) - for slide in self.slides: - slide_tiles = self._meta.filter_tiles_by_slide(slide["id"]) - - yield cast( + return ( + cast( "Dataset[T]", self.single_slide_ds_cls( slide, - tiles=slide_tiles, + tiles=self.filter_tiles_by_slide(slide["id"]), include_label=self.labeled, - **( - {"transforms": self.transforms} - if self.transforms is not None - else {} - ), + **({"transforms": self.transforms} if self.transforms else {}), ), ) + for slide in self.slides + ) From 6a71bd6a4aca0395b26fdd35a5510b676af6aaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 22 Jun 2026 05:50:41 +0000 Subject: [PATCH 131/284] feat: invoke check --- configs/experiment/train/base.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/experiment/train/base.yaml b/configs/experiment/train/base.yaml index f2735610..0669961c 100644 --- a/configs/experiment/train/base.yaml +++ b/configs/experiment/train/base.yaml @@ -3,6 +3,7 @@ defaults: - /callbacks/train@callbacks - _self_ + thresholds: ${train_data.thresholds} mean: ${train_data.mean} From 8183dc83d240fbbda39e474973ad022fcb6db791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 22 Jun 2026 07:32:02 +0000 Subject: [PATCH 132/284] fix: meta --- prostate_cancer/datamodule/datasets/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 9e28e76c..0c0744c4 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -142,7 +142,7 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: "Dataset[T]", self.single_slide_ds_cls( slide, - tiles=self.filter_tiles_by_slide(slide["id"]), + tiles=self._meta.filter_tiles_by_slide(slide["id"]), include_label=self.labeled, **({"transforms": self.transforms} if self.transforms else {}), ), From f1e51867703e22db4dd59fa8a66c32fc1fefc7f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 22 Jun 2026 07:43:54 +0000 Subject: [PATCH 133/284] fix: missing bs --- configs/experiment/train/embeddings/base.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configs/experiment/train/embeddings/base.yaml b/configs/experiment/train/embeddings/base.yaml index 1d7f33ce..fe31eb0c 100644 --- a/configs/experiment/train/embeddings/base.yaml +++ b/configs/experiment/train/embeddings/base.yaml @@ -4,3 +4,7 @@ defaults: - /experiment/train/base - /datamodule/embeddings_datamodule@datamodule - _self_ + +datamodule: + batch_size: 256 + num_workers: 8 From a10b5e78b240147833668f08a7059ce7db734010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 22 Jun 2026 07:49:00 +0000 Subject: [PATCH 134/284] feat: erase thresholds from main config --- configs/default.yaml | 2 -- configs/experiment/predict/base.yaml | 1 - configs/experiment/test/base.yaml | 1 - configs/experiment/train/base.yaml | 2 -- configs/postprocessing.yaml | 2 -- configs/preprocessing.yaml | 2 -- 6 files changed, 10 deletions(-) diff --git a/configs/default.yaml b/configs/default.yaml index 8b5746b1..2cfb07a7 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -6,8 +6,6 @@ defaults: prostate_cancer_artifacts: /mnt/projects/prostate_cancer/cancer -thresholds: ??? - metadata: experiment_name: Prostate run_name: ??? diff --git a/configs/experiment/predict/base.yaml b/configs/experiment/predict/base.yaml index a1f8f5f2..8468f597 100644 --- a/configs/experiment/predict/base.yaml +++ b/configs/experiment/predict/base.yaml @@ -7,7 +7,6 @@ model: checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} tile_threshold: ${model_data.tile_threshold} -thresholds: ${predict_data.thresholds} mean: ${model_data.stats.mean} std: ${model_data.stats.std} data_name: ${predict_data.data_name} diff --git a/configs/experiment/test/base.yaml b/configs/experiment/test/base.yaml index ef4faa55..5e0eaaaf 100644 --- a/configs/experiment/test/base.yaml +++ b/configs/experiment/test/base.yaml @@ -9,7 +9,6 @@ model: tile_threshold: ${model_data.tile_threshold} checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} -thresholds: ${test_data.thresholds} mean: ${model_data.stats.mean} std: ${model_data.stats.std} data_name: ${test_data.data_name} diff --git a/configs/experiment/train/base.yaml b/configs/experiment/train/base.yaml index 0669961c..20d85391 100644 --- a/configs/experiment/train/base.yaml +++ b/configs/experiment/train/base.yaml @@ -4,8 +4,6 @@ defaults: - _self_ -thresholds: ${train_data.thresholds} - mean: ${train_data.mean} std: ${train_data.std} stains: ${train_data.stains} diff --git a/configs/postprocessing.yaml b/configs/postprocessing.yaml index 8e264eb9..26fd04a2 100644 --- a/configs/postprocessing.yaml +++ b/configs/postprocessing.yaml @@ -9,8 +9,6 @@ use_case: ??? model_data: ??? preds_uri: ??? # each script requires prediction table -thresholds: null # not used in these scripts - metadata: run_name: ${use_case} ${model_data.model_name} ${data.data_name} description: ${use_case} ${model_data.model_name} ${data.data_name} diff --git a/configs/preprocessing.yaml b/configs/preprocessing.yaml index 0d50a875..66cb1dee 100644 --- a/configs/preprocessing.yaml +++ b/configs/preprocessing.yaml @@ -4,8 +4,6 @@ defaults: - /default - _self_ -thresholds: ${data.thresholds} - data: ??? use_case: ??? From 467cc18d4422d5188d3b027f22ac4a73ddb21545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 22 Jun 2026 11:52:49 +0000 Subject: [PATCH 135/284] feat: my filter --- prostate_cancer/datamodule/datasets/base.py | 27 +++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 0c0744c4..6196f664 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -1,4 +1,5 @@ from abc import ABC +from collections import defaultdict from collections.abc import Iterable from pathlib import Path from typing import TypeVar, cast @@ -119,6 +120,14 @@ def filter_non_carcinoma(self, tiles: HFDataset) -> HFDataset: ) ) + def _build_slide_index(self, tiles: HFDataset) -> dict[bytes, list[int]]: + index: dict[bytes, list[int]] = defaultdict(list) + + for i, slide_id in enumerate(tiles["slide_id"]): + index[slide_id].append(i) + + return dict(index) + def generate_datasets(self) -> Iterable[Dataset[T]]: tiles = self.tiles @@ -135,17 +144,21 @@ def generate_datasets(self) -> Iterable[Dataset[T]]: # after this, global tiles are enhanced with carcinoma and possibly filtered (if labeled stratified case) self.tiles = tiles self._meta.tiles = tiles - self._meta._slide_id_to_indices = self._meta._build_tile_index(tiles) + slide_index = self._build_slide_index(tiles) - return ( - cast( + for slide in self.slides: + slide_tiles = tiles.select(slide_index.get(slide["id"], [])) + + yield cast( "Dataset[T]", self.single_slide_ds_cls( slide, - tiles=self._meta.filter_tiles_by_slide(slide["id"]), + tiles=slide_tiles, include_label=self.labeled, - **({"transforms": self.transforms} if self.transforms else {}), + **( + {"transforms": self.transforms} + if self.transforms is not None + else {} + ), ), ) - for slide in self.slides - ) From f17cd75974708cc3fef846bab51568209af0adac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 22 Jun 2026 12:01:12 +0000 Subject: [PATCH 136/284] fix: carcinoma t --- .../datasets/embeddings/bag_of_embeddings_labeled.yaml | 2 +- configs/datamodule/datasets/embeddings/embeddings_labeled.yaml | 2 +- configs/datamodule/datasets/images/labeled.yaml | 2 +- configs/experiment/predict/base.yaml | 1 + configs/experiment/test/base.yaml | 1 + configs/experiment/train/base.yaml | 2 +- 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml b/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml index 5267b269..b66012d8 100644 --- a/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml +++ b/configs/datamodule/datasets/embeddings/bag_of_embeddings_labeled.yaml @@ -1,3 +1,3 @@ _target_: prostate_cancer.datamodule.datasets.LabeledBagOfEmbeddingsDataset uris: ??? -carcinoma_roi_t: ${thresholds.carcinoma_roi_t} +carcinoma_roi_t: ${carcinoma_roi_t} diff --git a/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml b/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml index 952b0bbc..80e6898f 100644 --- a/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml +++ b/configs/datamodule/datasets/embeddings/embeddings_labeled.yaml @@ -1,4 +1,4 @@ _target_: prostate_cancer.datamodule.datasets.LabeledEmbeddingsDataset uris: ??? stratified_filter: ??? -carcinoma_roi_t: ${thresholds.carcinoma_roi_t} +carcinoma_roi_t: ${carcinoma_roi_t} diff --git a/configs/datamodule/datasets/images/labeled.yaml b/configs/datamodule/datasets/images/labeled.yaml index 3e90939d..f3a5b9a9 100644 --- a/configs/datamodule/datasets/images/labeled.yaml +++ b/configs/datamodule/datasets/images/labeled.yaml @@ -1,4 +1,4 @@ _target_: prostate_cancer.datamodule.datasets.LabeledTilesDataset uris: ??? -carcinoma_roi_t: ${thresholds.carcinoma_roi_t} +carcinoma_roi_t: ${carcinoma_roi_t} stratified_filter: ??? diff --git a/configs/experiment/predict/base.yaml b/configs/experiment/predict/base.yaml index 8468f597..80dc7c4e 100644 --- a/configs/experiment/predict/base.yaml +++ b/configs/experiment/predict/base.yaml @@ -6,6 +6,7 @@ model: checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} +carcinoma_roi_t: ${predict_data.thresholds.carcinoma_roi_t} tile_threshold: ${model_data.tile_threshold} mean: ${model_data.stats.mean} std: ${model_data.stats.std} diff --git a/configs/experiment/test/base.yaml b/configs/experiment/test/base.yaml index 5e0eaaaf..dc6a491f 100644 --- a/configs/experiment/test/base.yaml +++ b/configs/experiment/test/base.yaml @@ -7,6 +7,7 @@ model: tl_threshold: ${tile_threshold} tile_threshold: ${model_data.tile_threshold} +carcinoma_roi_t: ${test_data.thresholds.carcinoma_roi_t} checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} mean: ${model_data.stats.mean} diff --git a/configs/experiment/train/base.yaml b/configs/experiment/train/base.yaml index 20d85391..718c6512 100644 --- a/configs/experiment/train/base.yaml +++ b/configs/experiment/train/base.yaml @@ -3,7 +3,7 @@ defaults: - /callbacks/train@callbacks - _self_ - +carcinoma_roi_t: ${train_data.thresholds.carcinoma_roi_t} mean: ${train_data.mean} std: ${train_data.std} stains: ${train_data.stains} From 18ed506a32ec2b237430746fc2d4a8cd10dd5d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 08:06:16 +0000 Subject: [PATCH 137/284] feat: stricter typing --- .../tile_embeddings/pgp/base.yaml | 2 +- .../tile_embeddings/virchow2/base.yaml | 2 +- configs/preprocessing/tile_embeddings.yaml | 9 -------- preprocessing/embeddings/tile_embeddings.py | 2 +- prostate_cancer/cnn_model.py | 3 ++- prostate_cancer/embedding_model.py | 7 ++++-- .../modeling/backbone/foundation_base.py | 9 ++++++++ prostate_cancer/modeling/backbone/pgp.py | 16 ++++++++++++++ .../modeling/backbone/virchow2.py | 22 +------------------ 9 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 prostate_cancer/modeling/backbone/foundation_base.py create mode 100644 prostate_cancer/modeling/backbone/pgp.py rename preprocessing/embeddings/encoders.py => prostate_cancer/modeling/backbone/virchow2.py (56%) diff --git a/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml b/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml index 6fd7981d..9a5d2b4b 100644 --- a/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml +++ b/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml @@ -1,5 +1,5 @@ # @package _global_ tile_encoder: - _target_: preprocessing.embeddings.encoders.ProvGigaPath + _target_: prostate_cancer.modeling.backbone.pgp.ProvGigaPath name: PGP diff --git a/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml b/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml index 28334043..2b62de2a 100644 --- a/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml +++ b/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml @@ -1,5 +1,5 @@ # @package _global_ tile_encoder: - _target_: preprocessing.embeddings.encoders.Virchow2 + _target_: prostate_cancer.modeling.backbone.virchow2.Virchow2 name: Virchow2 diff --git a/configs/preprocessing/tile_embeddings.yaml b/configs/preprocessing/tile_embeddings.yaml index 0a8a3951..d6154027 100644 --- a/configs/preprocessing/tile_embeddings.yaml +++ b/configs/preprocessing/tile_embeddings.yaml @@ -3,15 +3,6 @@ batch_size: 1024 output_path: ${prostate_cancer_artifacts}/tile_embeddings/${tile_encoder.name}/${data.data_name} -# create embeddings for all the tiles -thresholds: - tissue_roi_t: 0.0 - residual_t: 1.0 - blur_t: 1.0 - folding_t: 1.0 - exclude_t: 1.0 - another_pathology_t: 1.0 - tile_encoder: _target_: ??? name: ??? diff --git a/preprocessing/embeddings/tile_embeddings.py b/preprocessing/embeddings/tile_embeddings.py index 19e0c502..1ba54a2f 100644 --- a/preprocessing/embeddings/tile_embeddings.py +++ b/preprocessing/embeddings/tile_embeddings.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: - from preprocessing.embeddings.encoders import FoundationModel + from prostate_cancer.modeling.backbone.foundation_base import FoundationModel def save_embeddings( diff --git a/prostate_cancer/cnn_model.py b/prostate_cancer/cnn_model.py index 392ed79e..79f1ed57 100644 --- a/prostate_cancer/cnn_model.py +++ b/prostate_cancer/cnn_model.py @@ -1,13 +1,14 @@ from torch import Tensor, nn from prostate_cancer.base_model import ProstateCancerModel +from prostate_cancer.modeling.decode_head import BinaryCNNClassifier class CNNProstateModel(ProstateCancerModel): def __init__( self, backbone: nn.Module, - decode_head: nn.Module, + decode_head: BinaryCNNClassifier, lr: float, tl_threshold: float, ) -> None: diff --git a/prostate_cancer/embedding_model.py b/prostate_cancer/embedding_model.py index fb80adde..0ed0b0b6 100644 --- a/prostate_cancer/embedding_model.py +++ b/prostate_cancer/embedding_model.py @@ -1,10 +1,13 @@ -from torch import Tensor, nn +from torch import Tensor from prostate_cancer.base_model import ProstateCancerModel +from prostate_cancer.modeling.decode_head import BinaryEmbeddingClassifier class EmbeddingProstateModel(ProstateCancerModel): - def __init__(self, decode_head: nn.Module, lr: float, tl_threshold: float) -> None: + def __init__( + self, decode_head: BinaryEmbeddingClassifier, lr: float, tl_threshold: float + ) -> None: super().__init__(lr=lr, tl_threshold=tl_threshold) self.decode_head = decode_head diff --git a/prostate_cancer/modeling/backbone/foundation_base.py b/prostate_cancer/modeling/backbone/foundation_base.py new file mode 100644 index 00000000..3ff9722a --- /dev/null +++ b/prostate_cancer/modeling/backbone/foundation_base.py @@ -0,0 +1,9 @@ +import torch + + +class FoundationModel(torch.nn.Module): + def __init__(self, name: str, embed_dim: int) -> None: + """Wrapper for a foundation model - forward and dimension differ depending on the model.""" + super().__init__() + self.name = name + self.embed_dim = embed_dim diff --git a/prostate_cancer/modeling/backbone/pgp.py b/prostate_cancer/modeling/backbone/pgp.py new file mode 100644 index 00000000..51d1f2b8 --- /dev/null +++ b/prostate_cancer/modeling/backbone/pgp.py @@ -0,0 +1,16 @@ +import timm +import torch + +from prostate_cancer.modeling.backbone.foundation_base import FoundationModel + + +class ProvGigaPath(FoundationModel): + def __init__(self, name: str) -> None: + super().__init__(name, 1536) + # For this, you need to setup HF_TOKEN= env.variable. + self.module = timm.create_model( + "hf_hub:prov-gigapath/prov-gigapath", pretrained=True + ).eval() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return self.module(x) diff --git a/preprocessing/embeddings/encoders.py b/prostate_cancer/modeling/backbone/virchow2.py similarity index 56% rename from preprocessing/embeddings/encoders.py rename to prostate_cancer/modeling/backbone/virchow2.py index ee03335e..707b36bb 100644 --- a/preprocessing/embeddings/encoders.py +++ b/prostate_cancer/modeling/backbone/virchow2.py @@ -1,28 +1,8 @@ import timm import torch - -# taken from HuggingFace of Virchow2 from timm.layers import SwiGLUPacked # type: ignore[attr-defined] - -class FoundationModel(torch.nn.Module): - def __init__(self, name: str, embed_dim: int) -> None: - """Wrapper for a foundation model - forward and dimension differ depending on the model.""" - super().__init__() - self.name = name - self.embed_dim = embed_dim - - -class ProvGigaPath(FoundationModel): - def __init__(self, name: str) -> None: - super().__init__(name, 1536) - # For this, you need to setup HF_TOKEN= env.variable. - self.module = timm.create_model( - "hf_hub:prov-gigapath/prov-gigapath", pretrained=True - ).eval() - - def forward(self, x: torch.Tensor) -> torch.Tensor: - return self.module(x) +from prostate_cancer.modeling.backbone.foundation_base import FoundationModel class Virchow2(FoundationModel): From a1ba10e88197df9a956742793ad96cb459a80aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 08:35:17 +0000 Subject: [PATCH 138/284] feat: pgp augmented training --- .../train/images/pgp/base_stain.yaml | 21 +++++++++++++ .../train/images/pgp/mmci_tl_stain.yaml | 7 +++++ configs/model/backbone/pgp.yaml | 1 + configs/model/backbone/virchow2.yaml | 1 + configs/model/pgp_full_model.yaml | 12 ++++++++ configs/model/virchow2_full_model.yaml | 12 ++++++++ prostate_cancer/foundation_model.py | 30 +++++++++++++++++++ prostate_cancer/modeling/backbone/__init__.py | 4 ++- .../modeling/backbone/foundation_base.py | 8 ++++- prostate_cancer/modeling/backbone/pgp.py | 4 ++- prostate_cancer/modeling/backbone/virchow2.py | 3 +- 11 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 configs/experiment/train/images/pgp/base_stain.yaml create mode 100644 configs/experiment/train/images/pgp/mmci_tl_stain.yaml create mode 100644 configs/model/backbone/pgp.yaml create mode 100644 configs/model/backbone/virchow2.yaml create mode 100644 configs/model/pgp_full_model.yaml create mode 100644 configs/model/virchow2_full_model.yaml create mode 100644 prostate_cancer/foundation_model.py diff --git a/configs/experiment/train/images/pgp/base_stain.yaml b/configs/experiment/train/images/pgp/base_stain.yaml new file mode 100644 index 00000000..895f7bfe --- /dev/null +++ b/configs/experiment/train/images/pgp/base_stain.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /datamodule/transforms/normalize_w_stain_augments@transforms + - /model/pgp_full_model@model + - _self_ + +model_name: "PGP (Stain)" + +# ImageNet stats +mean: [123.675, 116.280, 103.530] +std: [58.395, 57.120, 57.375] + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/pgp/mmci_tl_stain.yaml b/configs/experiment/train/images/pgp/mmci_tl_stain.yaml new file mode 100644 index 00000000..23b85019 --- /dev/null +++ b/configs/experiment/train/images/pgp/mmci_tl_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/pgp/base_stain + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ diff --git a/configs/model/backbone/pgp.yaml b/configs/model/backbone/pgp.yaml new file mode 100644 index 00000000..5d69380c --- /dev/null +++ b/configs/model/backbone/pgp.yaml @@ -0,0 +1 @@ +_target_: prostate_cancer.modeling.backbone.ProvGigaPath \ No newline at end of file diff --git a/configs/model/backbone/virchow2.yaml b/configs/model/backbone/virchow2.yaml new file mode 100644 index 00000000..1b61c5e1 --- /dev/null +++ b/configs/model/backbone/virchow2.yaml @@ -0,0 +1 @@ +_target_: prostate_cancer.modeling.backbone.Virchow2 \ No newline at end of file diff --git a/configs/model/pgp_full_model.yaml b/configs/model/pgp_full_model.yaml new file mode 100644 index 00000000..4defdc66 --- /dev/null +++ b/configs/model/pgp_full_model.yaml @@ -0,0 +1,12 @@ +defaults: + - decode_head: embedding_classifier + - backbone: ProvGigaPath + - _self_ + +decode_head: + in_features: 1536 + +_target_: prostate_cancer.cnn_model.FoundationProstateModel +lr: ??? +tl_threshold: ??? +freeze_backbone: True diff --git a/configs/model/virchow2_full_model.yaml b/configs/model/virchow2_full_model.yaml new file mode 100644 index 00000000..09fe52e2 --- /dev/null +++ b/configs/model/virchow2_full_model.yaml @@ -0,0 +1,12 @@ +defaults: + - decode_head: embedding_classifier + - backbone: Virchow2 + - _self_ + +decode_head: + in_features: 2560 + +_target_: prostate_cancer.cnn_model.FoundationProstateModel +lr: ??? +tl_threshold: ??? +freeze_backbone: True diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py new file mode 100644 index 00000000..86113352 --- /dev/null +++ b/prostate_cancer/foundation_model.py @@ -0,0 +1,30 @@ +from torch import Tensor + +from prostate_cancer.base_model import ProstateCancerModel +from prostate_cancer.modeling.backbone.foundation_base import FoundationModel +from prostate_cancer.modeling.decode_head import BinaryCNNClassifier + + +class FoundationProstateModel(ProstateCancerModel): + def __init__( + self, + backbone: FoundationModel, + decode_head: BinaryCNNClassifier, + lr: float, + tl_threshold: float, + freeze_backbone: bool, + ) -> None: + super().__init__(lr=lr, tl_threshold=tl_threshold) + self.backbone = backbone + self.decode_head = decode_head + + # stay consistent with embedding training + if freeze_backbone: + for p in self.backbone.module.parameters(): + p.requires_grad = False + self.backbone.module.eval() + + def forward(self, x: Tensor) -> Tensor: + features = self.backbone(x) + logits = self.decode_head(features) + return logits diff --git a/prostate_cancer/modeling/backbone/__init__.py b/prostate_cancer/modeling/backbone/__init__.py index 1525619d..643a34d0 100644 --- a/prostate_cancer/modeling/backbone/__init__.py +++ b/prostate_cancer/modeling/backbone/__init__.py @@ -1,6 +1,8 @@ from prostate_cancer.modeling.backbone.google_vit import google_vit from prostate_cancer.modeling.backbone.resnet50 import resnet50 from prostate_cancer.modeling.backbone.vgg16 import vgg16 +from prostate_cancer.modeling.backbone.virchow2 import Virchow2 +from prostate_cancer.modeling.backbone.pgp import ProvGigaPath -__all__ = ["google_vit", "resnet50", "vgg16"] +__all__ = ["google_vit", "resnet50", "vgg16", "Virchow2", "ProvGigaPath"] diff --git a/prostate_cancer/modeling/backbone/foundation_base.py b/prostate_cancer/modeling/backbone/foundation_base.py index 3ff9722a..1a006170 100644 --- a/prostate_cancer/modeling/backbone/foundation_base.py +++ b/prostate_cancer/modeling/backbone/foundation_base.py @@ -1,9 +1,15 @@ +from abc import ABC, abstractmethod + import torch -class FoundationModel(torch.nn.Module): +class FoundationModel(torch.nn.Module, ABC): def __init__(self, name: str, embed_dim: int) -> None: """Wrapper for a foundation model - forward and dimension differ depending on the model.""" super().__init__() self.name = name self.embed_dim = embed_dim + self.module = self.get_module() + + @abstractmethod + def get_module(self) -> torch.nn.Module: ... diff --git a/prostate_cancer/modeling/backbone/pgp.py b/prostate_cancer/modeling/backbone/pgp.py index 51d1f2b8..0d38dffb 100644 --- a/prostate_cancer/modeling/backbone/pgp.py +++ b/prostate_cancer/modeling/backbone/pgp.py @@ -7,8 +7,10 @@ class ProvGigaPath(FoundationModel): def __init__(self, name: str) -> None: super().__init__(name, 1536) + + def get_module(self) -> torch.nn.Module: # For this, you need to setup HF_TOKEN= env.variable. - self.module = timm.create_model( + return timm.create_model( "hf_hub:prov-gigapath/prov-gigapath", pretrained=True ).eval() diff --git a/prostate_cancer/modeling/backbone/virchow2.py b/prostate_cancer/modeling/backbone/virchow2.py index 707b36bb..028154d5 100644 --- a/prostate_cancer/modeling/backbone/virchow2.py +++ b/prostate_cancer/modeling/backbone/virchow2.py @@ -9,8 +9,9 @@ class Virchow2(FoundationModel): def __init__(self, name: str) -> None: super().__init__(name, 2560) + def get_module(self) -> torch.nn.Module: # For this, you need to setup HF_TOKEN= env.variable. - self.module = timm.create_model( + return timm.create_model( "hf-hub:paige-ai/Virchow2", pretrained=True, mlp_layer=SwiGLUPacked, From 0afc18de17542f091d7e3dd5a63fd68062fc0ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 08:42:29 +0000 Subject: [PATCH 139/284] fix: model path --- configs/model/pgp_full_model.yaml | 2 +- configs/model/virchow2_full_model.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/model/pgp_full_model.yaml b/configs/model/pgp_full_model.yaml index 4defdc66..b322d607 100644 --- a/configs/model/pgp_full_model.yaml +++ b/configs/model/pgp_full_model.yaml @@ -1,6 +1,6 @@ defaults: - decode_head: embedding_classifier - - backbone: ProvGigaPath + - backbone: pgp - _self_ decode_head: diff --git a/configs/model/virchow2_full_model.yaml b/configs/model/virchow2_full_model.yaml index 09fe52e2..d2e1fe9f 100644 --- a/configs/model/virchow2_full_model.yaml +++ b/configs/model/virchow2_full_model.yaml @@ -1,6 +1,6 @@ defaults: - decode_head: embedding_classifier - - backbone: Virchow2 + - backbone: virchow2 - _self_ decode_head: From b084a11dc70b9fe0fc1842f77bf2ce258df3744f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 08:52:13 +0000 Subject: [PATCH 140/284] fix: proper import --- configs/model/pgp_full_model.yaml | 2 +- configs/model/virchow2_full_model.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/model/pgp_full_model.yaml b/configs/model/pgp_full_model.yaml index b322d607..e1e3d889 100644 --- a/configs/model/pgp_full_model.yaml +++ b/configs/model/pgp_full_model.yaml @@ -6,7 +6,7 @@ defaults: decode_head: in_features: 1536 -_target_: prostate_cancer.cnn_model.FoundationProstateModel +_target_: prostate_cancer.foundation_model.FoundationProstateModel lr: ??? tl_threshold: ??? freeze_backbone: True diff --git a/configs/model/virchow2_full_model.yaml b/configs/model/virchow2_full_model.yaml index d2e1fe9f..6f6309fd 100644 --- a/configs/model/virchow2_full_model.yaml +++ b/configs/model/virchow2_full_model.yaml @@ -6,7 +6,7 @@ defaults: decode_head: in_features: 2560 -_target_: prostate_cancer.cnn_model.FoundationProstateModel +_target_: prostate_cancer.foundation_model.FoundationProstateModel lr: ??? tl_threshold: ??? freeze_backbone: True From 790ff072389c44c88963bdea9963301978534ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 09:15:41 +0000 Subject: [PATCH 141/284] fix: name --- .../experiment/preprocessing/tile_embeddings/pgp/base.yaml | 1 - .../preprocessing/tile_embeddings/virchow2/base.yaml | 1 - prostate_cancer/modeling/backbone/pgp.py | 4 ++-- prostate_cancer/modeling/backbone/virchow2.py | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml b/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml index 9a5d2b4b..7ee4520c 100644 --- a/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml +++ b/configs/experiment/preprocessing/tile_embeddings/pgp/base.yaml @@ -2,4 +2,3 @@ tile_encoder: _target_: prostate_cancer.modeling.backbone.pgp.ProvGigaPath - name: PGP diff --git a/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml b/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml index 2b62de2a..3c1be55a 100644 --- a/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml +++ b/configs/experiment/preprocessing/tile_embeddings/virchow2/base.yaml @@ -2,4 +2,3 @@ tile_encoder: _target_: prostate_cancer.modeling.backbone.virchow2.Virchow2 - name: Virchow2 diff --git a/prostate_cancer/modeling/backbone/pgp.py b/prostate_cancer/modeling/backbone/pgp.py index 0d38dffb..5efbc9a7 100644 --- a/prostate_cancer/modeling/backbone/pgp.py +++ b/prostate_cancer/modeling/backbone/pgp.py @@ -5,8 +5,8 @@ class ProvGigaPath(FoundationModel): - def __init__(self, name: str) -> None: - super().__init__(name, 1536) + def __init__(self) -> None: + super().__init__("PGP", 1536) def get_module(self) -> torch.nn.Module: # For this, you need to setup HF_TOKEN= env.variable. diff --git a/prostate_cancer/modeling/backbone/virchow2.py b/prostate_cancer/modeling/backbone/virchow2.py index 028154d5..852c85af 100644 --- a/prostate_cancer/modeling/backbone/virchow2.py +++ b/prostate_cancer/modeling/backbone/virchow2.py @@ -6,8 +6,8 @@ class Virchow2(FoundationModel): - def __init__(self, name: str) -> None: - super().__init__(name, 2560) + def __init__(self) -> None: + super().__init__("Virchow2", 2560) def get_module(self) -> torch.nn.Module: # For this, you need to setup HF_TOKEN= env.variable. From 4cf664467085a9b24bb2ed2db983a8a33096dc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 10:05:19 +0000 Subject: [PATCH 142/284] fix: eval --- prostate_cancer/foundation_model.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index 86113352..d988c66d 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -24,6 +24,10 @@ def __init__( p.requires_grad = False self.backbone.module.eval() + # prevent unintentional train mode + def on_fit_start(self) -> None: + self.model.backbone.eval() + def forward(self, x: Tensor) -> Tensor: features = self.backbone(x) logits = self.decode_head(features) From b66555a28f1c68373daae3c56c1775cfe882b03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 10:06:21 +0000 Subject: [PATCH 143/284] fix: freeze --- prostate_cancer/foundation_model.py | 7 +++++-- prostate_cancer/modeling/backbone/__init__.py | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index d988c66d..4a424281 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -18,15 +18,18 @@ def __init__( self.backbone = backbone self.decode_head = decode_head + self.frozen_backbone = freeze_backbone + # stay consistent with embedding training if freeze_backbone: for p in self.backbone.module.parameters(): p.requires_grad = False self.backbone.module.eval() - # prevent unintentional train mode def on_fit_start(self) -> None: - self.model.backbone.eval() + # prevent unintentional train mode + if self.frozen_backbone: + self.model.backbone.eval() def forward(self, x: Tensor) -> Tensor: features = self.backbone(x) diff --git a/prostate_cancer/modeling/backbone/__init__.py b/prostate_cancer/modeling/backbone/__init__.py index 643a34d0..f6c3b814 100644 --- a/prostate_cancer/modeling/backbone/__init__.py +++ b/prostate_cancer/modeling/backbone/__init__.py @@ -1,8 +1,8 @@ from prostate_cancer.modeling.backbone.google_vit import google_vit +from prostate_cancer.modeling.backbone.pgp import ProvGigaPath from prostate_cancer.modeling.backbone.resnet50 import resnet50 from prostate_cancer.modeling.backbone.vgg16 import vgg16 from prostate_cancer.modeling.backbone.virchow2 import Virchow2 -from prostate_cancer.modeling.backbone.pgp import ProvGigaPath -__all__ = ["google_vit", "resnet50", "vgg16", "Virchow2", "ProvGigaPath"] +__all__ = ["ProvGigaPath", "Virchow2", "google_vit", "resnet50", "vgg16"] From 3127e2d697beb9bc81f35cbaf8b82e2e2ae1bd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 23 Jun 2026 14:02:40 +0000 Subject: [PATCH 144/284] fix: freezing --- prostate_cancer/foundation_model.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index 4a424281..e9e9ddf6 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -1,4 +1,4 @@ -from torch import Tensor +import torch from prostate_cancer.base_model import ProstateCancerModel from prostate_cancer.modeling.backbone.foundation_base import FoundationModel @@ -26,12 +26,17 @@ def __init__( p.requires_grad = False self.backbone.module.eval() - def on_fit_start(self) -> None: + def on_train_epoch_start(self) -> None: # prevent unintentional train mode if self.frozen_backbone: - self.model.backbone.eval() + self.backbone.module.eval() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + if self.frozen_backbone: + with torch.no_grad(): + features = self.backbone(x) + else: + features = self.backbone(x) - def forward(self, x: Tensor) -> Tensor: - features = self.backbone(x) logits = self.decode_head(features) return logits From 22a061093ce12317976aaa5205878b24773ae9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 24 Jun 2026 06:40:17 +0000 Subject: [PATCH 145/284] feat: light checkpoint --- prostate_cancer/foundation_model.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index e9e9ddf6..8995ee73 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -1,3 +1,5 @@ +from typing import Any + import torch from prostate_cancer.base_model import ProstateCancerModel @@ -26,6 +28,17 @@ def __init__( p.requires_grad = False self.backbone.module.eval() + def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None: + # no need to save frozen backbone + state_dict: dict[str, Any] = checkpoint["state_dict"] + + keys_to_remove = [ + k for k in list(state_dict.keys()) if k.startswith("backbone.") + ] + + for k in keys_to_remove: + del state_dict[k] + def on_train_epoch_start(self) -> None: # prevent unintentional train mode if self.frozen_backbone: From 44f2237b9f8ff2508110cff92bb4518f6dde21fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 25 Jun 2026 05:51:43 +0000 Subject: [PATCH 146/284] feat: pgp stain model --- .../model/checkpoints/mmci_tl/base_foundation.yaml | 3 +++ configs/model/checkpoints/mmci_tl/pgp.yaml | 2 +- configs/model/checkpoints/mmci_tl/pgp_mil.yaml | 2 +- configs/model/checkpoints/mmci_tl/pgp_stain.yaml | 12 ++++++++++++ configs/model/checkpoints/mmci_tl/virchow2.yaml | 2 +- configs/model/checkpoints/mmci_tl/virchow2_mil.yaml | 2 +- 6 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 configs/model/checkpoints/mmci_tl/base_foundation.yaml create mode 100644 configs/model/checkpoints/mmci_tl/pgp_stain.yaml diff --git a/configs/model/checkpoints/mmci_tl/base_foundation.yaml b/configs/model/checkpoints/mmci_tl/base_foundation.yaml new file mode 100644 index 00000000..2b734bb1 --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/base_foundation.yaml @@ -0,0 +1,3 @@ +mean: [123.675, 116.280, 103.530] +std: [58.395, 57.120, 57.375] + diff --git a/configs/model/checkpoints/mmci_tl/pgp.yaml b/configs/model/checkpoints/mmci_tl/pgp.yaml index 722615b5..c178d894 100644 --- a/configs/model/checkpoints/mmci_tl/pgp.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp.yaml @@ -1,5 +1,5 @@ defaults: - - base + - base_foundation - _self_ tile_threshold: 0.81 diff --git a/configs/model/checkpoints/mmci_tl/pgp_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_mil.yaml index 86ee2b9f..074c4921 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_mil.yaml @@ -1,5 +1,5 @@ defaults: - - base + - base_foundation - _self_ tile_threshold: 0.5 diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml new file mode 100644 index 00000000..8ca29823 --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -0,0 +1,12 @@ +defaults: + - base_foundation + - _self_ + +tile_threshold: 0.5 +slide_max_threshold: 0.5 +slide_mpm_threshold: 0.5 +slide_top_k_threshold: 0.5 +slide_span_threshold: 0.5 # arbitrary - span predictions are binary + +checkpoint: "mlflow-artifacts:/65/fad3ed3cceb448d3af6047d5b0b868fe/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" +public_checkpoint: null diff --git a/configs/model/checkpoints/mmci_tl/virchow2.yaml b/configs/model/checkpoints/mmci_tl/virchow2.yaml index 4e8343c1..17c4349f 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2.yaml @@ -1,5 +1,5 @@ defaults: - - base + - base_foundation - _self_ tile_threshold: 0.87 diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml index 72215900..9a00458b 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml @@ -1,5 +1,5 @@ defaults: - - base + - base_foundation - _self_ tile_threshold: 0.5 From d957b187326b37c00b293ba7a8f7502ef7539691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 25 Jun 2026 05:54:35 +0000 Subject: [PATCH 147/284] feat: try preds --- .../heatmaps_and_max_aggregations/stain_base.yaml | 9 +++++++++ .../stain_mmci_tl_val.yaml | 6 ++++++ .../predict/embeddings/pgp/stain_base.yaml | 12 ++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_base.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_tl_val.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/stain_base.yaml diff --git a/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_base.yaml b/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_base.yaml new file mode 100644 index 00000000..5b604019 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_base.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /callbacks/heatmap_and_aggregator@callbacks + - /aggregator/max@aggregator + - /experiment/predict/embeddings/pgp/stain_base + - _self_ + +use_case: "Heatmaps & Max. Aggregation" diff --git a/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_tl_val.yaml b/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_tl_val.yaml new file mode 100644 index 00000000..0728ab9b --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_base + - /data/mmci/tl_val@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/pgp/stain_base.yaml b/configs/experiment/predict/embeddings/pgp/stain_base.yaml new file mode 100644 index 00000000..d8f75112 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/stain_base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/base + - /model/pgp_model@model + - /model/checkpoints/mmci_tl/pgp_stain@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_w_pgp_uri_224} From e67036a0a7eaed86d8cf16f66d7b107a81efa278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 25 Jun 2026 05:58:17 +0000 Subject: [PATCH 148/284] fix: model name --- configs/model/checkpoints/mmci_tl/pgp_stain.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml index 8ca29823..c61747f0 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -10,3 +10,4 @@ slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/fad3ed3cceb448d3af6047d5b0b868fe/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" public_checkpoint: null +model_name: "PGP (Stain)" From d3e5eb94ee7ec8e7fedfb124354bf8bc8883cac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 25 Jun 2026 06:03:07 +0000 Subject: [PATCH 149/284] fix: nest --- configs/model/checkpoints/mmci_tl/base_foundation.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/base_foundation.yaml b/configs/model/checkpoints/mmci_tl/base_foundation.yaml index 2b734bb1..7a397793 100644 --- a/configs/model/checkpoints/mmci_tl/base_foundation.yaml +++ b/configs/model/checkpoints/mmci_tl/base_foundation.yaml @@ -1,3 +1,4 @@ -mean: [123.675, 116.280, 103.530] -std: [58.395, 57.120, 57.375] +stats: + mean: [123.675, 116.280, 103.530] + std: [58.395, 57.120, 57.375] From 520712d79c4f06268a159705b81a5833a976b4fd Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 25 Jun 2026 06:50:53 +0000 Subject: [PATCH 150/284] feat: pre tiling --- configs/data/radboud/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/data/radboud/test.yaml b/configs/data/radboud/test.yaml index cc5ee1dc..d20312ce 100644 --- a/configs/data/radboud/test.yaml +++ b/configs/data/radboud/test.yaml @@ -5,5 +5,6 @@ defaults: metadata_table: "mlflow-artifacts:/38/1a718a849be3412ab4621b6772daf049/artifacts/PANDA - Radboud_test.csv" tiles_uri_512: "mlflow-artifacts:/38/7bfcfeeaa29a4745943ac8556a63de4b/artifacts/PANDA - Radboud test" +tiles_uri_224: "mlflow-artifacts:/38/71423731ba2a45099f28892733667386/artifacts/PANDA - Radboud train" data_name: "PANDA - Radboud test" From dec409784263c68496fc9072c1e35ac976a6b3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 25 Jun 2026 07:00:43 +0000 Subject: [PATCH 151/284] feat: prepare virchow2 training --- .../train/images/virchow2/base_stain.yaml | 21 +++++++++++++++++++ .../train/images/virchow2/mmci_tl_stain.yaml | 7 +++++++ 2 files changed, 28 insertions(+) create mode 100644 configs/experiment/train/images/virchow2/base_stain.yaml create mode 100644 configs/experiment/train/images/virchow2/mmci_tl_stain.yaml diff --git a/configs/experiment/train/images/virchow2/base_stain.yaml b/configs/experiment/train/images/virchow2/base_stain.yaml new file mode 100644 index 00000000..58f3bd3b --- /dev/null +++ b/configs/experiment/train/images/virchow2/base_stain.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /datamodule/transforms/normalize_w_stain_augments@transforms + - /model/virchow2_full_model@model + - _self_ + +model_name: "Virchow2 (Stain)" + +# ImageNet stats +mean: [123.675, 116.280, 103.530] +std: [58.395, 57.120, 57.375] + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/virchow2/mmci_tl_stain.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain.yaml new file mode 100644 index 00000000..d57f2ea5 --- /dev/null +++ b/configs/experiment/train/images/virchow2/mmci_tl_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/virchow2/base_stain + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ From a242d0dc1e5ee404805c5ffe4b7131039885c06e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 27 Jun 2026 10:12:13 +0000 Subject: [PATCH 152/284] feat: update uris --- configs/data/radboud/test.yaml | 4 ++-- configs/data/radboud/train.yaml | 4 ++-- configs/data/radboud/val.yaml | 2 +- configs/data/radboud/val_heuristic_normalized.yaml | 4 ++-- configs/data/radboud/val_ml_normalized.yaml | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/configs/data/radboud/test.yaml b/configs/data/radboud/test.yaml index d20312ce..5fe744ac 100644 --- a/configs/data/radboud/test.yaml +++ b/configs/data/radboud/test.yaml @@ -4,7 +4,7 @@ defaults: metadata_table: "mlflow-artifacts:/38/1a718a849be3412ab4621b6772daf049/artifacts/PANDA - Radboud_test.csv" -tiles_uri_512: "mlflow-artifacts:/38/7bfcfeeaa29a4745943ac8556a63de4b/artifacts/PANDA - Radboud test" -tiles_uri_224: "mlflow-artifacts:/38/71423731ba2a45099f28892733667386/artifacts/PANDA - Radboud train" +tiles_uri_512: "mlflow-artifacts:/38/197410ccc71c443bb8b1d8eb517c53fa/artifacts/PANDA - Radboud test" +tiles_uri_224: "mlflow-artifacts:/38/4cab0908f7eb4d0aba64407af072f08c/artifacts/PANDA - Radboud test" data_name: "PANDA - Radboud test" diff --git a/configs/data/radboud/train.yaml b/configs/data/radboud/train.yaml index 95b509eb..b4800d2e 100644 --- a/configs/data/radboud/train.yaml +++ b/configs/data/radboud/train.yaml @@ -4,7 +4,7 @@ defaults: # points to the public mlflow metadata_table: "mlflow-artifacts:/38/25b0e9ead22e44908f74d23b4efcaa62/artifacts/PANDA - Radboud train + val_train.csv" -tiles_uri_512: "mlflow-artifacts:/38/fc5ea991725d4066bddeb31090aae463/artifacts/PANDA - Radboud train" -tiles_uri_224: "mlflow-artifacts:/38/71423731ba2a45099f28892733667386/artifacts/PANDA - Radboud train" +tiles_uri_512: "mlflow-artifacts:/38/fc5ea991725d4066bddeb31090aae463/artifacts/PANDA - Radboud train" # invalid QC +tiles_uri_224: "mlflow-artifacts:/38/71423731ba2a45099f28892733667386/artifacts/PANDA - Radboud train" # invalid QC data_name: "PANDA - Radboud train" diff --git a/configs/data/radboud/val.yaml b/configs/data/radboud/val.yaml index 9a59d188..b8692bcf 100644 --- a/configs/data/radboud/val.yaml +++ b/configs/data/radboud/val.yaml @@ -3,6 +3,6 @@ defaults: - _self_ tiles_uri_512: "mlflow-artifacts:/38/4e3b31f87a8a46df9f911a69206d6a1d/artifacts/PANDA - Radboud val" -tiles_uri_224: "mlflow-artifacts:/38/fae8eacf77a24fce808de83cb8ec3edd/artifacts/PANDA - Radboud val" +tiles_uri_224: "mlflow-artifacts:/38/33ad08ab5f9845809fb48b2b7d2aa643/artifacts/PANDA - Radboud val" data_name: "PANDA - Radboud val" diff --git a/configs/data/radboud/val_heuristic_normalized.yaml b/configs/data/radboud/val_heuristic_normalized.yaml index 58664cf1..a70b5802 100644 --- a/configs/data/radboud/val_heuristic_normalized.yaml +++ b/configs/data/radboud/val_heuristic_normalized.yaml @@ -2,7 +2,7 @@ defaults: - base_val - _self_ -tiles_uri_512: "mlflow-artifacts:/38/4ced588761614abda73d2f447284d6ce/artifacts/PANDA - Radboud val512_normalized" -tiles_uri_224: "mlflow-artifacts:/38/4ced588761614abda73d2f447284d6ce/artifacts/PANDA - Radboud val224_normalized" +tiles_uri_512: "mlflow-artifacts:/38/4ced588761614abda73d2f447284d6ce/artifacts/PANDA - Radboud val512_normalized" # invalid QC +tiles_uri_224: "mlflow-artifacts:/38/4ced588761614abda73d2f447284d6ce/artifacts/PANDA - Radboud val224_normalized" # invalid QC data_name: "PANDA - Radboud val (Heuristic Normalized)" diff --git a/configs/data/radboud/val_ml_normalized.yaml b/configs/data/radboud/val_ml_normalized.yaml index 3f8aaee9..2139085e 100644 --- a/configs/data/radboud/val_ml_normalized.yaml +++ b/configs/data/radboud/val_ml_normalized.yaml @@ -2,7 +2,7 @@ defaults: - base_val - _self_ -tiles_uri_512: "mlflow-artifacts:/38/c0a9e22016924940880188d6e111f150/artifacts/PANDA - Radboud val512_normalized" -tiles_uri_224: "mlflow-artifacts:/38/c0a9e22016924940880188d6e111f150/artifacts/PANDA - Radboud val224_normalized" +tiles_uri_512: "mlflow-artifacts:/38/c0a9e22016924940880188d6e111f150/artifacts/PANDA - Radboud val512_normalized" # invalid QC +tiles_uri_224: "mlflow-artifacts:/38/c0a9e22016924940880188d6e111f150/artifacts/PANDA - Radboud val224_normalized" # invalid QC data_name: "PANDA - Radboud val (ML Normalized)" From 4fce016f7426771ce0afde29b72fcf1a1e15338e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 27 Jun 2026 10:38:11 +0000 Subject: [PATCH 153/284] feat: filtered tiles --- configs/data/radboud/test.yaml | 4 ++++ .../preprocessing/filter_tiles/radboud_test.yaml | 5 +++++ .../tile_masks/radboud_test_224.yaml | 15 +++++++++++++++ .../tile_masks/radboud_test_512.yaml | 15 +++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 configs/experiment/preprocessing/filter_tiles/radboud_test.yaml create mode 100644 configs/experiment/preprocessing/tile_masks/radboud_test_224.yaml create mode 100644 configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml diff --git a/configs/data/radboud/test.yaml b/configs/data/radboud/test.yaml index 5fe744ac..acd5269b 100644 --- a/configs/data/radboud/test.yaml +++ b/configs/data/radboud/test.yaml @@ -4,7 +4,11 @@ defaults: metadata_table: "mlflow-artifacts:/38/1a718a849be3412ab4621b6772daf049/artifacts/PANDA - Radboud_test.csv" + tiles_uri_512: "mlflow-artifacts:/38/197410ccc71c443bb8b1d8eb517c53fa/artifacts/PANDA - Radboud test" +tiles_filtered_uri_512: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/artifacts/PANDA - Radboud test_512" + tiles_uri_224: "mlflow-artifacts:/38/4cab0908f7eb4d0aba64407af072f08c/artifacts/PANDA - Radboud test" +tiles_filtered_uri_224: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/artifacts/PANDA - Radboud test_224" data_name: "PANDA - Radboud test" diff --git a/configs/experiment/preprocessing/filter_tiles/radboud_test.yaml b/configs/experiment/preprocessing/filter_tiles/radboud_test.yaml new file mode 100644 index 00000000..23100058 --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/radboud_test.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/radboud/test@data + - _self_ diff --git a/configs/experiment/preprocessing/tile_masks/radboud_test_224.yaml b/configs/experiment/preprocessing/tile_masks/radboud_test_224.yaml new file mode 100644 index 00000000..ca9819ee --- /dev/null +++ b/configs/experiment/preprocessing/tile_masks/radboud_test_224.yaml @@ -0,0 +1,15 @@ +# @package _global_ + +defaults: + - /data/radboud/test@data + - _self_ + +tile_uris: + - ${data.tiles_filtered_uri_224} + +percentage_cols: + - "tissue_roi_percentage" + - "carcinoma_roi_percentage" + - "residual_percentage" + - "blur_percentage" + - "folding_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml b/configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml new file mode 100644 index 00000000..82c5458c --- /dev/null +++ b/configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml @@ -0,0 +1,15 @@ +# @package _global_ + +defaults: + - /data/radboud/val@data + - _self_ + +tile_uris: + - ${data.tiles_filtered_uri_512} + +percentage_cols: + - "tissue_roi_percentage" + - "carcinoma_roi_percentage" + - "residual_percentage" + - "blur_percentage" + - "folding_percentage" From 4bcd0b3e43db356de9340ae46b0c135eeb980f13 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 27 Jun 2026 16:55:37 +0000 Subject: [PATCH 154/284] feat: fix data source --- .../experiment/preprocessing/tile_masks/radboud_test_512.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml b/configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml index 82c5458c..4ff4d6ed 100644 --- a/configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml +++ b/configs/experiment/preprocessing/tile_masks/radboud_test_512.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /data/radboud/val@data + - /data/radboud/test@data - _self_ tile_uris: From 8736c26df230542416a2eea6d3a221a53d55adba Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 27 Jun 2026 17:12:43 +0000 Subject: [PATCH 155/284] feat: tile masks filtered --- configs/data/radboud/val.yaml | 3 +++ .../preprocessing/filter_tiles/radboud_val.yaml | 5 +++++ .../preprocessing/tile_masks/radboud_val_224.yaml | 15 +++++++++++++++ .../preprocessing/tile_masks/radboud_val_512.yaml | 5 ++++- 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 configs/experiment/preprocessing/filter_tiles/radboud_val.yaml create mode 100644 configs/experiment/preprocessing/tile_masks/radboud_val_224.yaml diff --git a/configs/data/radboud/val.yaml b/configs/data/radboud/val.yaml index b8692bcf..a05fe304 100644 --- a/configs/data/radboud/val.yaml +++ b/configs/data/radboud/val.yaml @@ -3,6 +3,9 @@ defaults: - _self_ tiles_uri_512: "mlflow-artifacts:/38/4e3b31f87a8a46df9f911a69206d6a1d/artifacts/PANDA - Radboud val" +tiles_filtered_uri_512: "mlflow-artifacts:/38/b38ca47d6fd4422a9f8d5a32fcae221c/artifacts/PANDA - Radboud val_512" + tiles_uri_224: "mlflow-artifacts:/38/33ad08ab5f9845809fb48b2b7d2aa643/artifacts/PANDA - Radboud val" +tiles_filtered_uri_224: "mlflow-artifacts:/38/b38ca47d6fd4422a9f8d5a32fcae221c/artifacts/PANDA - Radboud val_224" data_name: "PANDA - Radboud val" diff --git a/configs/experiment/preprocessing/filter_tiles/radboud_val.yaml b/configs/experiment/preprocessing/filter_tiles/radboud_val.yaml new file mode 100644 index 00000000..8ead8fb8 --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/radboud_val.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/radboud/val@data + - _self_ diff --git a/configs/experiment/preprocessing/tile_masks/radboud_val_224.yaml b/configs/experiment/preprocessing/tile_masks/radboud_val_224.yaml new file mode 100644 index 00000000..cd3f0fa9 --- /dev/null +++ b/configs/experiment/preprocessing/tile_masks/radboud_val_224.yaml @@ -0,0 +1,15 @@ +# @package _global_ + +defaults: + - /data/radboud/val@data + - _self_ + +tile_uris: + - ${data.tiles_filtered_uri_224} + +percentage_cols: + - "tissue_roi_percentage" + - "carcinoma_roi_percentage" + - "residual_percentage" + - "blur_percentage" + - "folding_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/radboud_val_512.yaml b/configs/experiment/preprocessing/tile_masks/radboud_val_512.yaml index 1d8ab1aa..82c5458c 100644 --- a/configs/experiment/preprocessing/tile_masks/radboud_val_512.yaml +++ b/configs/experiment/preprocessing/tile_masks/radboud_val_512.yaml @@ -5,8 +5,11 @@ defaults: - _self_ tile_uris: - - ${data.tiles_uri_512} + - ${data.tiles_filtered_uri_512} percentage_cols: - "tissue_roi_percentage" - "carcinoma_roi_percentage" + - "residual_percentage" + - "blur_percentage" + - "folding_percentage" From d616063ed55edb01626bbf88c1c1e863bf596be2 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 28 Jun 2026 06:30:49 +0000 Subject: [PATCH 156/284] feat: retile trai --- configs/data/radboud/train.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/data/radboud/train.yaml b/configs/data/radboud/train.yaml index b4800d2e..4c8f704d 100644 --- a/configs/data/radboud/train.yaml +++ b/configs/data/radboud/train.yaml @@ -4,7 +4,7 @@ defaults: # points to the public mlflow metadata_table: "mlflow-artifacts:/38/25b0e9ead22e44908f74d23b4efcaa62/artifacts/PANDA - Radboud train + val_train.csv" -tiles_uri_512: "mlflow-artifacts:/38/fc5ea991725d4066bddeb31090aae463/artifacts/PANDA - Radboud train" # invalid QC -tiles_uri_224: "mlflow-artifacts:/38/71423731ba2a45099f28892733667386/artifacts/PANDA - Radboud train" # invalid QC +tiles_uri_512: "mlflow-artifacts:/38/02b36c4f7e544436b0bd2f81146676c5/artifacts/PANDA - Radboud train" +tiles_uri_224: "mlflow-artifacts:/38/d658d0c339844eec8b425b403657f086/artifacts/PANDA - Radboud train" data_name: "PANDA - Radboud train" From 59bf8dfa3dfacae96cc6bca01c53e1dabb612499 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 28 Jun 2026 06:41:56 +0000 Subject: [PATCH 157/284] feat: tile masks filtered --- configs/data/radboud/train.yaml | 3 +++ .../preprocessing/filter_tiles/radboud_train.yaml | 5 +++++ .../tile_masks/radboud_train_224.yaml | 15 +++++++++++++++ .../tile_masks/radboud_train_512.yaml | 15 +++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 configs/experiment/preprocessing/filter_tiles/radboud_train.yaml create mode 100644 configs/experiment/preprocessing/tile_masks/radboud_train_224.yaml create mode 100644 configs/experiment/preprocessing/tile_masks/radboud_train_512.yaml diff --git a/configs/data/radboud/train.yaml b/configs/data/radboud/train.yaml index 4c8f704d..71f38a82 100644 --- a/configs/data/radboud/train.yaml +++ b/configs/data/radboud/train.yaml @@ -5,6 +5,9 @@ defaults: # points to the public mlflow metadata_table: "mlflow-artifacts:/38/25b0e9ead22e44908f74d23b4efcaa62/artifacts/PANDA - Radboud train + val_train.csv" tiles_uri_512: "mlflow-artifacts:/38/02b36c4f7e544436b0bd2f81146676c5/artifacts/PANDA - Radboud train" +tiles_filtered_uri_512: "mlflow-artifacts:/38/e1ef2698c62541e4a5e92a65ecb36fa5/artifacts/PANDA - Radboud train_512" + tiles_uri_224: "mlflow-artifacts:/38/d658d0c339844eec8b425b403657f086/artifacts/PANDA - Radboud train" +tiles_filtered_uri_224: "mlflow-artifacts:/38/e1ef2698c62541e4a5e92a65ecb36fa5/artifacts/PANDA - Radboud train_224" data_name: "PANDA - Radboud train" diff --git a/configs/experiment/preprocessing/filter_tiles/radboud_train.yaml b/configs/experiment/preprocessing/filter_tiles/radboud_train.yaml new file mode 100644 index 00000000..c6d87616 --- /dev/null +++ b/configs/experiment/preprocessing/filter_tiles/radboud_train.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /data/radboud/train@data + - _self_ diff --git a/configs/experiment/preprocessing/tile_masks/radboud_train_224.yaml b/configs/experiment/preprocessing/tile_masks/radboud_train_224.yaml new file mode 100644 index 00000000..bef43107 --- /dev/null +++ b/configs/experiment/preprocessing/tile_masks/radboud_train_224.yaml @@ -0,0 +1,15 @@ +# @package _global_ + +defaults: + - /data/radboud/train@data + - _self_ + +tile_uris: + - ${data.tiles_filtered_uri_224} + +percentage_cols: + - "tissue_roi_percentage" + - "carcinoma_roi_percentage" + - "residual_percentage" + - "blur_percentage" + - "folding_percentage" diff --git a/configs/experiment/preprocessing/tile_masks/radboud_train_512.yaml b/configs/experiment/preprocessing/tile_masks/radboud_train_512.yaml new file mode 100644 index 00000000..ecd8700f --- /dev/null +++ b/configs/experiment/preprocessing/tile_masks/radboud_train_512.yaml @@ -0,0 +1,15 @@ +# @package _global_ + +defaults: + - /data/radboud/train@data + - _self_ + +tile_uris: + - ${data.tiles_filtered_uri_512} + +percentage_cols: + - "tissue_roi_percentage" + - "carcinoma_roi_percentage" + - "residual_percentage" + - "blur_percentage" + - "folding_percentage" From 4d90926cb8c6b1301379855603a3780795b90b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 29 Jun 2026 19:48:59 +0000 Subject: [PATCH 158/284] feat: redesign --- configs/experiment/train/images/pgp/base.yaml | 18 ++++++++++++++++++ .../train/images/pgp/base_stain.yaml | 18 +----------------- .../train/images/pgp/base_stain_frozen.yaml | 10 ++++++++++ ...l_stain.yaml => mmci_tl_stain_frozen.yaml} | 2 +- .../train/images/virchow2/base.yaml | 18 ++++++++++++++++++ .../train/images/virchow2/base_stain.yaml | 18 +----------------- .../images/virchow2/base_stain_frozen.yaml | 10 ++++++++++ ...l_stain.yaml => mmci_tl_stain_frozen.yaml} | 2 +- configs/model/pgp_full_model.yaml | 2 +- configs/model/virchow2_full_model.yaml | 2 +- prostate_cancer/foundation_model.py | 19 ++++++++++--------- 11 files changed, 72 insertions(+), 47 deletions(-) create mode 100644 configs/experiment/train/images/pgp/base.yaml create mode 100644 configs/experiment/train/images/pgp/base_stain_frozen.yaml rename configs/experiment/train/images/pgp/{mmci_tl_stain.yaml => mmci_tl_stain_frozen.yaml} (67%) create mode 100644 configs/experiment/train/images/virchow2/base.yaml create mode 100644 configs/experiment/train/images/virchow2/base_stain_frozen.yaml rename configs/experiment/train/images/virchow2/{mmci_tl_stain.yaml => mmci_tl_stain_frozen.yaml} (65%) diff --git a/configs/experiment/train/images/pgp/base.yaml b/configs/experiment/train/images/pgp/base.yaml new file mode 100644 index 00000000..8342ffd6 --- /dev/null +++ b/configs/experiment/train/images/pgp/base.yaml @@ -0,0 +1,18 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /model/pgp_full_model@model + - _self_ + +# ImageNet stats +mean: [123.675, 116.280, 103.530] +std: [58.395, 57.120, 57.375] + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/pgp/base_stain.yaml b/configs/experiment/train/images/pgp/base_stain.yaml index 895f7bfe..18dc715d 100644 --- a/configs/experiment/train/images/pgp/base_stain.yaml +++ b/configs/experiment/train/images/pgp/base_stain.yaml @@ -1,21 +1,5 @@ # @package _global_ defaults: - - /experiment/train/images/base + - /experiment/train/images/pgp/base - /datamodule/transforms/normalize_w_stain_augments@transforms - - /model/pgp_full_model@model - - _self_ - -model_name: "PGP (Stain)" - -# ImageNet stats -mean: [123.675, 116.280, 103.530] -std: [58.395, 57.120, 57.375] - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/pgp/base_stain_frozen.yaml b/configs/experiment/train/images/pgp/base_stain_frozen.yaml new file mode 100644 index 00000000..d4e09178 --- /dev/null +++ b/configs/experiment/train/images/pgp/base_stain_frozen.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /experiment/train/images/pgp/base_stain + - _self_ + +model_name: "PGP (Stain & Frozen)" + +model: + freeze_backbone: True diff --git a/configs/experiment/train/images/pgp/mmci_tl_stain.yaml b/configs/experiment/train/images/pgp/mmci_tl_stain_frozen.yaml similarity index 67% rename from configs/experiment/train/images/pgp/mmci_tl_stain.yaml rename to configs/experiment/train/images/pgp/mmci_tl_stain_frozen.yaml index 23b85019..239751f7 100644 --- a/configs/experiment/train/images/pgp/mmci_tl_stain.yaml +++ b/configs/experiment/train/images/pgp/mmci_tl_stain_frozen.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/images/pgp/base_stain + - /experiment/train/images/pgp/base_stain_frozen - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - _self_ diff --git a/configs/experiment/train/images/virchow2/base.yaml b/configs/experiment/train/images/virchow2/base.yaml new file mode 100644 index 00000000..c37e4f2b --- /dev/null +++ b/configs/experiment/train/images/virchow2/base.yaml @@ -0,0 +1,18 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /model/virchow2_full_model@model + - _self_ + +# ImageNet stats +mean: [123.675, 116.280, 103.530] +std: [58.395, 57.120, 57.375] + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/virchow2/base_stain.yaml b/configs/experiment/train/images/virchow2/base_stain.yaml index 58f3bd3b..11182995 100644 --- a/configs/experiment/train/images/virchow2/base_stain.yaml +++ b/configs/experiment/train/images/virchow2/base_stain.yaml @@ -1,21 +1,5 @@ # @package _global_ defaults: - - /experiment/train/images/base + - /experiment/train/images/virchow2/base - /datamodule/transforms/normalize_w_stain_augments@transforms - - /model/virchow2_full_model@model - - _self_ - -model_name: "Virchow2 (Stain)" - -# ImageNet stats -mean: [123.675, 116.280, 103.530] -std: [58.395, 57.120, 57.375] - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/virchow2/base_stain_frozen.yaml b/configs/experiment/train/images/virchow2/base_stain_frozen.yaml new file mode 100644 index 00000000..98173eb0 --- /dev/null +++ b/configs/experiment/train/images/virchow2/base_stain_frozen.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /experiment/train/images/virchow2/base + - _self_ + +model_name: "Virchow (Stain & Frozen)" + +model: + freeze_backbone: True diff --git a/configs/experiment/train/images/virchow2/mmci_tl_stain.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml similarity index 65% rename from configs/experiment/train/images/virchow2/mmci_tl_stain.yaml rename to configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml index d57f2ea5..0eb54e8f 100644 --- a/configs/experiment/train/images/virchow2/mmci_tl_stain.yaml +++ b/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/images/virchow2/base_stain + - /experiment/train/images/virchow2/base_stain_frozen - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - _self_ diff --git a/configs/model/pgp_full_model.yaml b/configs/model/pgp_full_model.yaml index e1e3d889..e272ffad 100644 --- a/configs/model/pgp_full_model.yaml +++ b/configs/model/pgp_full_model.yaml @@ -9,4 +9,4 @@ decode_head: _target_: prostate_cancer.foundation_model.FoundationProstateModel lr: ??? tl_threshold: ??? -freeze_backbone: True +freeze_backbone: ??? diff --git a/configs/model/virchow2_full_model.yaml b/configs/model/virchow2_full_model.yaml index 6f6309fd..b3becc3e 100644 --- a/configs/model/virchow2_full_model.yaml +++ b/configs/model/virchow2_full_model.yaml @@ -9,4 +9,4 @@ decode_head: _target_: prostate_cancer.foundation_model.FoundationProstateModel lr: ??? tl_threshold: ??? -freeze_backbone: True +freeze_backbone: ??? \ No newline at end of file diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index 8995ee73..013460b8 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -29,15 +29,16 @@ def __init__( self.backbone.module.eval() def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None: - # no need to save frozen backbone - state_dict: dict[str, Any] = checkpoint["state_dict"] - - keys_to_remove = [ - k for k in list(state_dict.keys()) if k.startswith("backbone.") - ] - - for k in keys_to_remove: - del state_dict[k] + if self.frozen_backbone: + # no need to save frozen backbone + state_dict: dict[str, Any] = checkpoint["state_dict"] + + keys_to_remove = [ + k for k in list(state_dict.keys()) if k.startswith("backbone.") + ] + + for k in keys_to_remove: + del state_dict[k] def on_train_epoch_start(self) -> None: # prevent unintentional train mode From 4d7f215de19bbb0d43052eee3c640fb6a6772a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 29 Jun 2026 20:01:09 +0000 Subject: [PATCH 159/284] feat: add full stain trained variant --- .../experiment/train/images/pgp/base_stain_frozen.yaml | 4 ++-- .../experiment/train/images/pgp/base_stain_full.yaml | 10 ++++++++++ .../train/images/pgp/mmci_tl_stain_full.yaml | 7 +++++++ .../train/images/virchow2/base_stain_full.yaml | 10 ++++++++++ .../train/images/virchow2/mmci_tl_stain_full.yaml | 7 +++++++ 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 configs/experiment/train/images/pgp/base_stain_full.yaml create mode 100644 configs/experiment/train/images/pgp/mmci_tl_stain_full.yaml create mode 100644 configs/experiment/train/images/virchow2/base_stain_full.yaml create mode 100644 configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml diff --git a/configs/experiment/train/images/pgp/base_stain_frozen.yaml b/configs/experiment/train/images/pgp/base_stain_frozen.yaml index d4e09178..5e576233 100644 --- a/configs/experiment/train/images/pgp/base_stain_frozen.yaml +++ b/configs/experiment/train/images/pgp/base_stain_frozen.yaml @@ -4,7 +4,7 @@ defaults: - /experiment/train/images/pgp/base_stain - _self_ -model_name: "PGP (Stain & Frozen)" +model_name: "PGP (Stain)" model: - freeze_backbone: True + freeze_backbone: False diff --git a/configs/experiment/train/images/pgp/base_stain_full.yaml b/configs/experiment/train/images/pgp/base_stain_full.yaml new file mode 100644 index 00000000..d4e09178 --- /dev/null +++ b/configs/experiment/train/images/pgp/base_stain_full.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /experiment/train/images/pgp/base_stain + - _self_ + +model_name: "PGP (Stain & Frozen)" + +model: + freeze_backbone: True diff --git a/configs/experiment/train/images/pgp/mmci_tl_stain_full.yaml b/configs/experiment/train/images/pgp/mmci_tl_stain_full.yaml new file mode 100644 index 00000000..580a6f76 --- /dev/null +++ b/configs/experiment/train/images/pgp/mmci_tl_stain_full.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/pgp/base_stain_full + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ diff --git a/configs/experiment/train/images/virchow2/base_stain_full.yaml b/configs/experiment/train/images/virchow2/base_stain_full.yaml new file mode 100644 index 00000000..cdf5cbcb --- /dev/null +++ b/configs/experiment/train/images/virchow2/base_stain_full.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /experiment/train/images/virchow2/base + - _self_ + +model_name: "Virchow (Stain)" + +model: + freeze_backbone: False diff --git a/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml new file mode 100644 index 00000000..86bef889 --- /dev/null +++ b/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/virchow2/base_stain_full + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ From 073553367a258898e4154e320389f1566e313f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 29 Jun 2026 20:13:43 +0000 Subject: [PATCH 160/284] fix: proper inheritance --- configs/experiment/train/images/virchow2/base_stain_frozen.yaml | 2 +- configs/experiment/train/images/virchow2/base_stain_full.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/experiment/train/images/virchow2/base_stain_frozen.yaml b/configs/experiment/train/images/virchow2/base_stain_frozen.yaml index 98173eb0..49ac9b5b 100644 --- a/configs/experiment/train/images/virchow2/base_stain_frozen.yaml +++ b/configs/experiment/train/images/virchow2/base_stain_frozen.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/images/virchow2/base + - /experiment/train/images/virchow2/base_stain - _self_ model_name: "Virchow (Stain & Frozen)" diff --git a/configs/experiment/train/images/virchow2/base_stain_full.yaml b/configs/experiment/train/images/virchow2/base_stain_full.yaml index cdf5cbcb..8ab4e46b 100644 --- a/configs/experiment/train/images/virchow2/base_stain_full.yaml +++ b/configs/experiment/train/images/virchow2/base_stain_full.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/images/virchow2/base + - /experiment/train/images/virchow2/base_stain - _self_ model_name: "Virchow (Stain)" From 91c3f00496be3deff67ea553644002fde6f060a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 30 Jun 2026 06:26:52 +0000 Subject: [PATCH 161/284] feat: try smaller bs --- configs/experiment/train/images/virchow2/base.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configs/experiment/train/images/virchow2/base.yaml b/configs/experiment/train/images/virchow2/base.yaml index c37e4f2b..dc2e424b 100644 --- a/configs/experiment/train/images/virchow2/base.yaml +++ b/configs/experiment/train/images/virchow2/base.yaml @@ -10,6 +10,9 @@ mean: [123.675, 116.280, 103.530] std: [58.395, 57.120, 57.375] datamodule: + batch_size: 8 + num_workers: 4 + train: uris: - ${train_data.tiles_filtered_uri_224} From 5cdb54dd1304f3a061c59b85ceb60cf026c9cfe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 30 Jun 2026 14:31:39 +0000 Subject: [PATCH 162/284] feat: fix mlkit --- pyproject.toml | 2 +- uv.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0c78e67e..b6a979ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "triton>=3.3.0", "tqdm==4.67.1", "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", - "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git", + "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git@d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git@68904dfd216ce19454e08a032f7e4dd978a85707", diff --git a/uv.lock b/uv.lock index 672ff55d..3f98d154 100644 --- a/uv.lock +++ b/uv.lock @@ -2429,7 +2429,7 @@ requires-dist = [ { name = "pandas", specifier = ">=2.2.3" }, { name = "pillow", specifier = "==11.3.0" }, { name = "rationai-masks", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" }, - { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git" }, + { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git?rev=d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448" }, { name = "rationai-sdk", git = "https://github.com/RationAI/rationai-sdk-python.git?rev=68904dfd216ce19454e08a032f7e4dd978a85707" }, { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" }, { name = "rationai-tiling", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" }, @@ -2910,7 +2910,7 @@ dependencies = [ [[package]] name = "rationai-mlkit" version = "0.4.1" -source = { git = "https://github.com/RationAI/mlkit.git#51d1b52cdf4800962cf6f97a1cba1f72d78b8aa4" } +source = { git = "https://github.com/RationAI/mlkit.git?rev=d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448#d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448" } dependencies = [ { name = "datasets" }, { name = "gitpython" }, From eaa1c8d82dd5e6b6662bc20afb028f2598dad1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 1 Jul 2026 06:38:27 +0000 Subject: [PATCH 163/284] feat: advanced setup --- .../train/images/pgp/base_stain_full.yaml | 10 +++++ .../train/images/virchow2/base.yaml | 3 -- .../images/virchow2/base_stain_full.yaml | 10 +++++ prostate_cancer/foundation_model.py | 38 ++++++++++++++++++- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/configs/experiment/train/images/pgp/base_stain_full.yaml b/configs/experiment/train/images/pgp/base_stain_full.yaml index d4e09178..10e4f5a4 100644 --- a/configs/experiment/train/images/pgp/base_stain_full.yaml +++ b/configs/experiment/train/images/pgp/base_stain_full.yaml @@ -8,3 +8,13 @@ model_name: "PGP (Stain & Frozen)" model: freeze_backbone: True + backbone_lr: 1e-5 + warmup_steps: 200 + +trainer: + gradient_clip_val: 1.0 + +# full fine-tuning requires more memory +datamodule: + batch_size: 16 + num_workers: 8 diff --git a/configs/experiment/train/images/virchow2/base.yaml b/configs/experiment/train/images/virchow2/base.yaml index dc2e424b..c37e4f2b 100644 --- a/configs/experiment/train/images/virchow2/base.yaml +++ b/configs/experiment/train/images/virchow2/base.yaml @@ -10,9 +10,6 @@ mean: [123.675, 116.280, 103.530] std: [58.395, 57.120, 57.375] datamodule: - batch_size: 8 - num_workers: 4 - train: uris: - ${train_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/virchow2/base_stain_full.yaml b/configs/experiment/train/images/virchow2/base_stain_full.yaml index 8ab4e46b..8630da76 100644 --- a/configs/experiment/train/images/virchow2/base_stain_full.yaml +++ b/configs/experiment/train/images/virchow2/base_stain_full.yaml @@ -8,3 +8,13 @@ model_name: "Virchow (Stain)" model: freeze_backbone: False + backbone_lr: 1e-5 + warmup_steps: 200 + +trainer: + gradient_clip_val: 1.0 + +# full fine-tuning requires more memory +datamodule: + batch_size: 16 + num_workers: 8 diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index 013460b8..f3e2422b 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -1,6 +1,9 @@ from typing import Any import torch +from lightning.pytorch.utilities.types import OptimizerLRSchedulerConfig +from torch.optim import AdamW +from torch.optim.lr_scheduler import LambdaLR from prostate_cancer.base_model import ProstateCancerModel from prostate_cancer.modeling.backbone.foundation_base import FoundationModel @@ -15,6 +18,8 @@ def __init__( lr: float, tl_threshold: float, freeze_backbone: bool, + backbone_lr: float | None = None, + warmup_steps: int = 0, ) -> None: super().__init__(lr=lr, tl_threshold=tl_threshold) self.backbone = backbone @@ -22,6 +27,12 @@ def __init__( self.frozen_backbone = freeze_backbone + # full fine-tuning disrupts pretrained weights if updated at the + # same LR as the freshly initialized head, so it gets its own, + # lower LR (defaults to the head LR when unset) + self.backbone_lr = lr if backbone_lr is None else backbone_lr + self.warmup_steps = warmup_steps + # stay consistent with embedding training if freeze_backbone: for p in self.backbone.module.parameters(): @@ -32,11 +43,11 @@ def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None: if self.frozen_backbone: # no need to save frozen backbone state_dict: dict[str, Any] = checkpoint["state_dict"] - + keys_to_remove = [ k for k in list(state_dict.keys()) if k.startswith("backbone.") ] - + for k in keys_to_remove: del state_dict[k] @@ -54,3 +65,26 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: logits = self.decode_head(features) return logits + + def configure_optimizers(self) -> OptimizerLRSchedulerConfig: + param_groups = [{"params": self.decode_head.parameters(), "lr": self.lr}] + if not self.frozen_backbone: + param_groups.append( + {"params": self.backbone.parameters(), "lr": self.backbone_lr} + ) + + optimizer = AdamW(param_groups, lr=self.lr) + + if self.warmup_steps == 0: + return {"optimizer": optimizer} + + warmup_steps = self.warmup_steps + + def warmup(step: int) -> float: + return min(1.0, (step + 1) / warmup_steps) + + scheduler = LambdaLR(optimizer, lr_lambda=warmup) + return { + "optimizer": optimizer, + "lr_scheduler": {"scheduler": scheduler, "interval": "step"}, + } From b51c34e16eac35f3633300f0df104e68437dbe33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 1 Jul 2026 06:48:34 +0000 Subject: [PATCH 164/284] feat: virchow2 stain --- .../test/images/virchow2/stain_base.yaml | 7 +++++++ .../test/images/virchow2/stain_tile_threshold.yaml | 14 ++++++++++++++ .../model/checkpoints/mmci_tl/virchow2_stain.yaml | 10 ++++++++++ 3 files changed, 31 insertions(+) create mode 100644 configs/experiment/test/images/virchow2/stain_base.yaml create mode 100644 configs/experiment/test/images/virchow2/stain_tile_threshold.yaml create mode 100644 configs/model/checkpoints/mmci_tl/virchow2_stain.yaml diff --git a/configs/experiment/test/images/virchow2/stain_base.yaml b/configs/experiment/test/images/virchow2/stain_base.yaml new file mode 100644 index 00000000..29002f49 --- /dev/null +++ b/configs/experiment/test/images/virchow2/stain_base.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/test/images/base + - /model/virchow2_model@model + - /model/checkpoints/mmci_tl/virchow2_stain@model_data + - _self_ diff --git a/configs/experiment/test/images/virchow2/stain_tile_threshold.yaml b/configs/experiment/test/images/virchow2/stain_tile_threshold.yaml new file mode 100644 index 00000000..01169f6a --- /dev/null +++ b/configs/experiment/test/images/virchow2/stain_tile_threshold.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/images/virchow2/stain_base + - /data/mmci/tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.8 # Pathologist's estimate +use_case: "Tile Threshold Estimation" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml new file mode 100644 index 00000000..bda458e6 --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml @@ -0,0 +1,10 @@ +defaults: + - base_foundation + - _self_ + +tile_threshold: 0.5 + +checkpoint: "mlflow-artifacts:/65/d39c26e7dd474d3bbdd3932f6f8bf2fb/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" +public_checkpoint: null + +model_name: "Virchow2 (Stain & Frozen)" From a0b15f278f78848c22290fa94bb7430fcd1869d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 1 Jul 2026 06:52:27 +0000 Subject: [PATCH 165/284] feat: move to embeds --- .../test/{images => embeddings}/virchow2/stain_base.yaml | 7 ++++++- .../virchow2/stain_tile_threshold.yaml | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) rename configs/experiment/test/{images => embeddings}/virchow2/stain_base.yaml (51%) rename configs/experiment/test/{images => embeddings}/virchow2/stain_tile_threshold.yaml (60%) diff --git a/configs/experiment/test/images/virchow2/stain_base.yaml b/configs/experiment/test/embeddings/virchow2/stain_base.yaml similarity index 51% rename from configs/experiment/test/images/virchow2/stain_base.yaml rename to configs/experiment/test/embeddings/virchow2/stain_base.yaml index 29002f49..75bdaddb 100644 --- a/configs/experiment/test/images/virchow2/stain_base.yaml +++ b/configs/experiment/test/embeddings/virchow2/stain_base.yaml @@ -1,7 +1,12 @@ # @package _global_ defaults: - - /experiment/test/images/base + - /experiment/test/embeddings/base - /model/virchow2_model@model - /model/checkpoints/mmci_tl/virchow2_stain@model_data - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_w_virchow2_uri_224} diff --git a/configs/experiment/test/images/virchow2/stain_tile_threshold.yaml b/configs/experiment/test/embeddings/virchow2/stain_tile_threshold.yaml similarity index 60% rename from configs/experiment/test/images/virchow2/stain_tile_threshold.yaml rename to configs/experiment/test/embeddings/virchow2/stain_tile_threshold.yaml index 01169f6a..e31dbabe 100644 --- a/configs/experiment/test/images/virchow2/stain_tile_threshold.yaml +++ b/configs/experiment/test/embeddings/virchow2/stain_tile_threshold.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/tile_threshold@callbacks - - /experiment/test/images/virchow2/stain_base + - /experiment/test/embeddings/virchow2/stain_base - /data/mmci/tl_val@test_data - _self_ @@ -10,5 +10,5 @@ datamodule: test: stratified_filter: true # tile threshold not estimated from negative tiles in positive slides -tile_threshold: 0.8 # Pathologist's estimate -use_case: "Tile Threshold Estimation" +tile_threshold: 0.9 # Pathologist's estimate for other models +use_case: "TL Threshold Estimation" From d63b20cf7ed486f237c24c64908e05c6c3282658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 1 Jul 2026 07:55:01 +0000 Subject: [PATCH 166/284] feat: heatmaps --- .../embeddings/virchow2/heatmaps/stain_base.yaml | 8 ++++++++ .../virchow2/heatmaps/stain_mmci_tl_val.yaml | 6 ++++++ .../predict/embeddings/virchow2/stain_base.yaml | 12 ++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 configs/experiment/predict/embeddings/virchow2/heatmaps/stain_base.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/heatmaps/stain_mmci_tl_val.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/stain_base.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/heatmaps/stain_base.yaml b/configs/experiment/predict/embeddings/virchow2/heatmaps/stain_base.yaml new file mode 100644 index 00000000..2a2f969a --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/heatmaps/stain_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/heatmap@callbacks + - /experiment/predict/embeddings/virchow2/stain_base + - _self_ + +use_case: "Heatmaps" diff --git a/configs/experiment/predict/embeddings/virchow2/heatmaps/stain_mmci_tl_val.yaml b/configs/experiment/predict/embeddings/virchow2/heatmaps/stain_mmci_tl_val.yaml new file mode 100644 index 00000000..39a892a1 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/heatmaps/stain_mmci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/virchow2/heatmaps/stain_base + - /data/mmci/tl_val@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/virchow2/stain_base.yaml b/configs/experiment/predict/embeddings/virchow2/stain_base.yaml new file mode 100644 index 00000000..a12dd7ef --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/stain_base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/base + - /model/virchow2_model@model + - /model/checkpoints/mmci_tl/virchow2_stain@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_w_virchow2_uri_224} From 2e41cd9005b51162aafa6f08d1639e5d11ea28ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 1 Jul 2026 12:19:37 +0000 Subject: [PATCH 167/284] feat: tl test --- .../test/embeddings/virchow2/test_suite/stain_base.yaml | 8 ++++++++ .../virchow2/test_suite/stain_mmci_tl_test.yaml | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 configs/experiment/test/embeddings/virchow2/test_suite/stain_base.yaml create mode 100644 configs/experiment/test/embeddings/virchow2/test_suite/stain_mmci_tl_test.yaml diff --git a/configs/experiment/test/embeddings/virchow2/test_suite/stain_base.yaml b/configs/experiment/test/embeddings/virchow2/test_suite/stain_base.yaml new file mode 100644 index 00000000..8a96d268 --- /dev/null +++ b/configs/experiment/test/embeddings/virchow2/test_suite/stain_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/embeddings/virchow2/stain_base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/embeddings/virchow2/test_suite/stain_mmci_tl_test.yaml b/configs/experiment/test/embeddings/virchow2/test_suite/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..2a3ace81 --- /dev/null +++ b/configs/experiment/test/embeddings/virchow2/test_suite/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/embeddings/virchow2/test_suite/stain_base + - /data/mmci/tl_test@test_data + - _self_ From d5d492ec9562e03b9e663059bd86cb0c8a0594a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 2 Jul 2026 08:27:50 +0000 Subject: [PATCH 168/284] feat: v2stain heat and amx --- .../heatmaps_and_max_aggregations/stain_base.yaml | 9 +++++++++ .../stain_mmci_sl_group_1.yaml | 6 ++++++ 2 files changed, 15 insertions(+) create mode 100644 configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_base.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_base.yaml b/configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_base.yaml new file mode 100644 index 00000000..0b286255 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_base.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /callbacks/heatmap_and_aggregator@callbacks + - /aggregator/max@aggregator + - /experiment/predict/embeddings/virchow2/stain_base + - _self_ + +use_case: "Heatmaps & Max. Aggregation" diff --git a/configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml b/configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..7eec4b1c --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/virchow2/heatmaps_and_max_aggregations/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ From 656e2ceac52bafc6a3f0f47ac6f9f88857a17328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 2 Jul 2026 14:09:50 +0000 Subject: [PATCH 169/284] fix: last slide --- prostate_cancer/callbacks/aggregator_callback.py | 1 + 1 file changed, 1 insertion(+) diff --git a/prostate_cancer/callbacks/aggregator_callback.py b/prostate_cancer/callbacks/aggregator_callback.py index 977f8328..60575710 100644 --- a/prostate_cancer/callbacks/aggregator_callback.py +++ b/prostate_cancer/callbacks/aggregator_callback.py @@ -78,6 +78,7 @@ def on_predict_dataloader_end( def on_predict_epoch_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule ) -> None: + super().on_predict_epoch_end(trainer, pl_module) df = pd.DataFrame(self.table) df.to_json("aggregated_predictions.json", orient="split") mlflow.log_artifact( From d1183e1405fc8701a758ccb364eea5113e9b63c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 2 Jul 2026 14:11:43 +0000 Subject: [PATCH 170/284] fix: super call --- prostate_cancer/callbacks/carcinoma_prediction_table_callback.py | 1 + prostate_cancer/callbacks/estimation_callback.py | 1 + prostate_cancer/callbacks/num_positive_callback.py | 1 + 3 files changed, 3 insertions(+) diff --git a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py index 6ff3c4de..c30b4877 100644 --- a/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py +++ b/prostate_cancer/callbacks/carcinoma_prediction_table_callback.py @@ -65,6 +65,7 @@ def on_predict_batch_end( def on_predict_epoch_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule ) -> None: + super().on_predict_epoch_end(trainer, pl_module) df = pd.DataFrame(self.table) df.to_json("carcinoma_prediction_table.json", orient="split") mlflow.log_artifact( diff --git a/prostate_cancer/callbacks/estimation_callback.py b/prostate_cancer/callbacks/estimation_callback.py index e06c3d98..2c6dcbfa 100644 --- a/prostate_cancer/callbacks/estimation_callback.py +++ b/prostate_cancer/callbacks/estimation_callback.py @@ -108,6 +108,7 @@ def on_predict_dataloader_end( def on_predict_epoch_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule ) -> None: + super().on_predict_epoch_end(trainer, pl_module) df = pd.DataFrame(self.table) df.to_json("aggregated_predictions.json", orient="split") mlflow.log_artifact( diff --git a/prostate_cancer/callbacks/num_positive_callback.py b/prostate_cancer/callbacks/num_positive_callback.py index d4ab5a6c..d05843b5 100644 --- a/prostate_cancer/callbacks/num_positive_callback.py +++ b/prostate_cancer/callbacks/num_positive_callback.py @@ -52,6 +52,7 @@ def on_predict_dataloader_end( def on_predict_epoch_end( self, trainer: pl.Trainer, pl_module: pl.LightningModule ) -> None: + super().on_predict_epoch_end(trainer, pl_module) df = pd.DataFrame(self.table) df.to_json("num_positive_preds.json", orient="split") mlflow.log_artifact( From b47a6929b004421348710e3f632c77d3cc90aab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 2 Jul 2026 14:14:16 +0000 Subject: [PATCH 171/284] feat: use main mlkit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b6a979ef..0c78e67e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "triton>=3.3.0", "tqdm==4.67.1", "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", - "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git@d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448", + "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git@68904dfd216ce19454e08a032f7e4dd978a85707", From 7db49fd183411c328da860048c4c70967ad38f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 2 Jul 2026 14:15:56 +0000 Subject: [PATCH 172/284] feat: update lock --- uv.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uv.lock b/uv.lock index 3f98d154..8b53308e 100644 --- a/uv.lock +++ b/uv.lock @@ -2429,7 +2429,7 @@ requires-dist = [ { name = "pandas", specifier = ">=2.2.3" }, { name = "pillow", specifier = "==11.3.0" }, { name = "rationai-masks", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" }, - { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git?rev=d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448" }, + { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git" }, { name = "rationai-sdk", git = "https://github.com/RationAI/rationai-sdk-python.git?rev=68904dfd216ce19454e08a032f7e4dd978a85707" }, { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" }, { name = "rationai-tiling", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" }, @@ -2910,7 +2910,7 @@ dependencies = [ [[package]] name = "rationai-mlkit" version = "0.4.1" -source = { git = "https://github.com/RationAI/mlkit.git?rev=d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448#d0fab0cf8aba8f2cf3cb34be50040d6b3b4d1448" } +source = { git = "https://github.com/RationAI/mlkit.git#db38501bb363cd3d76c6a3d31bec702f16002dd1" } dependencies = [ { name = "datasets" }, { name = "gitpython" }, From c0563c7d730d1b0f57cfddd06746c6c0f0f33b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 2 Jul 2026 17:35:29 +0000 Subject: [PATCH 173/284] feat: loader --- .../datasets/bag_of_embeddings_dataset.py | 21 +++++---- prostate_cancer/datamodule/datasets/base.py | 44 ------------------- 2 files changed, 10 insertions(+), 55 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index a7c337eb..957e7685 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -7,9 +7,9 @@ import torch import torch.nn.functional as F +from rationai.mlkit.data.datasets.slides_tiles_loader import SlidesTilesLoader from torch.utils.data import Dataset -from prostate_cancer.datamodule.datasets.base import download_artifacts from prostate_cancer.typing import ( LabeledBagOfTilesSample, SlideMetadata, @@ -30,15 +30,21 @@ def __init__( self.include_labels = carcinoma_roi_t is not None self.carcinoma_roi_t = carcinoma_roi_t - self.slides, self.tiles = download_artifacts(uris) + self._meta = SlidesTilesLoader(uris=uris) + self.slides = self._meta.slides + tiles = self._meta.tiles if self.include_labels: - self.tiles = self.tiles.map( + tiles = tiles.map( lambda r: { "carcinoma": (r["carcinoma_roi_percentage"] > self.carcinoma_roi_t) } ) + self.tiles = tiles + self._meta.tiles = tiles + # no need to re-build index after .map + self.padding = padding # compute max tiles per slide (HF version) @@ -46,11 +52,6 @@ def __init__( self.max_embeddings = max(Counter(slide_ids).values()) - self.tiles_by_slide: dict[str, list[int]] = {} - - for i, sid in enumerate(self.tiles["slide_id"]): - self.tiles_by_slide.setdefault(sid, []).append(i) - def __len__(self) -> int: return len(self.slides) @@ -58,9 +59,7 @@ def __getitem__(self, idx: int) -> T: slide_metadata = self.slides[idx] slide_name = Path(slide_metadata["path"]).stem - - tile_indices = self.tiles_by_slide[slide_metadata["id"]] - slide_tiles = self.tiles.select(tile_indices) + slide_tiles = self._meta.filter_tiles_by_slide(slide_metadata["slide_id"]) slide_embeddings = torch.tensor(slide_tiles["embedding"]) diff --git a/prostate_cancer/datamodule/datasets/base.py b/prostate_cancer/datamodule/datasets/base.py index 0c0744c4..4f91dc6f 100644 --- a/prostate_cancer/datamodule/datasets/base.py +++ b/prostate_cancer/datamodule/datasets/base.py @@ -3,10 +3,8 @@ from pathlib import Path from typing import TypeVar, cast -import mlflow from albumentations.core.composition import TransformType from datasets import Dataset as HFDataset -from datasets import concatenate_datasets from rationai.mlkit.data.datasets import MetaTiledSlides from torch.utils.data import Dataset @@ -24,48 +22,6 @@ def get_slide_name(slide_metadata: TilingSlideMetadata) -> str: return Path(slide_metadata["path"]).stem -def download_artifacts(tiling_uris: Iterable[str]) -> tuple[HFDataset, HFDataset]: - slide_dsets = [] - tile_dsets = [] - - for tiling_uri in tiling_uris: - root = Path(mlflow.artifacts.download_artifacts(tiling_uri)) - - flat_slides = root / "slides.parquet" - flat_tiles = root / "tiles.parquet" - - if flat_slides.exists(): - slide_dsets.append(HFDataset.from_parquet(str(flat_slides))) - - if flat_tiles.exists(): - tile_dsets.append(HFDataset.from_parquet(str(flat_tiles))) - - slide_folder = root / "slides" - tile_folder = root / "tiles" - - slide_files = ( - list(slide_folder.glob("*.parquet")) if slide_folder.exists() else [] - ) - tile_files = list(tile_folder.glob("*.parquet")) if tile_folder.exists() else [] - - if slide_files: - slide_dsets.append(HFDataset.from_parquet([str(p) for p in slide_files])) - - if tile_files: - tile_dsets.append(HFDataset.from_parquet([str(p) for p in tile_files])) - - if not slide_dsets: - raise ValueError("No slide parquet files found in MLflow artifacts") - - if not tile_dsets: - raise ValueError("No tile parquet files found in MLflow artifacts") - - slides = concatenate_datasets(slide_dsets) - tiles = concatenate_datasets(tile_dsets) - - return slides, tiles - - class BaseSingleSlideDataset(Dataset[LabeledTileSample | UnlabeledTileSample], ABC): def __init__( self, From b91ba018703646165a300e137df384f03b134be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 2 Jul 2026 20:06:05 +0000 Subject: [PATCH 174/284] fix: id --- .../datamodule/datasets/bag_of_embeddings_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index 957e7685..a6bb5825 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -59,7 +59,7 @@ def __getitem__(self, idx: int) -> T: slide_metadata = self.slides[idx] slide_name = Path(slide_metadata["path"]).stem - slide_tiles = self._meta.filter_tiles_by_slide(slide_metadata["slide_id"]) + slide_tiles = self._meta.filter_tiles_by_slide(slide_metadata["id"]) slide_embeddings = torch.tensor(slide_tiles["embedding"]) From f36850f48dccce6fac0b44192993d03283f497d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 3 Jul 2026 06:07:08 +0000 Subject: [PATCH 175/284] fix: tl t v2s --- configs/experiment/train/images/virchow2/base_stain_full.yaml | 2 +- configs/model/checkpoints/mmci_tl/virchow2_stain.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/experiment/train/images/virchow2/base_stain_full.yaml b/configs/experiment/train/images/virchow2/base_stain_full.yaml index 8630da76..05f58645 100644 --- a/configs/experiment/train/images/virchow2/base_stain_full.yaml +++ b/configs/experiment/train/images/virchow2/base_stain_full.yaml @@ -4,7 +4,7 @@ defaults: - /experiment/train/images/virchow2/base_stain - _self_ -model_name: "Virchow (Stain)" +model_name: "Virchow2 (Stain)" model: freeze_backbone: False diff --git a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml index bda458e6..109a2efb 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml @@ -2,7 +2,7 @@ defaults: - base_foundation - _self_ -tile_threshold: 0.5 +tile_threshold: 0.8 checkpoint: "mlflow-artifacts:/65/d39c26e7dd474d3bbdd3932f6f8bf2fb/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" public_checkpoint: null From c5659def4a794ffcb2986d9c1a0b15c3efc15865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 3 Jul 2026 06:15:24 +0000 Subject: [PATCH 176/284] feat: ps --- .../experiment/test/embeddings/pgp/stain_base.yaml | 12 ++++++++++++ .../test/embeddings/pgp/stain_tile_threshold.yaml | 14 ++++++++++++++ configs/model/checkpoints/mmci_tl/pgp_stain.yaml | 6 +----- 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 configs/experiment/test/embeddings/pgp/stain_base.yaml create mode 100644 configs/experiment/test/embeddings/pgp/stain_tile_threshold.yaml diff --git a/configs/experiment/test/embeddings/pgp/stain_base.yaml b/configs/experiment/test/embeddings/pgp/stain_base.yaml new file mode 100644 index 00000000..383dd335 --- /dev/null +++ b/configs/experiment/test/embeddings/pgp/stain_base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/embeddings/base + - /model/pgp_model@model + - /model/checkpoints/mmci_tl/pgp_stain@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_w_pgp_uri_224} diff --git a/configs/experiment/test/embeddings/pgp/stain_tile_threshold.yaml b/configs/experiment/test/embeddings/pgp/stain_tile_threshold.yaml new file mode 100644 index 00000000..e657c584 --- /dev/null +++ b/configs/experiment/test/embeddings/pgp/stain_tile_threshold.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/embeddings/pgp/stain_base + - /data/mmci/tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.9 # Pathologist's estimate for other models +use_case: "TL Threshold Estimation" diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml index c61747f0..eb3f8161 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -3,11 +3,7 @@ defaults: - _self_ tile_threshold: 0.5 -slide_max_threshold: 0.5 -slide_mpm_threshold: 0.5 -slide_top_k_threshold: 0.5 -slide_span_threshold: 0.5 # arbitrary - span predictions are binary -checkpoint: "mlflow-artifacts:/65/fad3ed3cceb448d3af6047d5b0b868fe/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" +checkpoint: "mlflow-artifacts:/65/4e6a272ec77c4deaa8c8b7b53a6295b2/artifacts/checkpoints/epoch=3-step=222233/checkpoint.ckpt" public_checkpoint: null model_name: "PGP (Stain)" From 1f527503bbae4e4e29fb0a18eced56bad3a163a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 3 Jul 2026 06:18:33 +0000 Subject: [PATCH 177/284] feat: ps heatmaps --- .../predict/embeddings/pgp/heatmaps/stain_base.yaml | 8 ++++++++ .../embeddings/pgp/heatmaps/stain_mmci_tl_val.yaml | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 configs/experiment/predict/embeddings/pgp/heatmaps/stain_base.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/heatmaps/stain_mmci_tl_val.yaml diff --git a/configs/experiment/predict/embeddings/pgp/heatmaps/stain_base.yaml b/configs/experiment/predict/embeddings/pgp/heatmaps/stain_base.yaml new file mode 100644 index 00000000..5cec48f5 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/heatmaps/stain_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/heatmap@callbacks + - /experiment/predict/embeddings/pgp/stain_base + - _self_ + +use_case: "Heatmaps" diff --git a/configs/experiment/predict/embeddings/pgp/heatmaps/stain_mmci_tl_val.yaml b/configs/experiment/predict/embeddings/pgp/heatmaps/stain_mmci_tl_val.yaml new file mode 100644 index 00000000..5cc5e63a --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/heatmaps/stain_mmci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/heatmaps/stain_base + - /data/mmci/tl_val@predict_data + - _self_ From 68a5bc19dff32981cdbbb86018222706d1b737ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 3 Jul 2026 07:09:22 +0000 Subject: [PATCH 178/284] feat: pgp tl t --- .../test/embeddings/pgp/test_suite/stain_base.yaml | 8 ++++++++ .../embeddings/pgp/test_suite/stain_mmci_tl_test.yaml | 6 ++++++ configs/model/checkpoints/mmci_tl/pgp_stain.yaml | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 configs/experiment/test/embeddings/pgp/test_suite/stain_base.yaml create mode 100644 configs/experiment/test/embeddings/pgp/test_suite/stain_mmci_tl_test.yaml diff --git a/configs/experiment/test/embeddings/pgp/test_suite/stain_base.yaml b/configs/experiment/test/embeddings/pgp/test_suite/stain_base.yaml new file mode 100644 index 00000000..c33d5655 --- /dev/null +++ b/configs/experiment/test/embeddings/pgp/test_suite/stain_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/embeddings/pgp/stain_base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/embeddings/pgp/test_suite/stain_mmci_tl_test.yaml b/configs/experiment/test/embeddings/pgp/test_suite/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..1caa862b --- /dev/null +++ b/configs/experiment/test/embeddings/pgp/test_suite/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/embeddings/pgp/test_suite/stain_base + - /data/mmci/tl_test@test_data + - _self_ diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml index eb3f8161..dd206160 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -2,7 +2,7 @@ defaults: - base_foundation - _self_ -tile_threshold: 0.5 +tile_threshold: 0.78 checkpoint: "mlflow-artifacts:/65/4e6a272ec77c4deaa8c8b7b53a6295b2/artifacts/checkpoints/epoch=3-step=222233/checkpoint.ckpt" public_checkpoint: null From 8bc23e985a5eeecce8b35b9a73eb84612b745876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 3 Jul 2026 10:42:07 +0000 Subject: [PATCH 179/284] feat: pgp stain --- .../stain_mmci_sl_group_1.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml diff --git a/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml b/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..c1a71e2c --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_mmci_sl_group_1.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/heatmaps_and_max_aggregations/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ From c0c90878df3e5c7c9f834eb7f08097f4b2a809e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 3 Jul 2026 18:00:39 +0000 Subject: [PATCH 180/284] feat: max aggr --- .../max_aggregations/pgp/base_stain.yaml | 5 +++++ .../max_aggregations/pgp/mmci_sl_group_1.yaml | 4 ++-- .../max_aggregations/pgp/stain_mmci_sl_group_1.yaml | 8 ++++++++ .../max_aggregations/virchow2/base_stain.yaml | 5 +++++ .../virchow2/stain_mmci_sl_group_1.yaml | 8 ++++++++ configs/model/checkpoints/mmci_tl/pgp_stain.yaml | 12 +++++++++++- .../model/checkpoints/mmci_tl/virchow2_stain.yaml | 10 ++++++++++ 7 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base_stain.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/base_stain.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/stain_mmci_sl_group_1.yaml diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base_stain.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base_stain.yaml new file mode 100644 index 00000000..e7204676 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base_stain.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/base + - /model/checkpoints/mmci_tl/pgp_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml index db74d6e0..1c0d71a3 100644 --- a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml @@ -1,8 +1,8 @@ # @package _global_ defaults: - - /experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base + - /experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base_stain - /data/mmci/sl_group_1@data - _self_ -preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} \ No newline at end of file +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1 diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..db74d6e0 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} \ No newline at end of file diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/base_stain.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/base_stain.yaml new file mode 100644 index 00000000..bec85f2e --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/base_stain.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/base + - /model/checkpoints/mmci_tl/virchow2_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/stain_mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..cf0f0721 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/stain_mmci_sl_group_1.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/virchow2/base_stain + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml index dd206160..890f18d8 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -6,4 +6,14 @@ tile_threshold: 0.78 checkpoint: "mlflow-artifacts:/65/4e6a272ec77c4deaa8c8b7b53a6295b2/artifacts/checkpoints/epoch=3-step=222233/checkpoint.ckpt" public_checkpoint: null -model_name: "PGP (Stain)" +model_name: "PGP (Stain & Frozen)" + +slide_level_prediction_uris: + mean_pool_max: + mmci_sl_group_1: null + max: + mmci_sl_group_1: "mlflow-artifacts:/65/c66862a470084ad5bd4b3a4a91808ba8/artifacts/tables/aggregated_predictions.json" + top_k: + mmci_sl_group_1: null + span: + mmci_sl_group_1: null diff --git a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml index 109a2efb..2d8ce1dc 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml @@ -8,3 +8,13 @@ checkpoint: "mlflow-artifacts:/65/d39c26e7dd474d3bbdd3932f6f8bf2fb/artifacts/che public_checkpoint: null model_name: "Virchow2 (Stain & Frozen)" + +slide_level_prediction_uris: + mean_pool_max: + mmci_sl_group_1: null + max: + mmci_sl_group_1: "mlflow-artifacts:/65/0356850470b4450bb24d426a7074f224/artifacts/tables/aggregated_predictions.json" + top_k: + mmci_sl_group_1: null + span: + mmci_sl_group_1: null From cab2ec09dc64fe04b856440b815348c30b67331c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 3 Jul 2026 18:17:32 +0000 Subject: [PATCH 181/284] feat: estimations --- .../pgp/stain_mmci_sl_group_1.yaml | 2 +- .../pgp/mean_pool_max_aggregations/base.yaml | 14 -------------- .../embeddings/pgp/stain_mpm_estimation.yaml | 18 ++++++++++++++++++ .../embeddings/pgp/stain_span_estimation.yaml | 19 +++++++++++++++++++ .../pgp/stain_top_k_estimation.yaml | 19 +++++++++++++++++++ .../pgp/top_k_aggregations/base.yaml | 15 --------------- .../mean_pool_max_aggregation/base.yaml | 14 -------------- .../virchow2/stain_mpm_estimation.yaml | 19 +++++++++++++++++++ .../virchow2/stain_span_estimation.yaml | 19 +++++++++++++++++++ .../virchow2/stain_top_k_estimation.yaml | 19 +++++++++++++++++++ .../virchow2/top_k_aggregations/base.yaml | 15 --------------- .../mmci_sl_group_1.yaml | 6 ------ .../stain_base.yaml | 9 --------- .../stain_mmci_sl_group_1.yaml | 6 ------ .../mean_pool_max_aggregations/base.yaml | 14 -------------- .../stain_base.yaml | 14 -------------- .../resnet50/top_k_aggregations/base.yaml | 15 --------------- .../top_k_aggregations/stain_base.yaml | 15 --------------- .../mean_pool_max_aggregations/base.yaml | 14 -------------- .../stain_base.yaml | 14 -------------- .../images/vgg16/top_k_aggregations/base.yaml | 15 --------------- .../vgg16/top_k_aggregations/stain_base.yaml | 15 --------------- .../model/checkpoints/mmci_tl/pgp_stain.yaml | 1 + .../checkpoints/mmci_tl/virchow2_stain.yaml | 1 + 24 files changed, 116 insertions(+), 196 deletions(-) delete mode 100644 configs/experiment/predict/embeddings/pgp/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml delete mode 100644 configs/experiment/predict/embeddings/pgp/top_k_aggregations/base.yaml delete mode 100644 configs/experiment/predict/embeddings/virchow2/mean_pool_max_aggregation/base.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml delete mode 100644 configs/experiment/predict/embeddings/virchow2/top_k_aggregations/base.yaml delete mode 100644 configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/mmci_sl_group_1.yaml delete mode 100644 configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_base.yaml delete mode 100644 configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_mmci_sl_group_1.yaml delete mode 100644 configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/base.yaml delete mode 100644 configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/stain_base.yaml delete mode 100644 configs/experiment/predict/images/resnet50/top_k_aggregations/base.yaml delete mode 100644 configs/experiment/predict/images/resnet50/top_k_aggregations/stain_base.yaml delete mode 100644 configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/base.yaml delete mode 100644 configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/stain_base.yaml delete mode 100644 configs/experiment/predict/images/vgg16/top_k_aggregations/base.yaml delete mode 100644 configs/experiment/predict/images/vgg16/top_k_aggregations/stain_base.yaml diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml index db74d6e0..c1d7d116 100644 --- a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/stain_mmci_sl_group_1.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base + - /experiment/postprocessing/slide_level_curves/max_aggregations/pgp/base_stain - /data/mmci/sl_group_1@data - _self_ diff --git a/configs/experiment/predict/embeddings/pgp/mean_pool_max_aggregations/base.yaml b/configs/experiment/predict/embeddings/pgp/mean_pool_max_aggregations/base.yaml deleted file mode 100644 index c6e39470..00000000 --- a/configs/experiment/predict/embeddings/pgp/mean_pool_max_aggregations/base.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/mean_pool_max@aggregator - - /experiment/predict/embeddings/pgp/base - - _self_ - -aggregator: - kernel_size: 1 - extent_tile: 224 - stride_tile: 112 - -use_case: "Mean Pool Max. Aggregation" diff --git a/configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml b/configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml new file mode 100644 index 00000000..0d70a8c8 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml @@ -0,0 +1,18 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/embeddings/pgp/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5, 6, 7] + +use_case: "Kernel Size Estimation (MPM Aggregator)" diff --git a/configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml b/configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml new file mode 100644 index 00000000..781fbfa7 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/embeddings/pgp/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator + static: + extent_tile: 224 + stride_tile: 112 + cell_threshold: ${model_data.tile_threshold} + to_estimate: + k: [2, 3, 4, 5] + +use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml b/configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml new file mode 100644 index 00000000..ecc34900 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/embeddings/pgp/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5] + k: [2, 3, 4] + +use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/embeddings/pgp/top_k_aggregations/base.yaml b/configs/experiment/predict/embeddings/pgp/top_k_aggregations/base.yaml deleted file mode 100644 index 9a21056f..00000000 --- a/configs/experiment/predict/embeddings/pgp/top_k_aggregations/base.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/top_k@aggregator - - /experiment/predict/embeddings/pgp/base - - _self_ - -aggregator: - kernel_size: 1 - extent_tile: 224 - stride_tile: 112 - k: 4 - -use_case: "Top-k Aggregation" diff --git a/configs/experiment/predict/embeddings/virchow2/mean_pool_max_aggregation/base.yaml b/configs/experiment/predict/embeddings/virchow2/mean_pool_max_aggregation/base.yaml deleted file mode 100644 index e3214d1c..00000000 --- a/configs/experiment/predict/embeddings/virchow2/mean_pool_max_aggregation/base.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/mean_pool_max@aggregator - - /experiment/predict/embeddings/virchow2/base - - _self_ - -aggregator: - kernel_size: 2 - extent_tile: 224 - stride_tile: 112 - -use_case: "Mean Pool Max. Aggregation" diff --git a/configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml new file mode 100644 index 00000000..13ffc0b6 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/embeddings/virchow2/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator + + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5, 6, 7] + +use_case: "Kernel Size Estimation (MPM Aggregator)" diff --git a/configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml new file mode 100644 index 00000000..1cd269bc --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/embeddings/virchow2/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator + static: + extent_tile: 224 + stride_tile: 112 + cell_threshold: ${model_data.tile_threshold} + to_estimate: + k: [2, 3, 4, 5] + +use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml new file mode 100644 index 00000000..e7ee4722 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/embeddings/virchow2/stain_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5] + k: [2, 3, 4] + +use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/embeddings/virchow2/top_k_aggregations/base.yaml b/configs/experiment/predict/embeddings/virchow2/top_k_aggregations/base.yaml deleted file mode 100644 index bcf2388e..00000000 --- a/configs/experiment/predict/embeddings/virchow2/top_k_aggregations/base.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/top_k@aggregator - - /experiment/predict/embeddings/virchow2/base - - _self_ - -aggregator: - kernel_size: 2 - extent_tile: 224 - stride_tile: 112 - k: 3 - -use_case: "Top-k Aggregation" diff --git a/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/mmci_sl_group_1.yaml deleted file mode 100644 index ee9f1aa7..00000000 --- a/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/mmci_sl_group_1.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/predict/images/google_vit/mean_pool_max_aggregations/base - - /data/mmci/sl_group_1@predict_data - - _self_ diff --git a/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_base.yaml b/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_base.yaml deleted file mode 100644 index 413ac660..00000000 --- a/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_base.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/mean_pool_max@aggregator - - /experiment/predict/images/google_vit/stain_base - - _self_ - -use_case: "MPM Aggregation" diff --git a/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_mmci_sl_group_1.yaml b/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_mmci_sl_group_1.yaml deleted file mode 100644 index 8ded7317..00000000 --- a/configs/experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_mmci_sl_group_1.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/predict/images/google_vit/mean_pool_max_aggregations/stain_base - - /data/mmci/sl_group_1@predict_data - - _self_ diff --git a/configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/base.yaml b/configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/base.yaml deleted file mode 100644 index e4c3c423..00000000 --- a/configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/base.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/mean_pool_max@aggregator - - /experiment/predict/images/resnet50/base - - _self_ - -aggregator: - kernel_size: 5 - extent_tile: 512 - stride_tile: 256 - -use_case: "Mean Pool Max. Aggregation" diff --git a/configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/stain_base.yaml b/configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/stain_base.yaml deleted file mode 100644 index 890425d8..00000000 --- a/configs/experiment/predict/images/resnet50/mean_pool_max_aggregations/stain_base.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/mean_pool_max@aggregator - - /experiment/predict/images/resnet50/stain_base - - _self_ - -aggregator: - kernel_size: 3 - extent_tile: 512 - stride_tile: 256 - -use_case: "Mean Pool Max. Aggregation" diff --git a/configs/experiment/predict/images/resnet50/top_k_aggregations/base.yaml b/configs/experiment/predict/images/resnet50/top_k_aggregations/base.yaml deleted file mode 100644 index e9c92f79..00000000 --- a/configs/experiment/predict/images/resnet50/top_k_aggregations/base.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/top_k@aggregator - - /experiment/predict/images/resnet50/base - - _self_ - -aggregator: - kernel_size: 4 - extent_tile: 512 - stride_tile: 256 - k: 4 - -use_case: "Top-k Aggregation" diff --git a/configs/experiment/predict/images/resnet50/top_k_aggregations/stain_base.yaml b/configs/experiment/predict/images/resnet50/top_k_aggregations/stain_base.yaml deleted file mode 100644 index dadad056..00000000 --- a/configs/experiment/predict/images/resnet50/top_k_aggregations/stain_base.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/top_k@aggregator - - /experiment/predict/images/resnet50/stain_base - - _self_ - -aggregator: - kernel_size: 1 - extent_tile: 512 - stride_tile: 256 - k: 4 - -use_case: "Top-k Aggregation" diff --git a/configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/base.yaml b/configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/base.yaml deleted file mode 100644 index 0646c325..00000000 --- a/configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/base.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/mean_pool_max@aggregator - - /experiment/predict/images/vgg16/base - - _self_ - -aggregator: - kernel_size: 1 - extent_tile: 512 - stride_tile: 256 - -use_case: "Mean Pool Max. Aggregation" diff --git a/configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/stain_base.yaml b/configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/stain_base.yaml deleted file mode 100644 index 94dc6768..00000000 --- a/configs/experiment/predict/images/vgg16/mean_pool_max_aggregations/stain_base.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/mean_pool_max@aggregator - - /experiment/predict/images/vgg16/stain_base - - _self_ - -aggregator: - kernel_size: 1 - extent_tile: 512 - stride_tile: 256 - -use_case: "Mean Pool Max. Aggregation" diff --git a/configs/experiment/predict/images/vgg16/top_k_aggregations/base.yaml b/configs/experiment/predict/images/vgg16/top_k_aggregations/base.yaml deleted file mode 100644 index 4c64259b..00000000 --- a/configs/experiment/predict/images/vgg16/top_k_aggregations/base.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/top_k@aggregator - - /experiment/predict/images/vgg16/base - - _self_ - -aggregator: - kernel_size: 1 - extent_tile: 512 - stride_tile: 256 - k: 4 - -use_case: "Top-k Aggregation" diff --git a/configs/experiment/predict/images/vgg16/top_k_aggregations/stain_base.yaml b/configs/experiment/predict/images/vgg16/top_k_aggregations/stain_base.yaml deleted file mode 100644 index d3af4277..00000000 --- a/configs/experiment/predict/images/vgg16/top_k_aggregations/stain_base.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator@callbacks - - /aggregator/top_k@aggregator - - /experiment/predict/images/vgg16/stain_base - - _self_ - -aggregator: - kernel_size: 1 - extent_tile: 512 - stride_tile: 256 - k: 2 - -use_case: "Top-k Aggregation" diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml index 890f18d8..8463d571 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -3,6 +3,7 @@ defaults: - _self_ tile_threshold: 0.78 +slide_max_threshold: 0.9951838851 checkpoint: "mlflow-artifacts:/65/4e6a272ec77c4deaa8c8b7b53a6295b2/artifacts/checkpoints/epoch=3-step=222233/checkpoint.ckpt" public_checkpoint: null diff --git a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml index 2d8ce1dc..430d3260 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml @@ -3,6 +3,7 @@ defaults: - _self_ tile_threshold: 0.8 +slide_max_threshold: 0.9993972778 checkpoint: "mlflow-artifacts:/65/d39c26e7dd474d3bbdd3932f6f8bf2fb/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" public_checkpoint: null From 39144ad95294a134432c7a0f36c213935bcf59be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 4 Jul 2026 15:56:52 +0000 Subject: [PATCH 182/284] feat: up to topk --- .../postprocessing/eval_estimation/mpm/pgp_stain.yaml | 7 +++++++ .../eval_estimation/mpm/virchow2_stain.yaml | 7 +++++++ .../postprocessing/eval_estimation/top_k/pgp_stain.yaml | 7 +++++++ .../eval_estimation/top_k/virchow2_stain.yaml | 7 +++++++ .../mean_pool_max_aggregations/pgp/base_stain.yaml | 4 ++++ .../pgp/stain_mmci_sl_group_1.yaml | 9 +++++++++ .../mean_pool_max_aggregations/virchow2/base_stain.yaml | 4 ++++ .../virchow2/stain_mmci_sl_group_1.yaml | 9 +++++++++ .../top_k_aggregations/pgp/base_stain.yaml | 4 ++++ .../top_k_aggregations/pgp/stain_mmci_sl_group_1.yaml | 9 +++++++++ .../top_k_aggregations/virchow2/base_stain.yaml | 4 ++++ .../virchow2/stain_mmci_sl_group_1.yaml | 9 +++++++++ configs/model/checkpoints/mmci_tl/pgp_stain.yaml | 9 ++++++--- configs/model/checkpoints/mmci_tl/virchow2_stain.yaml | 9 ++++++--- 14 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 configs/experiment/postprocessing/eval_estimation/mpm/pgp_stain.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/mpm/virchow2_stain.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/top_k/pgp_stain.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/top_k/virchow2_stain.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/base_stain.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/stain_mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/base_stain.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/stain_mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/base_stain.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/stain_mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/base_stain.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/stain_mmci_sl_group_1.yaml diff --git a/configs/experiment/postprocessing/eval_estimation/mpm/pgp_stain.yaml b/configs/experiment/postprocessing/eval_estimation/mpm/pgp_stain.yaml new file mode 100644 index 00000000..18f5507a --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/mpm/pgp_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/mpm/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/pgp_stain@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/mpm/virchow2_stain.yaml b/configs/experiment/postprocessing/eval_estimation/mpm/virchow2_stain.yaml new file mode 100644 index 00000000..e1c27082 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/mpm/virchow2_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/mpm/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/virchow2_stain@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/top_k/pgp_stain.yaml b/configs/experiment/postprocessing/eval_estimation/top_k/pgp_stain.yaml new file mode 100644 index 00000000..de61b65f --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/top_k/pgp_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/top_k/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/pgp_stain@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/top_k/virchow2_stain.yaml b/configs/experiment/postprocessing/eval_estimation/top_k/virchow2_stain.yaml new file mode 100644 index 00000000..b98c0794 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/top_k/virchow2_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/top_k/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/virchow2_stain@model_data + - _self_ diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/base_stain.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/base_stain.yaml new file mode 100644 index 00000000..c2e341ee --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/base_stain.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/pgp_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/stain_mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..dcc9b88e --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/stain_mmci_sl_group_1.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/pgp/base_stain + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} +pred_column: "pred_kernel_size=3" diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/base_stain.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/base_stain.yaml new file mode 100644 index 00000000..b454e354 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/base_stain.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/virchow2_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/stain_mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..4a0f4577 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/stain_mmci_sl_group_1.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/virchow2/base_stain + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} +pred_column: "pred_kernel_size=2" diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/base_stain.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/base_stain.yaml new file mode 100644 index 00000000..c2e341ee --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/base_stain.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/pgp_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/stain_mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..628cc0a0 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/stain_mmci_sl_group_1.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/top_k_aggregations/pgp/base_stain + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} +pred_column: "pred_kernel_size=2_k=4" diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/base_stain.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/base_stain.yaml new file mode 100644 index 00000000..b454e354 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/base_stain.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/virchow2_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/stain_mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/stain_mmci_sl_group_1.yaml new file mode 100644 index 00000000..e2e6e918 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/stain_mmci_sl_group_1.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/top_k_aggregations/virchow2/base_stain + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} +pred_column: "pred_kernel_size=2_k=2" diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml index 8463d571..cc6c3208 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -4,6 +4,9 @@ defaults: tile_threshold: 0.78 slide_max_threshold: 0.9951838851 +slide_mpm_threshold: 0.8654243946 +slide_top_k_threshold: 0.911498785 +slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/4e6a272ec77c4deaa8c8b7b53a6295b2/artifacts/checkpoints/epoch=3-step=222233/checkpoint.ckpt" public_checkpoint: null @@ -11,10 +14,10 @@ model_name: "PGP (Stain & Frozen)" slide_level_prediction_uris: mean_pool_max: - mmci_sl_group_1: null + mmci_sl_group_1: "mlflow-artifacts:/65/38dce5a3317544768fa85e8e1597b8fc/artifacts/tables/aggregated_predictions.json" max: mmci_sl_group_1: "mlflow-artifacts:/65/c66862a470084ad5bd4b3a4a91808ba8/artifacts/tables/aggregated_predictions.json" top_k: - mmci_sl_group_1: null + mmci_sl_group_1: "mlflow-artifacts:/65/4b9ecc7d18904d8b89a053b7422102d0/artifacts/tables/aggregated_predictions.json" span: - mmci_sl_group_1: null + mmci_sl_group_1: "mlflow-artifacts:/65/f9a3a966368e4c0197bf60d80aa7ea8d/artifacts/tables/aggregated_predictions.json" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml index 430d3260..22957025 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml @@ -4,6 +4,9 @@ defaults: tile_threshold: 0.8 slide_max_threshold: 0.9993972778 +slide_mpm_threshold: 0.9899944067 +slide_top_k_threshold: 0.9723860025 +slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/d39c26e7dd474d3bbdd3932f6f8bf2fb/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" public_checkpoint: null @@ -12,10 +15,10 @@ model_name: "Virchow2 (Stain & Frozen)" slide_level_prediction_uris: mean_pool_max: - mmci_sl_group_1: null + mmci_sl_group_1: "mlflow-artifacts:/65/3f4b07e4bde34f12a4ed9461471ff9f2/artifacts/tables/aggregated_predictions.json" max: mmci_sl_group_1: "mlflow-artifacts:/65/0356850470b4450bb24d426a7074f224/artifacts/tables/aggregated_predictions.json" top_k: - mmci_sl_group_1: null + mmci_sl_group_1: "mlflow-artifacts:/65/5db07d221fca470e83e8af2aa1526bcd/artifacts/tables/aggregated_predictions.json" span: - mmci_sl_group_1: null + mmci_sl_group_1: "mlflow-artifacts:/65/cd2c46aecfa24d84ad023c78295caac7/artifacts/tables/aggregated_predictions.json" From f534822ccf751b2e37678d57292879b14170bc04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 4 Jul 2026 16:43:39 +0000 Subject: [PATCH 183/284] feat: span --- .../postprocessing/eval_estimation/span/pgp_stain.yaml | 7 +++++++ .../eval_estimation/span/virchow2_stain.yaml | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 configs/experiment/postprocessing/eval_estimation/span/pgp_stain.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/span/virchow2_stain.yaml diff --git a/configs/experiment/postprocessing/eval_estimation/span/pgp_stain.yaml b/configs/experiment/postprocessing/eval_estimation/span/pgp_stain.yaml new file mode 100644 index 00000000..89635dee --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/span/pgp_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/span/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/pgp_stain@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/span/virchow2_stain.yaml b/configs/experiment/postprocessing/eval_estimation/span/virchow2_stain.yaml new file mode 100644 index 00000000..a53a403e --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/span/virchow2_stain.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/span/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/virchow2_stain@model_data + - _self_ From d8fd4aaf3a1a5fddcefd54682b2bdfc02d3a8f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 4 Jul 2026 18:48:32 +0000 Subject: [PATCH 184/284] feat: evals --- .../postprocessing/slide_level_eval/pgp_stain/base.yaml | 4 ++++ .../slide_level_eval/pgp_stain/max_aggregations/base.yaml | 8 ++++++++ .../pgp_stain/max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../pgp_stain/max_aggregations/mmci_tl_test.yaml | 7 +++++++ .../pgp_stain/mean_pool_max_aggregations/base.yaml | 8 ++++++++ .../mean_pool_max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../pgp_stain/span_aggregations/base.yaml | 8 ++++++++ .../pgp_stain/span_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../pgp_stain/top_k_aggregations/base.yaml | 8 ++++++++ .../pgp_stain/top_k_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../slide_level_eval/virchow2_stain/base.yaml | 4 ++++ .../virchow2_stain/max_aggregations/base.yaml | 8 ++++++++ .../virchow2_stain/max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../virchow2_stain/max_aggregations/mmci_tl_test.yaml | 7 +++++++ .../virchow2_stain/mean_pool_max_aggregations/base.yaml | 8 ++++++++ .../mean_pool_max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../virchow2_stain/span_aggregations/base.yaml | 8 ++++++++ .../virchow2_stain/span_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../virchow2_stain/top_k_aggregations/base.yaml | 8 ++++++++ .../top_k_aggregations/mmci_sl_group_1.yaml | 7 +++++++ 20 files changed, 142 insertions(+) create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/mmci_sl_group_1.yaml diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/base.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/base.yaml new file mode 100644 index 00000000..c2e341ee --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/base.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/pgp_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/base.yaml new file mode 100644 index 00000000..2b0307b8 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/base + +aggregation_method: "Max" +pred_column: "prediction" +t: ${model_data.slide_max_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..8fbef951 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml new file mode 100644 index 00000000..959af99e --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/base + - /data/mmci/tl_test@data + +preds_uri: "mlflow-artifacts:/65/001dd3914dc04fa48f9eb98784709c4d/artifacts/tables/aggregated_predictions.json" diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/base.yaml new file mode 100644 index 00000000..a6f37bde --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/base + +aggregation_method: "Mean Pool Max" +pred_column: "pred_kernel_size=3" +t: ${model_data.slide_mpm_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..7da43f6a --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/mean_pool_max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/base.yaml new file mode 100644 index 00000000..7b4c7f99 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/base + +aggregation_method: "Span" +pred_column: "pred_k=5" +t: ${model_data.slide_span_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..0bcda981 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/span_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.span.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/base.yaml new file mode 100644 index 00000000..f77c11d4 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/base + +aggregation_method: "Top-k" +pred_column: "pred_kernel_size=2_k=4" +t: ${model_data.slide_top_k_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..9efd12ec --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/pgp_stain/top_k_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/base.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/base.yaml new file mode 100644 index 00000000..b454e354 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/base.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/virchow2_stain@model_data diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/base.yaml new file mode 100644 index 00000000..5bae572c --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/base + +aggregation_method: "Max" +pred_column: "prediction" +t: ${model_data.slide_max_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..c661649f --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml new file mode 100644 index 00000000..adfb82ee --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/base + - /data/mmci/tl_test@data + +preds_uri: "mlflow-artifacts:/65/9f48b149031743529ba456c236e4aee5/artifacts/tables/aggregated_predictions.json" diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/base.yaml new file mode 100644 index 00000000..1bbc9c82 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/base + +aggregation_method: "Mean Pool Max" +pred_column: "pred_kernel_size=2" +t: ${model_data.slide_mpm_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..50ab94e1 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/mean_pool_max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/base.yaml new file mode 100644 index 00000000..802d50f9 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/base + +aggregation_method: "Span" +pred_column: "pred_k=5" +t: ${model_data.slide_span_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..3c7a76d7 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/span_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.span.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/base.yaml new file mode 100644 index 00000000..54439ded --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/base + +aggregation_method: "Top-k" +pred_column: "pred_kernel_size=2_k=2" +t: ${model_data.slide_top_k_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..d978d978 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/virchow2_stain/top_k_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} From b44032e0071058d9123d4a3d197131d80182e0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 5 Jul 2026 07:08:28 +0000 Subject: [PATCH 185/284] feat: sl test tl test --- .../embeddings/pgp/max_aggregations/stain_base.yaml | 9 +++++++++ .../pgp/max_aggregations/stain_mmci_tl_test.yaml | 6 ++++++ .../embeddings/virchow2/max_aggregations/stain_base.yaml | 9 +++++++++ .../virchow2/max_aggregations/stain_mmci_tl_test.yaml | 6 ++++++ 4 files changed, 30 insertions(+) create mode 100644 configs/experiment/predict/embeddings/pgp/max_aggregations/stain_base.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/max_aggregations/stain_mmci_tl_test.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_base.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_mmci_tl_test.yaml diff --git a/configs/experiment/predict/embeddings/pgp/max_aggregations/stain_base.yaml b/configs/experiment/predict/embeddings/pgp/max_aggregations/stain_base.yaml new file mode 100644 index 00000000..5345e163 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/max_aggregations/stain_base.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator@callbacks + - /aggregator/max@aggregator + - /experiment/predict/embeddings/pgp/stain_base + - _self_ + +use_case: "Max. Aggregation" diff --git a/configs/experiment/predict/embeddings/pgp/max_aggregations/stain_mmci_tl_test.yaml b/configs/experiment/predict/embeddings/pgp/max_aggregations/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..34ae09d3 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/max_aggregations/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/max_aggregations/stain_base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_base.yaml b/configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_base.yaml new file mode 100644 index 00000000..870f5ed9 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_base.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator@callbacks + - /aggregator/max@aggregator + - /experiment/predict/embeddings/virchow2/stain_base + - _self_ + +use_case: "Max. Aggregation" diff --git a/configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_mmci_tl_test.yaml b/configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..90035b77 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/max_aggregations/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/virchow2/max_aggregations/stain_base + - /data/mmci/tl_test@predict_data + - _self_ From f7bb19d158fcd4eb9e6a2fc5fbab1ed67e0860d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 5 Jul 2026 07:35:17 +0000 Subject: [PATCH 186/284] feat: mmci tl eval --- .../pgp_stain/max_aggregations/mmci_tl_test.yaml | 2 +- .../virchow2_stain/max_aggregations/mmci_tl_test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml index 959af99e..cdc7843e 100644 --- a/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml +++ b/configs/experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/mmci_tl_test.yaml @@ -4,4 +4,4 @@ defaults: - /experiment/postprocessing/slide_level_eval/pgp_stain/max_aggregations/base - /data/mmci/tl_test@data -preds_uri: "mlflow-artifacts:/65/001dd3914dc04fa48f9eb98784709c4d/artifacts/tables/aggregated_predictions.json" +preds_uri: "mlflow-artifacts:/65/fd46c62186c44f599d6021bb7e394b23/artifacts/tables/aggregated_predictions.json" diff --git a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml index adfb82ee..a0f325a2 100644 --- a/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml +++ b/configs/experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/mmci_tl_test.yaml @@ -4,4 +4,4 @@ defaults: - /experiment/postprocessing/slide_level_eval/virchow2_stain/max_aggregations/base - /data/mmci/tl_test@data -preds_uri: "mlflow-artifacts:/65/9f48b149031743529ba456c236e4aee5/artifacts/tables/aggregated_predictions.json" +preds_uri: "mlflow-artifacts:/65/ab793fef8da2486aa443f6da26188561/artifacts/tables/aggregated_predictions.json" From 80cf5fe4c9911e173ee6da75164ab14fa0f2b24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 5 Jul 2026 07:53:26 +0000 Subject: [PATCH 187/284] feat: update non inferiority --- notebooks/eval/non_inferiority.ipynb | 312 +++++++++++++++++++++++---- 1 file changed, 265 insertions(+), 47 deletions(-) diff --git a/notebooks/eval/non_inferiority.ipynb b/notebooks/eval/non_inferiority.ipynb index e239c73c..dd6c7a45 100644 --- a/notebooks/eval/non_inferiority.ipynb +++ b/notebooks/eval/non_inferiority.ipynb @@ -8,16 +8,6 @@ "# Non-Inferiority of Max Aggregation" ] }, - { - "cell_type": "markdown", - "id": "e64ac033-0f0a-429d-b558-d4767472bb01", - "metadata": { - "jp-MarkdownHeadingCollapsed": true - }, - "source": [ - "## Fetch Data" - ] - }, { "cell_type": "markdown", "id": "e966e12e-5a72-4f10-81a0-c807cf6167e1", @@ -30,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "f6642d0e-0966-42a0-8348-dfce7b415dc2", "metadata": {}, "outputs": [], @@ -54,6 +44,24 @@ " \"pred_k=4\",\n", " ),\n", " },\n", + " \"pgp_stain\": {\n", + " \"max\": (\n", + " \"mlflow-artifacts:/65/c66862a470084ad5bd4b3a4a91808ba8/artifacts/tables/aggregated_predictions.json\",\n", + " \"prediction\",\n", + " ),\n", + " \"mpm\": (\n", + " \"mlflow-artifacts:/65/38dce5a3317544768fa85e8e1597b8fc/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=3\",\n", + " ),\n", + " \"topk\": (\n", + " \"mlflow-artifacts:/65/4b9ecc7d18904d8b89a053b7422102d0/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=2_k=4\",\n", + " ),\n", + " \"span\": (\n", + " \"mlflow-artifacts:/65/f9a3a966368e4c0197bf60d80aa7ea8d/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_k=5\",\n", + " ),\n", + " },\n", " \"virchow2\": {\n", " \"max\": (\n", " \"mlflow-artifacts:/65/55c83a014afa4d65979b0c549cbe5397/artifacts/tables/aggregated_predictions_corrected.json\",\n", @@ -72,6 +80,24 @@ " \"pred_k=5\",\n", " ),\n", " },\n", + " \"virchow2_stain\": {\n", + " \"max\": (\n", + " \"mlflow-artifacts:/65/0356850470b4450bb24d426a7074f224/artifacts/tables/aggregated_predictions.json\",\n", + " \"prediction\",\n", + " ),\n", + " \"mpm\": (\n", + " \"mlflow-artifacts:/65/3f4b07e4bde34f12a4ed9461471ff9f2/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=2\",\n", + " ),\n", + " \"topk\": (\n", + " \"mlflow-artifacts:/65/5db07d221fca470e83e8af2aa1526bcd/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=2_k=2\",\n", + " ),\n", + " \"span\": (\n", + " \"mlflow-artifacts:/65/cd2c46aecfa24d84ad023c78295caac7/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_k=5\",\n", + " ),\n", + " },\n", " \"resnet50\": {\n", " \"max\": (\n", " \"mlflow-artifacts:/65/94561b6417ea4f888495fdcb799555e2/artifacts/tables/aggregated_predictions_corrected.json\",\n", @@ -203,40 +229,46 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/jovyan/prostate-cancer/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 898.14it/s] \n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 126.00it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 114.25it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 119.86it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 118.40it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 109.98it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 118.01it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 117.58it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 115.82it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 108.30it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 118.35it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 129.00it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 118.43it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 128.62it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 117.74it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 137.37it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 132.27it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 119.79it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 78.48it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 132.64it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 131.86it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 133.20it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 123.05it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 150.36it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 135.70it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 143.62it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 127.69it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 139.79it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 125.83it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 130.78it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 113.19it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 137.25it/s]\n" + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 53.89it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 43.36it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 40.78it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 50.16it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 48.69it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.31it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 42.49it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 42.13it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 58.64it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 48.73it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 37.54it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 48.54it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.72it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 41.15it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 41.69it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 123.36it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.48it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 62.81it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 55.45it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.05it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 49.42it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 38.48it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 26.07it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 47.28it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 34.40it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 41.97it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 24.47it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 35.65it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 34.76it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 65.69it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 46.21it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 45.24it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 42.87it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 35.36it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 47.84it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 38.83it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 14.45it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.24it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 37.00it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.78it/s]\n" ] } ], @@ -252,7 +284,7 @@ " return mlflow.artifacts.download_artifacts(uri)\n", "\n", "\n", - "assert len(DATA) == 8 # 8 models\n", + "assert len(DATA) == 10 # 10 models\n", "\n", "for model_name, methods in DATA.items():\n", " assert len(methods) == 4 # 4 aggr. methods\n", @@ -419,7 +451,9 @@ "margin = 0.02 # We consider max acceptable if it is not worse by more than 0.02 AUC\n", "\n", "pgp_df = evaluate_single_model(master_df, \"pgp\", margin)\n", + "pgp_stain_df = evaluate_single_model(master_df, \"pgp_stain\", margin)\n", "virchow2_df = evaluate_single_model(master_df, \"virchow2\", margin)\n", + "virchow2_stain_df = evaluate_single_model(master_df, \"virchow2_stain\", margin)\n", "resnet50_df = evaluate_single_model(master_df, \"resnet50\", margin)\n", "resnet50_stain_df = evaluate_single_model(master_df, \"resnet50_stain\", margin)\n", "vgg16_df = evaluate_single_model(master_df, \"vgg16\", margin)\n", @@ -518,6 +552,98 @@ { "cell_type": "code", "execution_count": 8, + "id": "f7d47480-e78e-4c76-9441-a5bc4a593fd6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mean_deltaci_95_lowerci_95_upperupper_95non_inferiormodelmethod
0-0.001316-0.0047360.0017610.001203Truepgp_stainmpm
10.000212-0.0020450.0025960.002136Truepgp_staintopk
2-0.054213-0.077325-0.032631-0.036119Truepgp_stainspan
\n", + "
" + ], + "text/plain": [ + " mean_delta ci_95_lower ci_95_upper upper_95 non_inferior model \\\n", + "0 -0.001316 -0.004736 0.001761 0.001203 True pgp_stain \n", + "1 0.000212 -0.002045 0.002596 0.002136 True pgp_stain \n", + "2 -0.054213 -0.077325 -0.032631 -0.036119 True pgp_stain \n", + "\n", + " method \n", + "0 mpm \n", + "1 topk \n", + "2 span " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pgp_stain_df" + ] + }, + { + "cell_type": "code", + "execution_count": 9, "id": "3d520800-b28c-4d39-8c28-a41ae5089b81", "metadata": {}, "outputs": [ @@ -598,7 +724,7 @@ "2 span " ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -607,6 +733,98 @@ "virchow2_df" ] }, + { + "cell_type": "code", + "execution_count": 10, + "id": "8ca76c1c-56ae-405e-89a6-e72ff2542a73", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mean_deltaci_95_lowerci_95_upperupper_95non_inferiormodelmethod
00.001005-0.0016720.0038460.003306Truevirchow2_stainmpm
10.001402-0.0012200.0042310.003709Truevirchow2_staintopk
2-0.066170-0.089964-0.044106-0.047552Truevirchow2_stainspan
\n", + "
" + ], + "text/plain": [ + " mean_delta ci_95_lower ci_95_upper upper_95 non_inferior \\\n", + "0 0.001005 -0.001672 0.003846 0.003306 True \n", + "1 0.001402 -0.001220 0.004231 0.003709 True \n", + "2 -0.066170 -0.089964 -0.044106 -0.047552 True \n", + "\n", + " model method \n", + "0 virchow2_stain mpm \n", + "1 virchow2_stain topk \n", + "2 virchow2_stain span " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "virchow2_stain_df" + ] + }, { "cell_type": "code", "execution_count": 9, From a6c0a8c78d43aa58634e08f2c3af135ca43b52a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 7 Jul 2026 08:48:47 +0000 Subject: [PATCH 188/284] feat: normalization pipeline --- .../datamodule/datasets/images/labeled.yaml | 1 + .../datamodule/datasets/images/unlabeled.yaml | 1 + .../datamodule/transforms/augmentations.yaml | 38 ------------------- .../transforms/stain_normalize.yaml | 18 +++++++++ .../datamodule/transforms/__init__.py | 0 .../transforms/stain_normalization.py | 22 +++++++++++ 6 files changed, 42 insertions(+), 38 deletions(-) delete mode 100644 configs/datamodule/transforms/augmentations.yaml create mode 100644 configs/datamodule/transforms/stain_normalize.yaml create mode 100644 prostate_cancer/datamodule/transforms/__init__.py create mode 100644 prostate_cancer/datamodule/transforms/stain_normalization.py diff --git a/configs/datamodule/datasets/images/labeled.yaml b/configs/datamodule/datasets/images/labeled.yaml index f3a5b9a9..ce8f4854 100644 --- a/configs/datamodule/datasets/images/labeled.yaml +++ b/configs/datamodule/datasets/images/labeled.yaml @@ -1,4 +1,5 @@ _target_: prostate_cancer.datamodule.datasets.LabeledTilesDataset uris: ??? +transforms: ??? carcinoma_roi_t: ${carcinoma_roi_t} stratified_filter: ??? diff --git a/configs/datamodule/datasets/images/unlabeled.yaml b/configs/datamodule/datasets/images/unlabeled.yaml index dfe019b3..8278ab13 100644 --- a/configs/datamodule/datasets/images/unlabeled.yaml +++ b/configs/datamodule/datasets/images/unlabeled.yaml @@ -1,2 +1,3 @@ _target_: prostate_cancer.datamodule.datasets.UnlabeledTilesDataset uris: ??? +transforms: ??? diff --git a/configs/datamodule/transforms/augmentations.yaml b/configs/datamodule/transforms/augmentations.yaml deleted file mode 100644 index bbcd47ba..00000000 --- a/configs/datamodule/transforms/augmentations.yaml +++ /dev/null @@ -1,38 +0,0 @@ -_target_: albumentations.Compose -transforms: - - _target_: albumentations.RandomRotate90 - p: 1.0 - - _target_: albumentations.HorizontalFlip - p: 0.5 - - _target_: albumentations.VerticalFlip - p: 0.5 - - _target_: albumentations.RandomGamma - p: 0.5 - - _target_: albumentations.RandomBrightnessContrast - p: 0.5 - - _target_: albumentations.HueSaturationValue - p: 0.5 - - - _target_: rationai.staining.AugmentStainingTransform - - conversion: - _target_: rationai.staining.ColorConversion.from_stain_vectors - stain1: [0.479, 0.823, 0.305] - stain2: [0.156, 0.949, 0.273] - stain3: [-0.189, -0.242, 0.952] - - noise_transform: - _target_: albumentations.Compose - transforms: - - _target_: albumentations.MultiplicativeNoise - multiplier: [0.5, 1.5] - per_channel: true - elementwise: false - p: 1.0 - - - _target_: albumentations.AdditiveNoise - noise_type: uniform - noise_params: - ranges: - - [-0.005, 0.005] - p: 1.0 diff --git a/configs/datamodule/transforms/stain_normalize.yaml b/configs/datamodule/transforms/stain_normalize.yaml new file mode 100644 index 00000000..c275e375 --- /dev/null +++ b/configs/datamodule/transforms/stain_normalize.yaml @@ -0,0 +1,18 @@ +defaults: + - /data/mmci/base@mmci_stains + - _self_ + +_target_: albumentations.Compose +transforms: + - _target_: prostate_cancer.transforms.stain_normalization.build_normalize_staining_transform + stain1: ${stains.hematoxylin} + stain2: ${stains.eosin} + stain3: ${stains.residual} + target_stain1: ${mmci_stains.stains.hematoxylin} + target_stain2: ${mmci_stains.stains.eosin} + target_stain3: ${mmci_stains.stains.residual} + + - _target_: albumentations.Normalize + mean: ${mean} + std: ${std} + max_pixel_value: 1 diff --git a/prostate_cancer/datamodule/transforms/__init__.py b/prostate_cancer/datamodule/transforms/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prostate_cancer/datamodule/transforms/stain_normalization.py b/prostate_cancer/datamodule/transforms/stain_normalization.py new file mode 100644 index 00000000..4b3fd33d --- /dev/null +++ b/prostate_cancer/datamodule/transforms/stain_normalization.py @@ -0,0 +1,22 @@ +from collections.abc import Sequence + +from rationai.staining import ColorConversion, NormalizeStainingTransform + + +def build_normalize_staining_transform( + stain1: Sequence[float], + stain2: Sequence[float], + stain3: Sequence[float], + target_stain1: Sequence[float], + target_stain2: Sequence[float], + target_stain3: Sequence[float], +) -> NormalizeStainingTransform: + conversion = ColorConversion.from_stain_vectors( + tuple(stain1), tuple(stain2), tuple(stain3) + ) + return NormalizeStainingTransform( + rgb2stain=conversion.matrix, + target_stain1=tuple(target_stain1), + target_stain2=tuple(target_stain2), + target_stain3=tuple(target_stain3), + ) From 61647bee1d7513dcd799155bbe8b89eaca3274e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 7 Jul 2026 09:29:02 +0000 Subject: [PATCH 189/284] feat: add public checkpoints --- configs/model/checkpoints/mmci_tl/google_vit.yaml | 2 ++ configs/model/checkpoints/mmci_tl/pgp_stain.yaml | 3 ++- configs/model/checkpoints/mmci_tl/virchow2_stain.yaml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/google_vit.yaml b/configs/model/checkpoints/mmci_tl/google_vit.yaml index db3f6e1d..89c9323a 100644 --- a/configs/model/checkpoints/mmci_tl/google_vit.yaml +++ b/configs/model/checkpoints/mmci_tl/google_vit.yaml @@ -9,6 +9,8 @@ slide_top_k_threshold: 0.9998286963 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/4b7c1663713b4dd0b03063c9f9b60925/artifacts/checkpoints/epoch=0-step=55556/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/gvit_checkpoint.ckpt" + model_name: "Google ViT" slide_level_prediction_uris: diff --git a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml index cc6c3208..20c360b7 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_stain.yaml @@ -9,7 +9,8 @@ slide_top_k_threshold: 0.911498785 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/4e6a272ec77c4deaa8c8b7b53a6295b2/artifacts/checkpoints/epoch=3-step=222233/checkpoint.ckpt" -public_checkpoint: null +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/pgp_stain_checkpoint.ckpt" + model_name: "PGP (Stain & Frozen)" slide_level_prediction_uris: diff --git a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml index 22957025..38fa1d65 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_stain.yaml @@ -9,7 +9,7 @@ slide_top_k_threshold: 0.9723860025 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/d39c26e7dd474d3bbdd3932f6f8bf2fb/artifacts/checkpoints/epoch=0-step=27778/checkpoint.ckpt" -public_checkpoint: null +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/virchow2_stain_checkpoint.ckpt" model_name: "Virchow2 (Stain & Frozen)" From bb5a8f9aaaaf41f732f99bdff53079c8fa726082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 7 Jul 2026 09:35:49 +0000 Subject: [PATCH 190/284] feat: normalized test vgg16 --- .../experiment/test/images/normalized_base.yaml | 16 ++++++++++++++++ .../test/images/vgg16/normalized_base.yaml | 8 ++++++++ .../images/vgg16/test_suite/normalized_base.yaml | 8 ++++++++ .../test_suite/normalized_radboud_test.yaml | 8 ++++++++ .../images/vgg16/test_suite/radboud_test.yaml | 8 ++++++++ 5 files changed, 48 insertions(+) create mode 100644 configs/experiment/test/images/normalized_base.yaml create mode 100644 configs/experiment/test/images/vgg16/normalized_base.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/radboud_test.yaml diff --git a/configs/experiment/test/images/normalized_base.yaml b/configs/experiment/test/images/normalized_base.yaml new file mode 100644 index 00000000..91605553 --- /dev/null +++ b/configs/experiment/test/images/normalized_base.yaml @@ -0,0 +1,16 @@ +# @package _global_ + +defaults: + - /experiment/test/base + - /datamodule/images_datamodule@datamodule + - /datamodule/transforms/stain_normalize@transforms + - _self_ + +datamodule: + batch_size: 64 + num_workers: 8 + + test: + transforms: ${transforms} + uris: + - ${test_data.tiles_filtered_uri_512} diff --git a/configs/experiment/test/images/vgg16/normalized_base.yaml b/configs/experiment/test/images/vgg16/normalized_base.yaml new file mode 100644 index 00000000..e7bb9abc --- /dev/null +++ b/configs/experiment/test/images/vgg16/normalized_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/normalized_base + - /model/vgg16_model@model + - /model/checkpoints/mmci_tl/vgg16@model_data + - _self_ + diff --git a/configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml b/configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml new file mode 100644 index 00000000..cbc650f1 --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/images/vgg16/normalized_base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml b/configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml new file mode 100644 index 00000000..4e2b8ba3 --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/vgg16/test_suite/normalized_base + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/vgg16/test_suite/radboud_test.yaml b/configs/experiment/test/images/vgg16/test_suite/radboud_test.yaml new file mode 100644 index 00000000..80d26deb --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/radboud_test.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/vgg16/test_suite/base + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} From 990b90525ac1c0f3a53eafd9710bb79a2e7c231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 7 Jul 2026 09:41:11 +0000 Subject: [PATCH 191/284] fix: stains --- configs/experiment/test/base.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/experiment/test/base.yaml b/configs/experiment/test/base.yaml index dc6a491f..ac5aa364 100644 --- a/configs/experiment/test/base.yaml +++ b/configs/experiment/test/base.yaml @@ -8,6 +8,7 @@ model: tile_threshold: ${model_data.tile_threshold} carcinoma_roi_t: ${test_data.thresholds.carcinoma_roi_t} +stains: ${test_data.stains} checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} mean: ${model_data.stats.mean} From 5d4e8f132f2b2e20659ab07ce6c58c97169e642f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 7 Jul 2026 10:18:06 +0000 Subject: [PATCH 192/284] fix: mmci stains --- configs/datamodule/transforms/stain_normalize.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/datamodule/transforms/stain_normalize.yaml b/configs/datamodule/transforms/stain_normalize.yaml index c275e375..d0f1a38f 100644 --- a/configs/datamodule/transforms/stain_normalize.yaml +++ b/configs/datamodule/transforms/stain_normalize.yaml @@ -1,10 +1,10 @@ defaults: - - /data/mmci/base@mmci_stains + - /data/mmci/base@_global_.mmci_stains - _self_ _target_: albumentations.Compose transforms: - - _target_: prostate_cancer.transforms.stain_normalization.build_normalize_staining_transform + - _target_: prostate_cancer.datamodule.transforms.stain_normalization.build_normalize_staining_transform stain1: ${stains.hematoxylin} stain2: ${stains.eosin} stain3: ${stains.residual} From 218c73efe0ee7457bc383b488d637f13e5322c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 7 Jul 2026 20:41:03 +0000 Subject: [PATCH 193/284] feat: remove full fine tune --- .../train/images/pgp/base_stain.yaml | 2 + .../train/images/pgp/base_stain_frozen.yaml | 10 --- .../train/images/pgp/base_stain_full.yaml | 20 ------ ..._tl_stain_full.yaml => mmci_tl_stain.yaml} | 2 +- .../train/images/virchow2/base_stain.yaml | 2 + .../images/virchow2/base_stain_frozen.yaml | 10 --- .../images/virchow2/base_stain_full.yaml | 20 ------ .../mmci_tl_stain.yaml} | 2 +- .../images/virchow2/mmci_tl_stain_frozen.yaml | 7 -- .../images/virchow2/mmci_tl_stain_full.yaml | 7 -- configs/model/pgp_full_model.yaml | 1 - configs/model/virchow2_full_model.yaml | 1 - prostate_cancer/foundation_model.py | 70 ++++--------------- 13 files changed, 19 insertions(+), 135 deletions(-) delete mode 100644 configs/experiment/train/images/pgp/base_stain_frozen.yaml delete mode 100644 configs/experiment/train/images/pgp/base_stain_full.yaml rename configs/experiment/train/images/pgp/{mmci_tl_stain_full.yaml => mmci_tl_stain.yaml} (68%) delete mode 100644 configs/experiment/train/images/virchow2/base_stain_frozen.yaml delete mode 100644 configs/experiment/train/images/virchow2/base_stain_full.yaml rename configs/experiment/train/images/{pgp/mmci_tl_stain_frozen.yaml => virchow2/mmci_tl_stain.yaml} (67%) delete mode 100644 configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml delete mode 100644 configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml diff --git a/configs/experiment/train/images/pgp/base_stain.yaml b/configs/experiment/train/images/pgp/base_stain.yaml index 18dc715d..cc2f77cb 100644 --- a/configs/experiment/train/images/pgp/base_stain.yaml +++ b/configs/experiment/train/images/pgp/base_stain.yaml @@ -3,3 +3,5 @@ defaults: - /experiment/train/images/pgp/base - /datamodule/transforms/normalize_w_stain_augments@transforms + +model_name: "PGP (Stain)" diff --git a/configs/experiment/train/images/pgp/base_stain_frozen.yaml b/configs/experiment/train/images/pgp/base_stain_frozen.yaml deleted file mode 100644 index 5e576233..00000000 --- a/configs/experiment/train/images/pgp/base_stain_frozen.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/pgp/base_stain - - _self_ - -model_name: "PGP (Stain)" - -model: - freeze_backbone: False diff --git a/configs/experiment/train/images/pgp/base_stain_full.yaml b/configs/experiment/train/images/pgp/base_stain_full.yaml deleted file mode 100644 index 10e4f5a4..00000000 --- a/configs/experiment/train/images/pgp/base_stain_full.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/pgp/base_stain - - _self_ - -model_name: "PGP (Stain & Frozen)" - -model: - freeze_backbone: True - backbone_lr: 1e-5 - warmup_steps: 200 - -trainer: - gradient_clip_val: 1.0 - -# full fine-tuning requires more memory -datamodule: - batch_size: 16 - num_workers: 8 diff --git a/configs/experiment/train/images/pgp/mmci_tl_stain_full.yaml b/configs/experiment/train/images/pgp/mmci_tl_stain.yaml similarity index 68% rename from configs/experiment/train/images/pgp/mmci_tl_stain_full.yaml rename to configs/experiment/train/images/pgp/mmci_tl_stain.yaml index 580a6f76..23b85019 100644 --- a/configs/experiment/train/images/pgp/mmci_tl_stain_full.yaml +++ b/configs/experiment/train/images/pgp/mmci_tl_stain.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/images/pgp/base_stain_full + - /experiment/train/images/pgp/base_stain - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - _self_ diff --git a/configs/experiment/train/images/virchow2/base_stain.yaml b/configs/experiment/train/images/virchow2/base_stain.yaml index 11182995..2a204363 100644 --- a/configs/experiment/train/images/virchow2/base_stain.yaml +++ b/configs/experiment/train/images/virchow2/base_stain.yaml @@ -3,3 +3,5 @@ defaults: - /experiment/train/images/virchow2/base - /datamodule/transforms/normalize_w_stain_augments@transforms + +model_name: "Virchow2 (Stain & Frozen)" diff --git a/configs/experiment/train/images/virchow2/base_stain_frozen.yaml b/configs/experiment/train/images/virchow2/base_stain_frozen.yaml deleted file mode 100644 index 49ac9b5b..00000000 --- a/configs/experiment/train/images/virchow2/base_stain_frozen.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain - - _self_ - -model_name: "Virchow (Stain & Frozen)" - -model: - freeze_backbone: True diff --git a/configs/experiment/train/images/virchow2/base_stain_full.yaml b/configs/experiment/train/images/virchow2/base_stain_full.yaml deleted file mode 100644 index 05f58645..00000000 --- a/configs/experiment/train/images/virchow2/base_stain_full.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain - - _self_ - -model_name: "Virchow2 (Stain)" - -model: - freeze_backbone: False - backbone_lr: 1e-5 - warmup_steps: 200 - -trainer: - gradient_clip_val: 1.0 - -# full fine-tuning requires more memory -datamodule: - batch_size: 16 - num_workers: 8 diff --git a/configs/experiment/train/images/pgp/mmci_tl_stain_frozen.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain.yaml similarity index 67% rename from configs/experiment/train/images/pgp/mmci_tl_stain_frozen.yaml rename to configs/experiment/train/images/virchow2/mmci_tl_stain.yaml index 239751f7..d57f2ea5 100644 --- a/configs/experiment/train/images/pgp/mmci_tl_stain_frozen.yaml +++ b/configs/experiment/train/images/virchow2/mmci_tl_stain.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/images/pgp/base_stain_frozen + - /experiment/train/images/virchow2/base_stain - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - _self_ diff --git a/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml deleted file mode 100644 index 0eb54e8f..00000000 --- a/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain_frozen - - /data/mmci/tl_train@train_data - - /data/mmci/tl_val@val_data - - _self_ diff --git a/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml deleted file mode 100644 index 86bef889..00000000 --- a/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain_full - - /data/mmci/tl_train@train_data - - /data/mmci/tl_val@val_data - - _self_ diff --git a/configs/model/pgp_full_model.yaml b/configs/model/pgp_full_model.yaml index e272ffad..55948d52 100644 --- a/configs/model/pgp_full_model.yaml +++ b/configs/model/pgp_full_model.yaml @@ -9,4 +9,3 @@ decode_head: _target_: prostate_cancer.foundation_model.FoundationProstateModel lr: ??? tl_threshold: ??? -freeze_backbone: ??? diff --git a/configs/model/virchow2_full_model.yaml b/configs/model/virchow2_full_model.yaml index b3becc3e..967d50f4 100644 --- a/configs/model/virchow2_full_model.yaml +++ b/configs/model/virchow2_full_model.yaml @@ -9,4 +9,3 @@ decode_head: _target_: prostate_cancer.foundation_model.FoundationProstateModel lr: ??? tl_threshold: ??? -freeze_backbone: ??? \ No newline at end of file diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index f3e2422b..afdb307f 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -1,9 +1,6 @@ from typing import Any import torch -from lightning.pytorch.utilities.types import OptimizerLRSchedulerConfig -from torch.optim import AdamW -from torch.optim.lr_scheduler import LambdaLR from prostate_cancer.base_model import ProstateCancerModel from prostate_cancer.modeling.backbone.foundation_base import FoundationModel @@ -17,74 +14,33 @@ def __init__( decode_head: BinaryCNNClassifier, lr: float, tl_threshold: float, - freeze_backbone: bool, - backbone_lr: float | None = None, - warmup_steps: int = 0, ) -> None: super().__init__(lr=lr, tl_threshold=tl_threshold) self.backbone = backbone self.decode_head = decode_head - self.frozen_backbone = freeze_backbone - - # full fine-tuning disrupts pretrained weights if updated at the - # same LR as the freshly initialized head, so it gets its own, - # lower LR (defaults to the head LR when unset) - self.backbone_lr = lr if backbone_lr is None else backbone_lr - self.warmup_steps = warmup_steps - - # stay consistent with embedding training - if freeze_backbone: - for p in self.backbone.module.parameters(): - p.requires_grad = False - self.backbone.module.eval() + # freeze backbone + for p in self.backbone.module.parameters(): + p.requires_grad = False + self.backbone.module.eval() def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None: - if self.frozen_backbone: - # no need to save frozen backbone - state_dict: dict[str, Any] = checkpoint["state_dict"] + # no need to save frozen backbone + state_dict: dict[str, Any] = checkpoint["state_dict"] - keys_to_remove = [ - k for k in list(state_dict.keys()) if k.startswith("backbone.") - ] + keys_to_remove = [ + k for k in list(state_dict.keys()) if k.startswith("backbone.") + ] - for k in keys_to_remove: - del state_dict[k] + for k in keys_to_remove: + del state_dict[k] def on_train_epoch_start(self) -> None: - # prevent unintentional train mode - if self.frozen_backbone: - self.backbone.module.eval() + self.backbone.module.eval() def forward(self, x: torch.Tensor) -> torch.Tensor: - if self.frozen_backbone: - with torch.no_grad(): - features = self.backbone(x) - else: + with torch.no_grad(): features = self.backbone(x) logits = self.decode_head(features) return logits - - def configure_optimizers(self) -> OptimizerLRSchedulerConfig: - param_groups = [{"params": self.decode_head.parameters(), "lr": self.lr}] - if not self.frozen_backbone: - param_groups.append( - {"params": self.backbone.parameters(), "lr": self.backbone_lr} - ) - - optimizer = AdamW(param_groups, lr=self.lr) - - if self.warmup_steps == 0: - return {"optimizer": optimizer} - - warmup_steps = self.warmup_steps - - def warmup(step: int) -> float: - return min(1.0, (step + 1) / warmup_steps) - - scheduler = LambdaLR(optimizer, lr_lambda=warmup) - return { - "optimizer": optimizer, - "lr_scheduler": {"scheduler": scheduler, "interval": "step"}, - } From 07e12e5a30adb424c8714c195e8c7126c830b10b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 06:17:27 +0000 Subject: [PATCH 194/284] fix: checkpoint loading --- prostate_cancer/foundation_model.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index afdb307f..6331a7db 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -24,6 +24,11 @@ def __init__( p.requires_grad = False self.backbone.module.eval() + def load_state_dict(self, state_dict: dict[str, Any], strict: bool = False) -> Any: + return super().load_state_dict( + state_dict, strict=False + ) # frozen backbone is not stored + def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None: # no need to save frozen backbone state_dict: dict[str, Any] = checkpoint["state_dict"] From 3abff4c841110ca84c07db4e0513f787954e9eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 06:19:01 +0000 Subject: [PATCH 195/284] feat: test non strict load --- configs/experiment/test/images/pgp/base.yaml | 15 +++++++++++++++ .../test/images/pgp/tile_threshold.yaml | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 configs/experiment/test/images/pgp/base.yaml create mode 100644 configs/experiment/test/images/pgp/tile_threshold.yaml diff --git a/configs/experiment/test/images/pgp/base.yaml b/configs/experiment/test/images/pgp/base.yaml new file mode 100644 index 00000000..a5117bd6 --- /dev/null +++ b/configs/experiment/test/images/pgp/base.yaml @@ -0,0 +1,15 @@ +# @package _global_ + +defaults: + - /experiment/test/images/base + - /model/pgp@model + - /model/checkpoints/mmci_tl/pgp@model_data + - _self_ + +datamodule: + batch_size: 96 + num_workers: 8 + + test: + uris: + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/pgp/tile_threshold.yaml b/configs/experiment/test/images/pgp/tile_threshold.yaml new file mode 100644 index 00000000..930cb088 --- /dev/null +++ b/configs/experiment/test/images/pgp/tile_threshold.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/images/pgp/base + - /data/mmci/tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.9 # Pathologist's estimate +use_case: "Tile Threshold Estimation" From e197851711aee6d0df030190790017e543590359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 06:25:47 +0000 Subject: [PATCH 196/284] fix: header --- configs/experiment/test/images/pgp/base.yaml | 2 +- prostate_cancer/foundation_model.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/configs/experiment/test/images/pgp/base.yaml b/configs/experiment/test/images/pgp/base.yaml index a5117bd6..e17b0cc7 100644 --- a/configs/experiment/test/images/pgp/base.yaml +++ b/configs/experiment/test/images/pgp/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/test/images/base - - /model/pgp@model + - /model/pgp_full_model@model - /model/checkpoints/mmci_tl/pgp@model_data - _self_ diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index 6331a7db..9122861f 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -24,9 +24,11 @@ def __init__( p.requires_grad = False self.backbone.module.eval() - def load_state_dict(self, state_dict: dict[str, Any], strict: bool = False) -> Any: + def load_state_dict( + self, state_dict: dict[str, Any], strict: bool = False, assign: bool = False + ) -> Any: return super().load_state_dict( - state_dict, strict=False + state_dict, strict=False, assign=assign ) # frozen backbone is not stored def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None: From e28c5d9e3bb7145b3a30aa0c8f514fa3da063ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 06:29:32 +0000 Subject: [PATCH 197/284] fix: mapping --- prostate_cancer/foundation_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index 9122861f..fcf3868d 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Mapping import torch @@ -25,7 +25,7 @@ def __init__( self.backbone.module.eval() def load_state_dict( - self, state_dict: dict[str, Any], strict: bool = False, assign: bool = False + self, state_dict: Mapping[str, Any], strict: bool = False, assign: bool = False ) -> Any: return super().load_state_dict( state_dict, strict=False, assign=assign From e091df67c14a8925528a57d9de1a2d917cf0a856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 06:31:19 +0000 Subject: [PATCH 198/284] fix: lint --- prostate_cancer/foundation_model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prostate_cancer/foundation_model.py b/prostate_cancer/foundation_model.py index fcf3868d..3bda1b49 100644 --- a/prostate_cancer/foundation_model.py +++ b/prostate_cancer/foundation_model.py @@ -1,4 +1,5 @@ -from typing import Any, Mapping +from collections.abc import Mapping +from typing import Any import torch From 3ff39018d5a833c4b59cbeb1432e3d72be3692bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 06:37:45 +0000 Subject: [PATCH 199/284] feat: remove test pgp --- configs/experiment/test/images/pgp/base.yaml | 15 --------------- .../test/images/pgp/tile_threshold.yaml | 14 -------------- 2 files changed, 29 deletions(-) delete mode 100644 configs/experiment/test/images/pgp/base.yaml delete mode 100644 configs/experiment/test/images/pgp/tile_threshold.yaml diff --git a/configs/experiment/test/images/pgp/base.yaml b/configs/experiment/test/images/pgp/base.yaml deleted file mode 100644 index e17b0cc7..00000000 --- a/configs/experiment/test/images/pgp/base.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/test/images/base - - /model/pgp_full_model@model - - /model/checkpoints/mmci_tl/pgp@model_data - - _self_ - -datamodule: - batch_size: 96 - num_workers: 8 - - test: - uris: - - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/pgp/tile_threshold.yaml b/configs/experiment/test/images/pgp/tile_threshold.yaml deleted file mode 100644 index 930cb088..00000000 --- a/configs/experiment/test/images/pgp/tile_threshold.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_threshold@callbacks - - /experiment/test/images/pgp/base - - /data/mmci/tl_val@test_data - - _self_ - -datamodule: - test: - stratified_filter: true # tile threshold not estimated from negative tiles in positive slides - -tile_threshold: 0.9 # Pathologist's estimate -use_case: "Tile Threshold Estimation" From 9f016f58af9456142dac998f9146d95c95c685d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 07:55:32 +0000 Subject: [PATCH 200/284] feat: smaller patch size --- configs/experiment/train/base.yaml | 6 ++++++ configs/experiment/train/images/google_vit/base.yaml | 8 -------- .../experiment/train/images/google_vit/base_stain.yaml | 8 -------- configs/experiment/train/images/pgp/base.yaml | 8 -------- configs/experiment/train/images/resnet50/base.yaml | 8 -------- configs/experiment/train/images/resnet50/base_stain.yaml | 8 -------- configs/experiment/train/images/vgg16/base.yaml | 8 -------- configs/experiment/train/images/vgg16/base_stain.yaml | 8 -------- configs/experiment/train/images/virchow2/base.yaml | 8 -------- 9 files changed, 6 insertions(+), 64 deletions(-) diff --git a/configs/experiment/train/base.yaml b/configs/experiment/train/base.yaml index 718c6512..81fcacc6 100644 --- a/configs/experiment/train/base.yaml +++ b/configs/experiment/train/base.yaml @@ -22,6 +22,12 @@ data_name: ${train_data.data_name} datamodule: batch_size: ??? num_workers: ??? + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} model: lr: 1e-4 diff --git a/configs/experiment/train/images/google_vit/base.yaml b/configs/experiment/train/images/google_vit/base.yaml index d5530967..ad84f3f5 100644 --- a/configs/experiment/train/images/google_vit/base.yaml +++ b/configs/experiment/train/images/google_vit/base.yaml @@ -7,11 +7,3 @@ defaults: - _self_ model_name: "Google ViT" - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/google_vit/base_stain.yaml b/configs/experiment/train/images/google_vit/base_stain.yaml index 80ff80a8..0ee0dc4b 100644 --- a/configs/experiment/train/images/google_vit/base_stain.yaml +++ b/configs/experiment/train/images/google_vit/base_stain.yaml @@ -7,11 +7,3 @@ defaults: - _self_ model_name: "Google ViT (Stain)" - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/pgp/base.yaml b/configs/experiment/train/images/pgp/base.yaml index 8342ffd6..46ab3eac 100644 --- a/configs/experiment/train/images/pgp/base.yaml +++ b/configs/experiment/train/images/pgp/base.yaml @@ -8,11 +8,3 @@ defaults: # ImageNet stats mean: [123.675, 116.280, 103.530] std: [58.395, 57.120, 57.375] - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/resnet50/base.yaml b/configs/experiment/train/images/resnet50/base.yaml index bfa6d390..02412a1e 100644 --- a/configs/experiment/train/images/resnet50/base.yaml +++ b/configs/experiment/train/images/resnet50/base.yaml @@ -7,11 +7,3 @@ defaults: - _self_ model_name: "ResNet50" - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_512} - val: - uris: - - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/resnet50/base_stain.yaml b/configs/experiment/train/images/resnet50/base_stain.yaml index 34855979..5f724341 100644 --- a/configs/experiment/train/images/resnet50/base_stain.yaml +++ b/configs/experiment/train/images/resnet50/base_stain.yaml @@ -7,11 +7,3 @@ defaults: - _self_ model_name: "ResNet50 Stain" - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_512} - val: - uris: - - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/vgg16/base.yaml b/configs/experiment/train/images/vgg16/base.yaml index 38ab882b..fb7ce56c 100644 --- a/configs/experiment/train/images/vgg16/base.yaml +++ b/configs/experiment/train/images/vgg16/base.yaml @@ -7,11 +7,3 @@ defaults: - _self_ model_name: "VGG-16" - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_512} - val: - uris: - - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/vgg16/base_stain.yaml b/configs/experiment/train/images/vgg16/base_stain.yaml index cfea58c9..a46e8f4d 100644 --- a/configs/experiment/train/images/vgg16/base_stain.yaml +++ b/configs/experiment/train/images/vgg16/base_stain.yaml @@ -7,11 +7,3 @@ defaults: - _self_ model_name: "VGG-16 Stain" - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_512} - val: - uris: - - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/virchow2/base.yaml b/configs/experiment/train/images/virchow2/base.yaml index c37e4f2b..65516ebb 100644 --- a/configs/experiment/train/images/virchow2/base.yaml +++ b/configs/experiment/train/images/virchow2/base.yaml @@ -8,11 +8,3 @@ defaults: # ImageNet stats mean: [123.675, 116.280, 103.530] std: [58.395, 57.120, 57.375] - -datamodule: - train: - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} From f8551a5b9a9a0a4c6df4431f1c7f332409303042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 8 Jul 2026 08:23:30 +0000 Subject: [PATCH 201/284] feat: virchow rbt embeds --- .../tile_embeddings_v2/virchow2/radboud_test.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_test.yaml diff --git a/configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_test.yaml b/configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_test.yaml new file mode 100644 index 00000000..ce19ac71 --- /dev/null +++ b/configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_test.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/preprocessing/tile_embeddings_v2/virchow2/base + - /data/radboud/test@data From dc33db2cbc131aac80c8e0ac0c6e073702f81f94 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 9 Jul 2026 06:43:19 +0000 Subject: [PATCH 202/284] feat: test --- .../heatmaps_and_max_aggregations/radboud_test.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 configs/experiment/predict/images/resnet50/heatmaps_and_max_aggregations/radboud_test.yaml diff --git a/configs/experiment/predict/images/resnet50/heatmaps_and_max_aggregations/radboud_test.yaml b/configs/experiment/predict/images/resnet50/heatmaps_and_max_aggregations/radboud_test.yaml new file mode 100644 index 00000000..35ee7dbb --- /dev/null +++ b/configs/experiment/predict/images/resnet50/heatmaps_and_max_aggregations/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/heatmaps_and_max_aggregations/base + - /data/radboud/test@predict_data + - _self_ From 4f852679d2d4b5c5302bb58b20433d8a00bbb489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 9 Jul 2026 08:19:19 +0000 Subject: [PATCH 203/284] feat: rename --- .../train/images/pgp/base_stain.yaml | 2 +- .../train/images/pgp/base_stain_full.yaml | 20 ------------------- .../images/virchow2/base_stain_frozen.yaml | 10 ---------- .../images/virchow2/base_stain_full.yaml | 20 ------------------- .../images/virchow2/mmci_tl_stain_frozen.yaml | 7 ------- .../images/virchow2/mmci_tl_stain_full.yaml | 7 ------- 6 files changed, 1 insertion(+), 65 deletions(-) delete mode 100644 configs/experiment/train/images/pgp/base_stain_full.yaml delete mode 100644 configs/experiment/train/images/virchow2/base_stain_frozen.yaml delete mode 100644 configs/experiment/train/images/virchow2/base_stain_full.yaml delete mode 100644 configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml delete mode 100644 configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml diff --git a/configs/experiment/train/images/pgp/base_stain.yaml b/configs/experiment/train/images/pgp/base_stain.yaml index cc2f77cb..a1e6b81a 100644 --- a/configs/experiment/train/images/pgp/base_stain.yaml +++ b/configs/experiment/train/images/pgp/base_stain.yaml @@ -4,4 +4,4 @@ defaults: - /experiment/train/images/pgp/base - /datamodule/transforms/normalize_w_stain_augments@transforms -model_name: "PGP (Stain)" +model_name: "PGP (Stain & Frozen)" diff --git a/configs/experiment/train/images/pgp/base_stain_full.yaml b/configs/experiment/train/images/pgp/base_stain_full.yaml deleted file mode 100644 index 10e4f5a4..00000000 --- a/configs/experiment/train/images/pgp/base_stain_full.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/pgp/base_stain - - _self_ - -model_name: "PGP (Stain & Frozen)" - -model: - freeze_backbone: True - backbone_lr: 1e-5 - warmup_steps: 200 - -trainer: - gradient_clip_val: 1.0 - -# full fine-tuning requires more memory -datamodule: - batch_size: 16 - num_workers: 8 diff --git a/configs/experiment/train/images/virchow2/base_stain_frozen.yaml b/configs/experiment/train/images/virchow2/base_stain_frozen.yaml deleted file mode 100644 index 49ac9b5b..00000000 --- a/configs/experiment/train/images/virchow2/base_stain_frozen.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain - - _self_ - -model_name: "Virchow (Stain & Frozen)" - -model: - freeze_backbone: True diff --git a/configs/experiment/train/images/virchow2/base_stain_full.yaml b/configs/experiment/train/images/virchow2/base_stain_full.yaml deleted file mode 100644 index 05f58645..00000000 --- a/configs/experiment/train/images/virchow2/base_stain_full.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain - - _self_ - -model_name: "Virchow2 (Stain)" - -model: - freeze_backbone: False - backbone_lr: 1e-5 - warmup_steps: 200 - -trainer: - gradient_clip_val: 1.0 - -# full fine-tuning requires more memory -datamodule: - batch_size: 16 - num_workers: 8 diff --git a/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml deleted file mode 100644 index 0eb54e8f..00000000 --- a/configs/experiment/train/images/virchow2/mmci_tl_stain_frozen.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain_frozen - - /data/mmci/tl_train@train_data - - /data/mmci/tl_val@val_data - - _self_ diff --git a/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml b/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml deleted file mode 100644 index 86bef889..00000000 --- a/configs/experiment/train/images/virchow2/mmci_tl_stain_full.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/train/images/virchow2/base_stain_full - - /data/mmci/tl_train@train_data - - /data/mmci/tl_val@val_data - - _self_ From 0b52b7e7edab9b6f920fd0a7df8ea2a2251d4823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 9 Jul 2026 08:31:14 +0000 Subject: [PATCH 204/284] fix: missing brace --- .../max_aggregations/pgp/mmci_sl_group_1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml index 1c0d71a3..25971221 100644 --- a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/pgp/mmci_sl_group_1.yaml @@ -5,4 +5,4 @@ defaults: - /data/mmci/sl_group_1@data - _self_ -preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1 +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} From 2327d85bd29096d683eb8b034ac9bc3b02d093d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 9 Jul 2026 08:42:52 +0000 Subject: [PATCH 205/284] fix: sdk version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0c78e67e..069f8a29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", - "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git@68904dfd216ce19454e08a032f7e4dd978a85707", + "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git", "numpy>=2.3.2", "timm==1.0.15", "matplotlib==3.10.1", From 0343c9e8524f63c127fcd0fa5544d601d443ce36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 10 Jul 2026 08:24:39 +0000 Subject: [PATCH 206/284] fix: rows per file --- preprocessing/embeddings/tile_embeddings_v2.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/preprocessing/embeddings/tile_embeddings_v2.py b/preprocessing/embeddings/tile_embeddings_v2.py index e8ff41e1..8f715722 100644 --- a/preprocessing/embeddings/tile_embeddings_v2.py +++ b/preprocessing/embeddings/tile_embeddings_v2.py @@ -86,8 +86,13 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: if tiles_parquet_dir.exists(): shutil.rmtree(tiles_parquet_dir) - slides.to_parquet(output_path / "slides.parquet", index=False) - ds.write_parquet(str(tiles_parquet_dir), min_rows_per_file=config.rows_per_file) + slides_parquet_dir = (output_path / "slides") + if slides_parquet_dir.exists(): + shutil.rmtree(slides_parquet_dir) + + slides_parquet_dir.mkdir(parents=True, exist_ok=True) + slides.to_parquet(output_path / "slides" / "slides.parquet", index=False) + ds.write_parquet(str(tiles_parquet_dir), max_rows_per_file=config.rows_per_file) logger.log_artifacts(str(output_path), f"{config.data.data_name}") From f8464f756147a8ba4edc3c6d0f76c90bfc8a4385 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Jul 2026 09:31:15 +0000 Subject: [PATCH 207/284] feat: embedddings more --- configs/data/radboud/test.yaml | 1 + .../preprocessing/tile_embeddings_v2/pgp/radboud_test.yaml | 5 +++++ .../preprocessing/tile_embeddings_v2/pgp/radboud_val.yaml | 5 +++++ .../tile_embeddings_v2/virchow2/radboud_val.yaml | 5 +++++ 4 files changed, 16 insertions(+) create mode 100644 configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_test.yaml create mode 100644 configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_val.yaml create mode 100644 configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_val.yaml diff --git a/configs/data/radboud/test.yaml b/configs/data/radboud/test.yaml index acd5269b..e8fee97a 100644 --- a/configs/data/radboud/test.yaml +++ b/configs/data/radboud/test.yaml @@ -10,5 +10,6 @@ tiles_filtered_uri_512: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/a tiles_uri_224: "mlflow-artifacts:/38/4cab0908f7eb4d0aba64407af072f08c/artifacts/PANDA - Radboud test" tiles_filtered_uri_224: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/artifacts/PANDA - Radboud test_224" +tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/38/a63457454feb49ecb362af00ad5e2320/artifacts/PANDA - Radboud test" data_name: "PANDA - Radboud test" diff --git a/configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_test.yaml b/configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_test.yaml new file mode 100644 index 00000000..4f0f5548 --- /dev/null +++ b/configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_test.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/preprocessing/tile_embeddings_v2/pgp/base + - /data/radboud/test@data diff --git a/configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_val.yaml b/configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_val.yaml new file mode 100644 index 00000000..6d312f4d --- /dev/null +++ b/configs/experiment/preprocessing/tile_embeddings_v2/pgp/radboud_val.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/preprocessing/tile_embeddings_v2/pgp/base + - /data/radboud/val@data diff --git a/configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_val.yaml b/configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_val.yaml new file mode 100644 index 00000000..38e710a1 --- /dev/null +++ b/configs/experiment/preprocessing/tile_embeddings_v2/virchow2/radboud_val.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/preprocessing/tile_embeddings_v2/virchow2/base + - /data/radboud/val@data From c4a1b5b7d234ba839b26c11cc683a548b7f31693 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Jul 2026 09:33:07 +0000 Subject: [PATCH 208/284] fix: pgp name --- .../experiment/preprocessing/tile_embeddings_v2/pgp/base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/preprocessing/tile_embeddings_v2/pgp/base.yaml b/configs/experiment/preprocessing/tile_embeddings_v2/pgp/base.yaml index eec9b461..fac2b2f7 100644 --- a/configs/experiment/preprocessing/tile_embeddings_v2/pgp/base.yaml +++ b/configs/experiment/preprocessing/tile_embeddings_v2/pgp/base.yaml @@ -1,3 +1,3 @@ # @package _global_ -encoder: "provgigapath" +encoder: "prov-gigapath" From 306a036838e33e25a18f5d326a15f6724b956fbf Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Jul 2026 10:36:35 +0000 Subject: [PATCH 209/284] feat: ignore bg --- pyproject.toml | 2 +- uv.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 069f8a29..d70316a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "rationai-masks @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git", "rationai-mlkit @ git+https://github.com/RationAI/mlkit.git", "rationai-tiling @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git", - "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git", + "rationai-staining @ git+https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git@2437ec9af319731f3e32aa79c4bf9e6fef5a5711", "rationai-sdk @git+https://github.com/RationAI/rationai-sdk-python.git", "numpy>=2.3.2", "timm==1.0.15", diff --git a/uv.lock b/uv.lock index 8b53308e..2836046d 100644 --- a/uv.lock +++ b/uv.lock @@ -2430,8 +2430,8 @@ requires-dist = [ { name = "pillow", specifier = "==11.3.0" }, { name = "rationai-masks", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/masks.git" }, { name = "rationai-mlkit", git = "https://github.com/RationAI/mlkit.git" }, - { name = "rationai-sdk", git = "https://github.com/RationAI/rationai-sdk-python.git?rev=68904dfd216ce19454e08a032f7e4dd978a85707" }, - { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git" }, + { name = "rationai-sdk", git = "https://github.com/RationAI/rationai-sdk-python.git" }, + { name = "rationai-staining", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git?rev=2437ec9af319731f3e32aa79c4bf9e6fef5a5711" }, { name = "rationai-tiling", git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/tiling.git" }, { name = "ratiopath", specifier = ">=1.1.2" }, { name = "ray", extras = ["data"], specifier = ">=2.52.1" }, @@ -2927,7 +2927,7 @@ dependencies = [ [[package]] name = "rationai-sdk" version = "0.1.0" -source = { git = "https://github.com/RationAI/rationai-sdk-python.git?rev=68904dfd216ce19454e08a032f7e4dd978a85707#68904dfd216ce19454e08a032f7e4dd978a85707" } +source = { git = "https://github.com/RationAI/rationai-sdk-python.git#3fb74c0f867432422dacde29ec5c0a4584af4830" } dependencies = [ { name = "httpx" }, { name = "lz4" }, @@ -2938,8 +2938,8 @@ dependencies = [ [[package]] name = "rationai-staining" -version = "0.3.0" -source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git#c48d216e910df11f3e6891aa071ebb8d04635727" } +version = "0.4.0" +source = { git = "https://gitlab.ics.muni.cz/rationai/digital-pathology/libraries/staining.git?rev=2437ec9af319731f3e32aa79c4bf9e6fef5a5711#2437ec9af319731f3e32aa79c4bf9e6fef5a5711" } dependencies = [ { name = "albumentations" }, { name = "numpy" }, From ea88f85b39954376908ae541e0280d5abc55285e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Jul 2026 06:43:18 +0000 Subject: [PATCH 210/284] feat: radboud embeds --- configs/data/radboud/test.yaml | 1 + configs/data/radboud/val.yaml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/configs/data/radboud/test.yaml b/configs/data/radboud/test.yaml index e8fee97a..72bdfbb0 100644 --- a/configs/data/radboud/test.yaml +++ b/configs/data/radboud/test.yaml @@ -11,5 +11,6 @@ tiles_filtered_uri_512: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/a tiles_uri_224: "mlflow-artifacts:/38/4cab0908f7eb4d0aba64407af072f08c/artifacts/PANDA - Radboud test" tiles_filtered_uri_224: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/artifacts/PANDA - Radboud test_224" tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/38/a63457454feb49ecb362af00ad5e2320/artifacts/PANDA - Radboud test" +tiles_filtered_w_pgp_uri_224: null data_name: "PANDA - Radboud test" diff --git a/configs/data/radboud/val.yaml b/configs/data/radboud/val.yaml index a05fe304..ce1628a4 100644 --- a/configs/data/radboud/val.yaml +++ b/configs/data/radboud/val.yaml @@ -7,5 +7,7 @@ tiles_filtered_uri_512: "mlflow-artifacts:/38/b38ca47d6fd4422a9f8d5a32fcae221c/a tiles_uri_224: "mlflow-artifacts:/38/33ad08ab5f9845809fb48b2b7d2aa643/artifacts/PANDA - Radboud val" tiles_filtered_uri_224: "mlflow-artifacts:/38/b38ca47d6fd4422a9f8d5a32fcae221c/artifacts/PANDA - Radboud val_224" +tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/38/e0376ffbbc894cb5862011e7f79c03f9/artifacts/PANDA - Radboud val" +tiles_filtered_w_pgp_uri_224: "mlflow-artifacts:/38/1ffee8c4b2c841879a25aa3781a271ff/artifacts/PANDA - Radboud val" data_name: "PANDA - Radboud val" From 018b69812216cd360c053ad40cb306f642620b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 13 Jul 2026 06:48:15 +0000 Subject: [PATCH 211/284] feat: radboud embed test --- .../experiment/test/embeddings/pgp/curves/base.yaml | 8 -------- .../test/embeddings/pgp/nested_metrics/base.yaml | 8 -------- .../test/embeddings/pgp/test_suite/radboud_test.yaml | 6 ++++++ .../pgp/test_suite/stain_radboud_test.yaml | 6 ++++++ .../test/embeddings/pgp/tile_histograms/base.yaml | 12 ------------ .../test/embeddings/virchow2/curves/base.yaml | 8 -------- .../embeddings/virchow2/nested_metrics/base.yaml | 8 -------- .../embeddings/virchow2/test_suite/radboud_test.yaml | 6 ++++++ .../virchow2/test_suite/stain_radboud_test.yaml | 6 ++++++ .../embeddings/virchow2/tile_histograms/base.yaml | 12 ------------ 10 files changed, 24 insertions(+), 56 deletions(-) delete mode 100644 configs/experiment/test/embeddings/pgp/curves/base.yaml delete mode 100644 configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml create mode 100644 configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml create mode 100644 configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml delete mode 100644 configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml delete mode 100644 configs/experiment/test/embeddings/virchow2/curves/base.yaml delete mode 100644 configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml create mode 100644 configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml create mode 100644 configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml delete mode 100644 configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml diff --git a/configs/experiment/test/embeddings/pgp/curves/base.yaml b/configs/experiment/test/embeddings/pgp/curves/base.yaml deleted file mode 100644 index 4519f4be..00000000 --- a/configs/experiment/test/embeddings/pgp/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/embeddings/pgp/base - - _self_ - -use_case: "TL ROC & PR Curves" diff --git a/configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml b/configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml deleted file mode 100644 index 42d6cfdf..00000000 --- a/configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/embeddings/pgp/base - - _self_ - -use_case: Tile-Level Per-Slide Metrics diff --git a/configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml b/configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml new file mode 100644 index 00000000..d1a0b964 --- /dev/null +++ b/configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/embeddings/pgp/test_suite/base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml b/configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml new file mode 100644 index 00000000..9c288620 --- /dev/null +++ b/configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/embeddings/pgp/test_suite/stain_base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml b/configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml deleted file mode 100644 index 45e65999..00000000 --- a/configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/embeddings/pgp/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/embeddings/virchow2/curves/base.yaml b/configs/experiment/test/embeddings/virchow2/curves/base.yaml deleted file mode 100644 index 488fcf2f..00000000 --- a/configs/experiment/test/embeddings/virchow2/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/embeddings/virchow2/base - - _self_ - -use_case: "TL ROC & PR Curves" diff --git a/configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml b/configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml deleted file mode 100644 index 893544a4..00000000 --- a/configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/embeddings/virchow2/base - - _self_ - -use_case: "Per-Slide Metrics" diff --git a/configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml b/configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml new file mode 100644 index 00000000..6ee0501a --- /dev/null +++ b/configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/embeddings/virchow2/test_suite/base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml b/configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml new file mode 100644 index 00000000..0b5ce548 --- /dev/null +++ b/configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/embeddings/virchow2/test_suite/stain_base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml b/configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml deleted file mode 100644 index b8d7bfbf..00000000 --- a/configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/embeddings/virchow2/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" From f1cc7a353513835c8cbb9a4770f0e2d6fdd71b2e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 13 Jul 2026 20:20:34 +0000 Subject: [PATCH 212/284] fix: checpoint --- .../experiment/test/embeddings/pgp/test_suite/radboud_test.yaml | 2 ++ .../test/embeddings/pgp/test_suite/stain_radboud_test.yaml | 2 ++ .../test/embeddings/virchow2/test_suite/radboud_test.yaml | 2 ++ .../test/embeddings/virchow2/test_suite/stain_radboud_test.yaml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml b/configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml index d1a0b964..24e1ab6b 100644 --- a/configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml +++ b/configs/experiment/test/embeddings/pgp/test_suite/radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/embeddings/pgp/test_suite/base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml b/configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml index 9c288620..b74ba07b 100644 --- a/configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml +++ b/configs/experiment/test/embeddings/pgp/test_suite/stain_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/embeddings/pgp/test_suite/stain_base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml b/configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml index 6ee0501a..03d7b8a7 100644 --- a/configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml +++ b/configs/experiment/test/embeddings/virchow2/test_suite/radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/embeddings/virchow2/test_suite/base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml b/configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml index 0b5ce548..c103e074 100644 --- a/configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml +++ b/configs/experiment/test/embeddings/virchow2/test_suite/stain_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/embeddings/virchow2/test_suite/stain_base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} From 430b4388c08ffa8fdbd260c6e7a45db99a6f0385 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 14 Jul 2026 06:34:39 +0000 Subject: [PATCH 213/284] feat: pgp embeds --- configs/data/radboud/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/data/radboud/test.yaml b/configs/data/radboud/test.yaml index 72bdfbb0..c880b3ab 100644 --- a/configs/data/radboud/test.yaml +++ b/configs/data/radboud/test.yaml @@ -11,6 +11,6 @@ tiles_filtered_uri_512: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/a tiles_uri_224: "mlflow-artifacts:/38/4cab0908f7eb4d0aba64407af072f08c/artifacts/PANDA - Radboud test" tiles_filtered_uri_224: "mlflow-artifacts:/38/0525b745d0954fb6ace98ead20042faf/artifacts/PANDA - Radboud test_224" tiles_filtered_w_virchow2_uri_224: "mlflow-artifacts:/38/a63457454feb49ecb362af00ad5e2320/artifacts/PANDA - Radboud test" -tiles_filtered_w_pgp_uri_224: null +tiles_filtered_w_pgp_uri_224: "mlflow-artifacts:/38/11c6715b9d1445a9aa59e9d0e0294c4c/artifacts/PANDA - Radboud test" data_name: "PANDA - Radboud test" From fc05c991cf9ab2f5459eb6aa6a34331a719380f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 14 Jul 2026 06:44:16 +0000 Subject: [PATCH 214/284] feat: gvit --- .../test/images/google_vit/curves/stain_base.yaml | 8 -------- .../images/google_vit/nested_metrics/stain_base.yaml | 8 -------- .../images/google_vit/test_suite/radboud_test.yaml | 8 ++++++++ .../google_vit/test_suite/stain_radboud_test.yaml | 8 ++++++++ .../google_vit/tile_histograms/stain_base.yaml | 12 ------------ .../model/checkpoints/mmci_tl/google_vit_stain.yaml | 2 ++ 6 files changed, 18 insertions(+), 28 deletions(-) delete mode 100644 configs/experiment/test/images/google_vit/curves/stain_base.yaml delete mode 100644 configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml create mode 100644 configs/experiment/test/images/google_vit/test_suite/radboud_test.yaml create mode 100644 configs/experiment/test/images/google_vit/test_suite/stain_radboud_test.yaml delete mode 100644 configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml diff --git a/configs/experiment/test/images/google_vit/curves/stain_base.yaml b/configs/experiment/test/images/google_vit/curves/stain_base.yaml deleted file mode 100644 index 347e5996..00000000 --- a/configs/experiment/test/images/google_vit/curves/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/google_vit/stain_base - - _self_ - -use_case: "TL Curves" \ No newline at end of file diff --git a/configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml b/configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml deleted file mode 100644 index b5f3e066..00000000 --- a/configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/google_vit/stain_base - - _self_ - -use_case: "TL Per-Slide metrics" \ No newline at end of file diff --git a/configs/experiment/test/images/google_vit/test_suite/radboud_test.yaml b/configs/experiment/test/images/google_vit/test_suite/radboud_test.yaml new file mode 100644 index 00000000..d71bad18 --- /dev/null +++ b/configs/experiment/test/images/google_vit/test_suite/radboud_test.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/google_vit/test_suite/base + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/google_vit/test_suite/stain_radboud_test.yaml b/configs/experiment/test/images/google_vit/test_suite/stain_radboud_test.yaml new file mode 100644 index 00000000..fb049c19 --- /dev/null +++ b/configs/experiment/test/images/google_vit/test_suite/stain_radboud_test.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/google_vit/test_suite/stain_base + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml b/configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml deleted file mode 100644 index ca5e3cde..00000000 --- a/configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/google_vit/stain_base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/model/checkpoints/mmci_tl/google_vit_stain.yaml b/configs/model/checkpoints/mmci_tl/google_vit_stain.yaml index ab7ed29a..4fa5ab9e 100644 --- a/configs/model/checkpoints/mmci_tl/google_vit_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/google_vit_stain.yaml @@ -9,6 +9,8 @@ slide_top_k_threshold: 0.9986477494 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/a07e33c384a64cb9ab73621200efd314/artifacts/checkpoints/epoch=5-step=291684/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/gvit_stain_checkpoint.ckpt" + model_name: "Google ViT (Stain)" slide_level_prediction_uris: From 64042ce956c8fdf9e26e13e49185e37a1aa4a084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 14 Jul 2026 18:19:09 +0000 Subject: [PATCH 215/284] feat: gvit load --- .../datamodule/transforms/stain_normalize.yaml | 18 ------------------ .../test/images/normalized_base.yaml | 16 ---------------- .../test/images/resnet50/curves/base.yaml | 8 -------- .../images/resnet50/curves/stain_base.yaml | 8 -------- .../images/resnet50/nested_metrics/base.yaml | 8 -------- .../resnet50/nested_metrics/stain_base.yaml | 8 -------- .../images/resnet50/tile_histograms/base.yaml | 12 ------------ .../resnet50/tile_histograms/stain_base.yaml | 12 ------------ .../test/images/vgg16/curves/base.yaml | 8 -------- .../test/images/vgg16/curves/stain_base.yaml | 8 -------- .../test/images/vgg16/nested_metrics/base.yaml | 8 -------- .../vgg16/nested_metrics/stain_base.yaml | 8 -------- .../test/images/vgg16/normalized_base.yaml | 8 -------- .../vgg16/test_suite/normalized_base.yaml | 8 -------- .../test_suite/normalized_radboud_test.yaml | 8 -------- .../images/vgg16/tile_histograms/base.yaml | 12 ------------ .../vgg16/tile_histograms/stain_base.yaml | 12 ------------ preprocessing/embeddings/tile_embeddings_v2.py | 2 +- prostate_cancer/vit_model.py | 10 ++++++++++ 19 files changed, 11 insertions(+), 171 deletions(-) delete mode 100644 configs/datamodule/transforms/stain_normalize.yaml delete mode 100644 configs/experiment/test/images/normalized_base.yaml delete mode 100644 configs/experiment/test/images/resnet50/curves/base.yaml delete mode 100644 configs/experiment/test/images/resnet50/curves/stain_base.yaml delete mode 100644 configs/experiment/test/images/resnet50/nested_metrics/base.yaml delete mode 100644 configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml delete mode 100644 configs/experiment/test/images/resnet50/tile_histograms/base.yaml delete mode 100644 configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/curves/base.yaml delete mode 100644 configs/experiment/test/images/vgg16/curves/stain_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/nested_metrics/base.yaml delete mode 100644 configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/normalized_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml delete mode 100644 configs/experiment/test/images/vgg16/tile_histograms/base.yaml delete mode 100644 configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml diff --git a/configs/datamodule/transforms/stain_normalize.yaml b/configs/datamodule/transforms/stain_normalize.yaml deleted file mode 100644 index d0f1a38f..00000000 --- a/configs/datamodule/transforms/stain_normalize.yaml +++ /dev/null @@ -1,18 +0,0 @@ -defaults: - - /data/mmci/base@_global_.mmci_stains - - _self_ - -_target_: albumentations.Compose -transforms: - - _target_: prostate_cancer.datamodule.transforms.stain_normalization.build_normalize_staining_transform - stain1: ${stains.hematoxylin} - stain2: ${stains.eosin} - stain3: ${stains.residual} - target_stain1: ${mmci_stains.stains.hematoxylin} - target_stain2: ${mmci_stains.stains.eosin} - target_stain3: ${mmci_stains.stains.residual} - - - _target_: albumentations.Normalize - mean: ${mean} - std: ${std} - max_pixel_value: 1 diff --git a/configs/experiment/test/images/normalized_base.yaml b/configs/experiment/test/images/normalized_base.yaml deleted file mode 100644 index 91605553..00000000 --- a/configs/experiment/test/images/normalized_base.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/test/base - - /datamodule/images_datamodule@datamodule - - /datamodule/transforms/stain_normalize@transforms - - _self_ - -datamodule: - batch_size: 64 - num_workers: 8 - - test: - transforms: ${transforms} - uris: - - ${test_data.tiles_filtered_uri_512} diff --git a/configs/experiment/test/images/resnet50/curves/base.yaml b/configs/experiment/test/images/resnet50/curves/base.yaml deleted file mode 100644 index df1ec0ee..00000000 --- a/configs/experiment/test/images/resnet50/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/resnet50/base - - _self_ - -use_case: "TL Curves" diff --git a/configs/experiment/test/images/resnet50/curves/stain_base.yaml b/configs/experiment/test/images/resnet50/curves/stain_base.yaml deleted file mode 100644 index 3e445080..00000000 --- a/configs/experiment/test/images/resnet50/curves/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/resnet50/stain_base - - _self_ - -use_case: "TL Curves" diff --git a/configs/experiment/test/images/resnet50/nested_metrics/base.yaml b/configs/experiment/test/images/resnet50/nested_metrics/base.yaml deleted file mode 100644 index 4d21ea5c..00000000 --- a/configs/experiment/test/images/resnet50/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/resnet50/base - - _self_ - -use_case: "Per-Slide Metrics" diff --git a/configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml b/configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml deleted file mode 100644 index bda966bc..00000000 --- a/configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/resnet50/stain_base - - _self_ - -use_case: "Per-Slide Metrics" diff --git a/configs/experiment/test/images/resnet50/tile_histograms/base.yaml b/configs/experiment/test/images/resnet50/tile_histograms/base.yaml deleted file mode 100644 index ea44646d..00000000 --- a/configs/experiment/test/images/resnet50/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/resnet50/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml b/configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml deleted file mode 100644 index 9b113e55..00000000 --- a/configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/resnet50/stain_base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/vgg16/curves/base.yaml b/configs/experiment/test/images/vgg16/curves/base.yaml deleted file mode 100644 index fe5fc878..00000000 --- a/configs/experiment/test/images/vgg16/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/vgg16/base - - _self_ - -use_case: "TL Curves" \ No newline at end of file diff --git a/configs/experiment/test/images/vgg16/curves/stain_base.yaml b/configs/experiment/test/images/vgg16/curves/stain_base.yaml deleted file mode 100644 index 5daeb4fb..00000000 --- a/configs/experiment/test/images/vgg16/curves/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/vgg16/stain_base - - _self_ - -use_case: "TL Curves" \ No newline at end of file diff --git a/configs/experiment/test/images/vgg16/nested_metrics/base.yaml b/configs/experiment/test/images/vgg16/nested_metrics/base.yaml deleted file mode 100644 index f38abc29..00000000 --- a/configs/experiment/test/images/vgg16/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/vgg16/base - - _self_ - -use_case: "TL Per-Slide metrics" \ No newline at end of file diff --git a/configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml b/configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml deleted file mode 100644 index 81e92e83..00000000 --- a/configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/vgg16/stain_base - - _self_ - -use_case: "TL Per-Slide metrics" diff --git a/configs/experiment/test/images/vgg16/normalized_base.yaml b/configs/experiment/test/images/vgg16/normalized_base.yaml deleted file mode 100644 index e7bb9abc..00000000 --- a/configs/experiment/test/images/vgg16/normalized_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/test/images/normalized_base - - /model/vgg16_model@model - - /model/checkpoints/mmci_tl/vgg16@model_data - - _self_ - diff --git a/configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml b/configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml deleted file mode 100644 index cbc650f1..00000000 --- a/configs/experiment/test/images/vgg16/test_suite/normalized_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/combined_test_suite@callbacks - - /experiment/test/images/vgg16/normalized_base - - _self_ - -use_case: "TL Test" diff --git a/configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml b/configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml deleted file mode 100644 index 4e2b8ba3..00000000 --- a/configs/experiment/test/images/vgg16/test_suite/normalized_radboud_test.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /experiment/test/images/vgg16/test_suite/normalized_base - - /data/radboud/test@test_data - - _self_ - -checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/vgg16/tile_histograms/base.yaml b/configs/experiment/test/images/vgg16/tile_histograms/base.yaml deleted file mode 100644 index 3780470a..00000000 --- a/configs/experiment/test/images/vgg16/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/vgg16/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml b/configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml deleted file mode 100644 index 6c6913b3..00000000 --- a/configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/vgg16/stain_base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/preprocessing/embeddings/tile_embeddings_v2.py b/preprocessing/embeddings/tile_embeddings_v2.py index 8f715722..877bb4a1 100644 --- a/preprocessing/embeddings/tile_embeddings_v2.py +++ b/preprocessing/embeddings/tile_embeddings_v2.py @@ -86,7 +86,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: if tiles_parquet_dir.exists(): shutil.rmtree(tiles_parquet_dir) - slides_parquet_dir = (output_path / "slides") + slides_parquet_dir = output_path / "slides" if slides_parquet_dir.exists(): shutil.rmtree(slides_parquet_dir) diff --git a/prostate_cancer/vit_model.py b/prostate_cancer/vit_model.py index 9c3b645b..09d98736 100644 --- a/prostate_cancer/vit_model.py +++ b/prostate_cancer/vit_model.py @@ -1,3 +1,6 @@ +from collections.abc import Mapping +from typing import Any + from torch import Tensor, nn from transformers import ViTModel @@ -16,3 +19,10 @@ def forward(self, x: Tensor) -> Tensor: features = self.backbone(x).last_hidden_state logits = self.decode_head(features) return logits + + def load_state_dict( + self, state_dict: Mapping[str, Any], strict: bool = False, assign: bool = False + ) -> Any: + return super().load_state_dict( + state_dict, strict=False, assign=assign + ) # we have one ViT model containing pooler (unused, but present in the checkpoint) From c0e9eeb0cdc2540e239e4419ad5db913992fa026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 15 Jul 2026 09:18:26 +0000 Subject: [PATCH 216/284] feat: drop checkpoint load --- prostate_cancer/vit_model.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/prostate_cancer/vit_model.py b/prostate_cancer/vit_model.py index 09d98736..dac7c77a 100644 --- a/prostate_cancer/vit_model.py +++ b/prostate_cancer/vit_model.py @@ -19,10 +19,3 @@ def forward(self, x: Tensor) -> Tensor: features = self.backbone(x).last_hidden_state logits = self.decode_head(features) return logits - - def load_state_dict( - self, state_dict: Mapping[str, Any], strict: bool = False, assign: bool = False - ) -> Any: - return super().load_state_dict( - state_dict, strict=False, assign=assign - ) # we have one ViT model containing pooler (unused, but present in the checkpoint) From bfa044d3912146ce9a71274ac306279904e9bfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 16 Jul 2026 07:16:10 +0000 Subject: [PATCH 217/284] feat: TL thresholds --- configs/experiment/predict/images/base.yaml | 3 +-- configs/experiment/test/images/base.yaml | 2 +- configs/experiment/train/base.yaml | 6 ------ configs/experiment/train/images/base.yaml | 5 +++++ configs/model/checkpoints/mmci_tl/resnet50.yaml | 2 +- configs/model/checkpoints/mmci_tl/resnet50_stain.yaml | 2 +- configs/model/checkpoints/mmci_tl/vgg16.yaml | 2 +- configs/model/checkpoints/mmci_tl/vgg16_stain.yaml | 2 +- 8 files changed, 11 insertions(+), 13 deletions(-) diff --git a/configs/experiment/predict/images/base.yaml b/configs/experiment/predict/images/base.yaml index f9d913ea..6fe6306d 100644 --- a/configs/experiment/predict/images/base.yaml +++ b/configs/experiment/predict/images/base.yaml @@ -11,5 +11,4 @@ datamodule: predict: uris: - - ${predict_data.tiles_filtered_uri_512} - + - ${predict_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/base.yaml b/configs/experiment/test/images/base.yaml index 803cda61..711fbc27 100644 --- a/configs/experiment/test/images/base.yaml +++ b/configs/experiment/test/images/base.yaml @@ -11,4 +11,4 @@ datamodule: test: uris: - - ${test_data.tiles_filtered_uri_512} \ No newline at end of file + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/base.yaml b/configs/experiment/train/base.yaml index 81fcacc6..718c6512 100644 --- a/configs/experiment/train/base.yaml +++ b/configs/experiment/train/base.yaml @@ -22,12 +22,6 @@ data_name: ${train_data.data_name} datamodule: batch_size: ??? num_workers: ??? - train: - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} model: lr: 1e-4 diff --git a/configs/experiment/train/images/base.yaml b/configs/experiment/train/images/base.yaml index dc9c5267..9c4d1183 100644 --- a/configs/experiment/train/images/base.yaml +++ b/configs/experiment/train/images/base.yaml @@ -11,3 +11,8 @@ datamodule: train: transforms: ${transforms} + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/model/checkpoints/mmci_tl/resnet50.yaml b/configs/model/checkpoints/mmci_tl/resnet50.yaml index 108cd36a..8e36c7ae 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50.yaml @@ -8,7 +8,7 @@ slide_mpm_threshold: 0.324303627 slide_top_k_threshold: 0.4309533238 slide_span_threshold: 0.5 # arbitrary - span predictions are binary -checkpoint: "mlflow-artifacts:/65/aebc892f526047249b972f200bef4381/artifacts/checkpoints/epoch=0-step=4648/checkpoint.ckpt" +checkpoint: "mlflow-artifacts:/65/d9bd39a0207c4b9484ad92574c50b503/artifacts/checkpoints/epoch=0-step=13889/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_checkpoint.ckpt" model_name: "ResNet50" diff --git a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml index 6e894773..6360a262 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml @@ -8,7 +8,7 @@ slide_mpm_threshold: 0.7526295781 slide_top_k_threshold: 0.9902788997 slide_span_threshold: 0.5 # arbitrary - span predictions are binary -checkpoint: "mlflow-artifacts:/65/0d6af27cf5d848279c891eb3039b11c8/artifacts/checkpoints/epoch=0-step=6972/checkpoint.ckpt" +checkpoint: "mlflow-artifacts:/65/80543b4ec2d14ca48384e6d52a7418fb/artifacts/checkpoints/epoch=0-step=55556/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_stain_checkpoint.ckpt" model_name: "ResNet50 Stain" diff --git a/configs/model/checkpoints/mmci_tl/vgg16.yaml b/configs/model/checkpoints/mmci_tl/vgg16.yaml index 78dbca99..fd51eeda 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16.yaml @@ -8,7 +8,7 @@ slide_mpm_threshold: 0.9917962551 slide_top_k_threshold: 0.9871470928 slide_span_threshold: 0.5 # arbitrary - span predictions are binary -checkpoint: "mlflow-artifacts:/65/7b52930515c14710855962f8882fb4d3/artifacts/checkpoints/epoch=0-step=9296/checkpoint.ckpt" +checkpoint: "mlflow-artifacts:/65/2b4986681bb24e1abd85a9108a7a49bb/artifacts/checkpoints/epoch=1-step=97226/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_checkpoint.ckpt" model_name: "VGG-16" diff --git a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml index 4ad8eb63..b2b153f7 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml @@ -8,7 +8,7 @@ slide_mpm_threshold: 0.9827958941 slide_top_k_threshold: 0.9819370508 slide_span_threshold: 0.5 # arbitrary - span predictions are binary -checkpoint: "mlflow-artifacts:/65/3d053bb91d4841e28afe2c93dfe0b273/artifacts/checkpoints/epoch=2-step=25564/checkpoint.ckpt" +checkpoint: "mlflow-artifacts:/65/98e0d43027664084aef1beebaeac7fff/artifacts/checkpoints/epoch=1-step=111115/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_stain_checkpoint.ckpt" From c7125501845a6aca8cd7efdb1ada26d011842da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 16 Jul 2026 08:48:04 +0000 Subject: [PATCH 218/284] feat: estimations --- .../experiment/predict/images/resnet50/mpm_estimation.yaml | 4 ++-- .../experiment/predict/images/resnet50/span_estimation.yaml | 4 ++-- .../predict/images/resnet50/stain_mpm_estimation.yaml | 4 ++-- .../predict/images/resnet50/stain_span_estimation.yaml | 4 ++-- .../predict/images/resnet50/stain_top_k_estimation.yaml | 4 ++-- .../experiment/predict/images/resnet50/top_k_estimation.yaml | 4 ++-- configs/experiment/predict/images/vgg16/mpm_estimation.yaml | 4 ++-- configs/experiment/predict/images/vgg16/span_estimation.yaml | 4 ++-- .../experiment/predict/images/vgg16/stain_mpm_estimation.yaml | 4 ++-- .../predict/images/vgg16/stain_span_estimation.yaml | 4 ++-- .../predict/images/vgg16/stain_top_k_estimation.yaml | 4 ++-- configs/experiment/predict/images/vgg16/top_k_estimation.yaml | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/configs/experiment/predict/images/resnet50/mpm_estimation.yaml b/configs/experiment/predict/images/resnet50/mpm_estimation.yaml index edc6fce5..80b81078 100644 --- a/configs/experiment/predict/images/resnet50/mpm_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/mpm_estimation.yaml @@ -11,8 +11,8 @@ callbacks: aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5, 6, 7] diff --git a/configs/experiment/predict/images/resnet50/span_estimation.yaml b/configs/experiment/predict/images/resnet50/span_estimation.yaml index 769f8347..b7e12a39 100644 --- a/configs/experiment/predict/images/resnet50/span_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/span_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 cell_threshold: ${model_data.tile_threshold} to_estimate: k: [2, 3, 4, 5] diff --git a/configs/experiment/predict/images/resnet50/stain_mpm_estimation.yaml b/configs/experiment/predict/images/resnet50/stain_mpm_estimation.yaml index 4b2500f0..255ce978 100644 --- a/configs/experiment/predict/images/resnet50/stain_mpm_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/stain_mpm_estimation.yaml @@ -11,8 +11,8 @@ callbacks: aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5, 6, 7] diff --git a/configs/experiment/predict/images/resnet50/stain_span_estimation.yaml b/configs/experiment/predict/images/resnet50/stain_span_estimation.yaml index e492e813..4c8237c0 100644 --- a/configs/experiment/predict/images/resnet50/stain_span_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/stain_span_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 cell_threshold: ${model_data.tile_threshold} to_estimate: k: [2, 3, 4, 5] diff --git a/configs/experiment/predict/images/resnet50/stain_top_k_estimation.yaml b/configs/experiment/predict/images/resnet50/stain_top_k_estimation.yaml index c4c54b97..f1d0beee 100644 --- a/configs/experiment/predict/images/resnet50/stain_top_k_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/stain_top_k_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5] k: [2, 3, 4] diff --git a/configs/experiment/predict/images/resnet50/top_k_estimation.yaml b/configs/experiment/predict/images/resnet50/top_k_estimation.yaml index 71dedc59..ad646c46 100644 --- a/configs/experiment/predict/images/resnet50/top_k_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/top_k_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5] k: [2, 3, 4] diff --git a/configs/experiment/predict/images/vgg16/mpm_estimation.yaml b/configs/experiment/predict/images/vgg16/mpm_estimation.yaml index bce6e294..d73ef67d 100644 --- a/configs/experiment/predict/images/vgg16/mpm_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/mpm_estimation.yaml @@ -11,8 +11,8 @@ callbacks: aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5, 6, 7] diff --git a/configs/experiment/predict/images/vgg16/span_estimation.yaml b/configs/experiment/predict/images/vgg16/span_estimation.yaml index d24a26d8..185906a0 100644 --- a/configs/experiment/predict/images/vgg16/span_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/span_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 cell_threshold: ${model_data.tile_threshold} to_estimate: k: [2, 3, 4, 5] diff --git a/configs/experiment/predict/images/vgg16/stain_mpm_estimation.yaml b/configs/experiment/predict/images/vgg16/stain_mpm_estimation.yaml index aaea610d..b9fb9248 100644 --- a/configs/experiment/predict/images/vgg16/stain_mpm_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/stain_mpm_estimation.yaml @@ -11,8 +11,8 @@ callbacks: aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5, 6, 7] diff --git a/configs/experiment/predict/images/vgg16/stain_span_estimation.yaml b/configs/experiment/predict/images/vgg16/stain_span_estimation.yaml index 69b13b48..c674cd13 100644 --- a/configs/experiment/predict/images/vgg16/stain_span_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/stain_span_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 cell_threshold: ${model_data.tile_threshold} to_estimate: k: [2, 3, 4, 5] diff --git a/configs/experiment/predict/images/vgg16/stain_top_k_estimation.yaml b/configs/experiment/predict/images/vgg16/stain_top_k_estimation.yaml index 94e64b97..44bdde00 100644 --- a/configs/experiment/predict/images/vgg16/stain_top_k_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/stain_top_k_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5] k: [2, 3, 4] diff --git a/configs/experiment/predict/images/vgg16/top_k_estimation.yaml b/configs/experiment/predict/images/vgg16/top_k_estimation.yaml index ed8d9a40..ee387894 100644 --- a/configs/experiment/predict/images/vgg16/top_k_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/top_k_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator static: - extent_tile: 512 - stride_tile: 256 + extent_tile: 224 + stride_tile: 112 to_estimate: kernel_size: [1, 2, 3, 4, 5] k: [2, 3, 4] From 9bdcb5e20ff2b912363ff8c16a35ccbbcac2c621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 16 Jul 2026 12:01:30 +0000 Subject: [PATCH 219/284] feat: tl est --- configs/callbacks/curves.yaml | 2 +- configs/callbacks/curves_mil.yaml | 4 +++ configs/callbacks/tile_histograms.yaml | 2 +- configs/callbacks/tile_histograms_mil.yaml | 2 ++ configs/callbacks/tile_threshold_mil.yaml | 7 +++++ ...es_callback.py => curves_callback_base.py} | 12 ++++---- .../callbacks/curves_callback_mil.py | 22 ++++++++++++++ .../callbacks/curves_callback_tl.py | 21 ++++++++++++++ ...ck.py => tile_histograms_callback_base.py} | 16 ++++------ .../callbacks/tile_histograms_callback_mil.py | 29 +++++++++++++++++++ .../callbacks/tile_histograms_callback_tl.py | 26 +++++++++++++++++ 11 files changed, 123 insertions(+), 20 deletions(-) create mode 100644 configs/callbacks/curves_mil.yaml create mode 100644 configs/callbacks/tile_histograms_mil.yaml create mode 100644 configs/callbacks/tile_threshold_mil.yaml rename prostate_cancer/callbacks/{curves_callback.py => curves_callback_base.py} (94%) create mode 100644 prostate_cancer/callbacks/curves_callback_mil.py create mode 100644 prostate_cancer/callbacks/curves_callback_tl.py rename prostate_cancer/callbacks/{tile_histograms_callback.py => tile_histograms_callback_base.py} (84%) create mode 100644 prostate_cancer/callbacks/tile_histograms_callback_mil.py create mode 100644 prostate_cancer/callbacks/tile_histograms_callback_tl.py diff --git a/configs/callbacks/curves.yaml b/configs/callbacks/curves.yaml index d848857b..7cd9d2dc 100644 --- a/configs/callbacks/curves.yaml +++ b/configs/callbacks/curves.yaml @@ -1,4 +1,4 @@ curves_callback: - _target_: prostate_cancer.callbacks.CurvesCallback + _target_: prostate_cancer.callbacks.CurvesCallbackTile threshold: ${tile_threshold} optimal_seek: false diff --git a/configs/callbacks/curves_mil.yaml b/configs/callbacks/curves_mil.yaml new file mode 100644 index 00000000..ee55fe20 --- /dev/null +++ b/configs/callbacks/curves_mil.yaml @@ -0,0 +1,4 @@ +curves_callback: + _target_: prostate_cancer.callbacks.CurvesCallbackMIL + threshold: ${tile_threshold} + optimal_seek: false diff --git a/configs/callbacks/tile_histograms.yaml b/configs/callbacks/tile_histograms.yaml index 16a4bfd0..8f1e765b 100644 --- a/configs/callbacks/tile_histograms.yaml +++ b/configs/callbacks/tile_histograms.yaml @@ -1,2 +1,2 @@ tile_histograms_callback: - _target_: prostate_cancer.callbacks.TileHistogramsCallback + _target_: prostate_cancer.callbacks.TileHistogramsCallbackTile diff --git a/configs/callbacks/tile_histograms_mil.yaml b/configs/callbacks/tile_histograms_mil.yaml new file mode 100644 index 00000000..87f8a110 --- /dev/null +++ b/configs/callbacks/tile_histograms_mil.yaml @@ -0,0 +1,2 @@ +tile_histograms_callback: + _target_: prostate_cancer.callbacks.TileHistogramsCallbackMIL diff --git a/configs/callbacks/tile_threshold_mil.yaml b/configs/callbacks/tile_threshold_mil.yaml new file mode 100644 index 00000000..5ac964c4 --- /dev/null +++ b/configs/callbacks/tile_threshold_mil.yaml @@ -0,0 +1,7 @@ +defaults: + - tile_histograms_mil + - curves_mil + - _self_ + +curves_callback: + optimal_seek: true diff --git a/prostate_cancer/callbacks/curves_callback.py b/prostate_cancer/callbacks/curves_callback_base.py similarity index 94% rename from prostate_cancer/callbacks/curves_callback.py rename to prostate_cancer/callbacks/curves_callback_base.py index d5d58649..8ff09761 100644 --- a/prostate_cancer/callbacks/curves_callback.py +++ b/prostate_cancer/callbacks/curves_callback_base.py @@ -1,3 +1,4 @@ +from abc import ABC, abstractmethod from typing import Any import mlflow @@ -8,10 +9,9 @@ from sklearn.metrics import auc, precision_recall_curve, roc_curve from postprocessing.slide_level_curves import _plot_curve -from prostate_cancer.typing import LabeledTileSampleBatch -class CurvesCallback(Callback): +class CurvesCallbackBase(Callback, ABC): def __init__(self, threshold: float, optimal_seek: bool = True) -> None: """This callback creates tile-level ROC curve and Precision-Recall curve and marks selected + optimized thresholds used for metric computation. @@ -25,18 +25,16 @@ def __init__(self, threshold: float, optimal_seek: bool = True) -> None: self.preds: list[torch.Tensor] = [] self.targets: list[torch.Tensor] = [] + @abstractmethod def on_test_batch_end( self, trainer: Trainer, pl_module: LightningModule, outputs: Any, - batch: LabeledTileSampleBatch, + batch: Any, batch_idx: int, dataloader_idx: int = 0, - ) -> None: - targets = batch[1] - self.preds.append(outputs.cpu()) - self.targets.append(targets.cpu()) + ) -> None: ... def _plot_roc( self, y_pred: NDArray[np.float32], y_true: NDArray[np.float32] diff --git a/prostate_cancer/callbacks/curves_callback_mil.py b/prostate_cancer/callbacks/curves_callback_mil.py new file mode 100644 index 00000000..7124a2db --- /dev/null +++ b/prostate_cancer/callbacks/curves_callback_mil.py @@ -0,0 +1,22 @@ +from lightning import LightningModule, Trainer + +from prostate_cancer.callbacks.curves_callback_base import CurvesCallbackBase +from prostate_cancer.typing import LabeledBagOfTilesSampleBatch, MILModelOutput + + +class CurvesCallbackMIL(CurvesCallbackBase): + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, # type: ignore[override] + batch: LabeledBagOfTilesSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + + _, tl_outputs_raw, mask, _ = outputs + tl_outputs_valid = tl_outputs_raw[mask.bool()] + targets = batch[1][mask.bool()] + self.preds.append(tl_outputs_valid.cpu()) + self.targets.append(targets.cpu()) diff --git a/prostate_cancer/callbacks/curves_callback_tl.py b/prostate_cancer/callbacks/curves_callback_tl.py new file mode 100644 index 00000000..e8223a9a --- /dev/null +++ b/prostate_cancer/callbacks/curves_callback_tl.py @@ -0,0 +1,21 @@ +from typing import Any + +from lightning import LightningModule, Trainer + +from prostate_cancer.callbacks.curves_callback_base import CurvesCallbackBase +from prostate_cancer.typing import LabeledTileSampleBatch + + +class CurvesCallbackTile(CurvesCallbackBase): + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: Any, + batch: LabeledTileSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + targets = batch[1] + self.preds.append(outputs.cpu()) + self.targets.append(targets.cpu()) diff --git a/prostate_cancer/callbacks/tile_histograms_callback.py b/prostate_cancer/callbacks/tile_histograms_callback_base.py similarity index 84% rename from prostate_cancer/callbacks/tile_histograms_callback.py rename to prostate_cancer/callbacks/tile_histograms_callback_base.py index ca304609..3db47fe9 100644 --- a/prostate_cancer/callbacks/tile_histograms_callback.py +++ b/prostate_cancer/callbacks/tile_histograms_callback_base.py @@ -1,3 +1,4 @@ +from abc import ABC, abstractmethod from pathlib import Path from typing import Any @@ -8,31 +9,24 @@ from numpy.typing import NDArray from rationai.mlkit.lightning.loggers import MLFlowLogger -from prostate_cancer.typing import LabeledTileSampleBatch - -class TileHistogramsCallback(Callback): +class TileHistogramsCallbackBase(Callback, ABC): def __init__(self) -> None: """This callback creates prediction histograms for both negative and positive distribution of tiles.""" super().__init__() self.all_preds: list[NDArray[np.floating]] = [] self.all_labels: list[NDArray[np.floating]] = [] + @abstractmethod def on_test_batch_end( self, trainer: Trainer, pl_module: LightningModule, outputs: Any, - batch: LabeledTileSampleBatch, + batch: Any, batch_idx: int, dataloader_idx: int = 0, - ) -> None: - _, y, _ = batch - preds = outputs.detach().cpu().numpy().flatten() - labels = y.detach().cpu().numpy().flatten() - - self.all_preds.append(preds) - self.all_labels.append(labels) + ) -> None: ... def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: assert isinstance(trainer.logger, MLFlowLogger) diff --git a/prostate_cancer/callbacks/tile_histograms_callback_mil.py b/prostate_cancer/callbacks/tile_histograms_callback_mil.py new file mode 100644 index 00000000..d61dbf6e --- /dev/null +++ b/prostate_cancer/callbacks/tile_histograms_callback_mil.py @@ -0,0 +1,29 @@ +from lightning import LightningModule, Trainer + +from prostate_cancer.callbacks.tile_histograms_callback_base import ( + TileHistogramsCallbackBase, +) +from prostate_cancer.typing import LabeledBagOfTilesSampleBatch, MILModelOutput + + +class TileHistogramsCallbackMIL(TileHistogramsCallbackBase): + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, # type: ignore[override] + batch: LabeledBagOfTilesSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + _, y, _, _ = batch + _, tl_outputs_raw, mask, _ = outputs + + tl_outputs_valid = tl_outputs_raw[mask.bool()] + preds = tl_outputs_valid.detach().cpu().numpy().flatten() + + labels_valid = y[mask.bool()] + labels = labels_valid.detach().cpu().numpy().flatten() + + self.all_preds.append(preds) + self.all_labels.append(labels) diff --git a/prostate_cancer/callbacks/tile_histograms_callback_tl.py b/prostate_cancer/callbacks/tile_histograms_callback_tl.py new file mode 100644 index 00000000..be49aec8 --- /dev/null +++ b/prostate_cancer/callbacks/tile_histograms_callback_tl.py @@ -0,0 +1,26 @@ +from typing import Any + +from lightning import LightningModule, Trainer + +from prostate_cancer.callbacks.tile_histograms_callback_base import ( + TileHistogramsCallbackBase, +) +from prostate_cancer.typing import LabeledTileSampleBatch + + +class TileHistogramsCallbackTile(TileHistogramsCallbackBase): + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: Any, + batch: LabeledTileSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + _, y, _ = batch + preds = outputs.detach().cpu().numpy().flatten() + labels = y.detach().cpu().numpy().flatten() + + self.all_preds.append(preds) + self.all_labels.append(labels) From 77b21a4bb789ece66d6cd1016d24637c9cc64372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 16 Jul 2026 12:10:19 +0000 Subject: [PATCH 220/284] feat: configs --- configs/experiment/test/bag_of_embeddings/base.yaml | 10 ++++++++++ .../experiment/test/bag_of_embeddings/pgp/base.yaml | 12 ++++++++++++ .../test/bag_of_embeddings/pgp/tile_threshold.yaml | 10 ++++++++++ .../test/bag_of_embeddings/virchow2/base.yaml | 12 ++++++++++++ .../bag_of_embeddings/virchow2/tile_threshold.yaml | 10 ++++++++++ configs/experiment/train/bag_of_embeddings/pgp.yaml | 2 +- .../experiment/train/bag_of_embeddings/virchow2.yaml | 2 +- .../mmci_tl/{pgp_mil.yaml => pgp_hybrid_mil.yaml} | 0 .../{virchow2_mil.yaml => virchow2_hybrid_mil.yaml} | 0 ...{pgp_mil_model.yaml => pgp_hybrid_mil_model.yaml} | 0 ...mil_model.yaml => virchow2_hybrid_mil_model.yaml} | 0 11 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 configs/experiment/test/bag_of_embeddings/base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/tile_threshold.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml rename configs/model/checkpoints/mmci_tl/{pgp_mil.yaml => pgp_hybrid_mil.yaml} (100%) rename configs/model/checkpoints/mmci_tl/{virchow2_mil.yaml => virchow2_hybrid_mil.yaml} (100%) rename configs/model/{pgp_mil_model.yaml => pgp_hybrid_mil_model.yaml} (100%) rename configs/model/{virchow2_mil_model.yaml => virchow2_hybrid_mil_model.yaml} (100%) diff --git a/configs/experiment/test/bag_of_embeddings/base.yaml b/configs/experiment/test/bag_of_embeddings/base.yaml new file mode 100644 index 00000000..6ca0fd75 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/base.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /experiment/test/base + - /datamodule/bag_of_embeddings_datamodule@datamodule + - _self_ + +datamodule: + batch_size: 4 + num_workers: 8 diff --git a/configs/experiment/test/bag_of_embeddings/pgp/base.yaml b/configs/experiment/test/bag_of_embeddings/pgp/base.yaml new file mode 100644 index 00000000..99151a94 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/base + - /model/pgp_hybird_mil_model@model + - /model/checkpoints/mmci_tl/pgp_hybrid_mil@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_w_pgp_uri_224} diff --git a/configs/experiment/test/bag_of_embeddings/pgp/tile_threshold.yaml b/configs/experiment/test/bag_of_embeddings/pgp/tile_threshold.yaml new file mode 100644 index 00000000..1e133ddd --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/tile_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold_mil@callbacks + - /experiment/test/bag_of_embeddings/pgp/base + - /data/mmci/tl_val@test_data + - _self_ + +tile_threshold: 0.9 # Pathologist's estimate for other models +use_case: "TL Threshold Estimation" diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/base.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/base.yaml new file mode 100644 index 00000000..c90e5378 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/base + - /model/virchow2_hybrid_mil_model@model + - /model/checkpoints/mmci_tl/virchow2_hybrid_mil@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_w_virchow2_uri_224} diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml new file mode 100644 index 00000000..e2909112 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/bag_of_embeddings/virchow2/base + - /data/mmci/tl_val@test_data + - _self_ + +tile_threshold: 0.9 # Pathologist's estimate for other models +use_case: "TL Threshold Estimation" diff --git a/configs/experiment/train/bag_of_embeddings/pgp.yaml b/configs/experiment/train/bag_of_embeddings/pgp.yaml index a3be9998..8c4439d0 100644 --- a/configs/experiment/train/bag_of_embeddings/pgp.yaml +++ b/configs/experiment/train/bag_of_embeddings/pgp.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/train/bag_of_embeddings/base - - /model/pgp_mil_model@model + - /model/pgp_hybrid_mil_model@model - _self_ datamodule: diff --git a/configs/experiment/train/bag_of_embeddings/virchow2.yaml b/configs/experiment/train/bag_of_embeddings/virchow2.yaml index 4a3674e8..15adeb39 100644 --- a/configs/experiment/train/bag_of_embeddings/virchow2.yaml +++ b/configs/experiment/train/bag_of_embeddings/virchow2.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/train/bag_of_embeddings/base - - /model/virchow2_mil_model@model + - /model/virchow2_hybrid_mil_model@model - _self_ datamodule: diff --git a/configs/model/checkpoints/mmci_tl/pgp_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml similarity index 100% rename from configs/model/checkpoints/mmci_tl/pgp_mil.yaml rename to configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml diff --git a/configs/model/checkpoints/mmci_tl/virchow2_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml similarity index 100% rename from configs/model/checkpoints/mmci_tl/virchow2_mil.yaml rename to configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml diff --git a/configs/model/pgp_mil_model.yaml b/configs/model/pgp_hybrid_mil_model.yaml similarity index 100% rename from configs/model/pgp_mil_model.yaml rename to configs/model/pgp_hybrid_mil_model.yaml diff --git a/configs/model/virchow2_mil_model.yaml b/configs/model/virchow2_hybrid_mil_model.yaml similarity index 100% rename from configs/model/virchow2_mil_model.yaml rename to configs/model/virchow2_hybrid_mil_model.yaml From db8421383bb20966666d6b740d54d226ef8e954d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 16 Jul 2026 15:44:50 +0000 Subject: [PATCH 221/284] feat: update TL thresholds --- configs/model/checkpoints/mmci_tl/resnet50.yaml | 2 +- configs/model/checkpoints/mmci_tl/resnet50_stain.yaml | 2 +- configs/model/checkpoints/mmci_tl/vgg16.yaml | 2 +- configs/model/checkpoints/mmci_tl/vgg16_stain.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/resnet50.yaml b/configs/model/checkpoints/mmci_tl/resnet50.yaml index 8e36c7ae..b77df70d 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50.yaml @@ -2,7 +2,7 @@ defaults: - base - _self_ -tile_threshold: 0.44 +tile_threshold: 0.82 slide_max_threshold: 0.9540096521 slide_mpm_threshold: 0.324303627 slide_top_k_threshold: 0.4309533238 diff --git a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml index 6360a262..a73c0a86 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml @@ -2,7 +2,7 @@ defaults: - base - _self_ -tile_threshold: 0.76 +tile_threshold: 0.85 slide_max_threshold: 0.9963486195 slide_mpm_threshold: 0.7526295781 slide_top_k_threshold: 0.9902788997 diff --git a/configs/model/checkpoints/mmci_tl/vgg16.yaml b/configs/model/checkpoints/mmci_tl/vgg16.yaml index fd51eeda..1ad5f4cf 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16.yaml @@ -2,7 +2,7 @@ defaults: - base - _self_ -tile_threshold: 0.94 +tile_threshold: 0.96 slide_max_threshold: 0.9917962551 slide_mpm_threshold: 0.9917962551 slide_top_k_threshold: 0.9871470928 diff --git a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml index b2b153f7..6c136dd2 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml @@ -2,7 +2,7 @@ defaults: - base - _self_ -tile_threshold: 0.73 +tile_threshold: 0.93 slide_max_threshold: 0.9859181643 slide_mpm_threshold: 0.9827958941 slide_top_k_threshold: 0.9819370508 From 9be08ece7a9622536c300f9cf47c5ca990bbf76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 09:03:09 +0000 Subject: [PATCH 222/284] feat: add new est. runs --- configs/model/checkpoints/mmci_tl/resnet50.yaml | 8 ++++---- configs/model/checkpoints/mmci_tl/resnet50_stain.yaml | 8 ++++---- configs/model/checkpoints/mmci_tl/vgg16.yaml | 8 ++++---- configs/model/checkpoints/mmci_tl/vgg16_stain.yaml | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/resnet50.yaml b/configs/model/checkpoints/mmci_tl/resnet50.yaml index b77df70d..7c4c761d 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50.yaml @@ -14,10 +14,10 @@ public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifa model_name: "ResNet50" slide_level_prediction_uris: mean_pool_max: - mmci_sl_group_1: "mlflow-artifacts:/65/46a594a334ef4bbe9fdc7ba73250b12a/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/e87e9b41dfaf4a878069af2045f164ef/artifacts/tables/aggregated_predictions.json" max: - mmci_sl_group_1: "mlflow-artifacts:/65/94561b6417ea4f888495fdcb799555e2/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/098f0fb5aad246389e39bfd2265247a4/artifacts/tables/aggregated_predictions.json" top_k: - mmci_sl_group_1: "mlflow-artifacts:/65/bcf0312c738c460381bde996e5cb93d8/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/f79c58db2f9f48148a61c9948bd04541/artifacts/tables/aggregated_predictions.json" span: - mmci_sl_group_1: "mlflow-artifacts:/65/ccd4a8001874439f8c860eefa5d41fbe/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/423dd217a0b94fbabaee1e1bdda156d8/artifacts/tables/aggregated_predictions.json" diff --git a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml index a73c0a86..c9cab50f 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml @@ -14,10 +14,10 @@ public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifa model_name: "ResNet50 Stain" slide_level_prediction_uris: mean_pool_max: - mmci_sl_group_1: "mlflow-artifacts:/65/bdbd2d8aa5fa44a581281f2396afa4a7/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/a6e128babae44dab9d2785624e0107a0/artifacts/tables/aggregated_predictions.json" max: - mmci_sl_group_1: "mlflow-artifacts:/65/b982f0b741b442a5b7c7f16cae8fb155/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/eec60275b3c247a2b8fc486d1a46aa6f/artifacts/tables/aggregated_predictions.json" top_k: - mmci_sl_group_1: "mlflow-artifacts:/65/51be8706f3fb401ab455ecf2c7b64b93/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/d97f25bcecc24ad8bd223b3ae0af970f/artifacts/tables/aggregated_predictions.json" span: - mmci_sl_group_1: "mlflow-artifacts:/65/647659f90b8144989062a73882e50d1f/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/254fc6852ea845c1b11d799e5d24d947/artifacts/tables/aggregated_predictions.json" diff --git a/configs/model/checkpoints/mmci_tl/vgg16.yaml b/configs/model/checkpoints/mmci_tl/vgg16.yaml index 1ad5f4cf..bb517841 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16.yaml @@ -14,10 +14,10 @@ public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifa model_name: "VGG-16" slide_level_prediction_uris: mean_pool_max: - mmci_sl_group_1: "mlflow-artifacts:/65/1a1eeddaf5864f6aa8b0d9e8a7f887ec/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/8ee0c7d3c61d46acad41262ce6a065ec/artifacts/tables/aggregated_predictions.json" max: - mmci_sl_group_1: "mlflow-artifacts:/65/4c57d5df00fb4b26baa7ae761f4824c8/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/8b607e5204f548d28d11ac392fd4a835/artifacts/tables/aggregated_predictions.json" top_k: - mmci_sl_group_1: "mlflow-artifacts:/65/3e2d76776e2e4ce5ac25041d7b97fd7d/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/17b3a4b3c9bc420ca4fff7c185063f36/artifacts/tables/aggregated_predictions.json" span: - mmci_sl_group_1: "mlflow-artifacts:/65/1981e514a7be46d7a1188bd93c34eefe/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/ce1bdc240623403aa45cca7465d47b29/artifacts/tables/aggregated_predictions.json" diff --git a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml index 6c136dd2..138cef8b 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml @@ -15,10 +15,10 @@ public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifa model_name: "VGG-16 Stain" slide_level_prediction_uris: mean_pool_max: - mmci_sl_group_1: "mlflow-artifacts:/65/de09807020fa4d4ca15b840692fedf82/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/b7a34bdffb2b4f729b5c8ce98d9921ce/artifacts/tables/aggregated_predictions.json" max: - mmci_sl_group_1: "mlflow-artifacts:/65/1c08494a5de648d4b7f025aa89ac1be9/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/a7495ce0ac2b4fb99d0cc55e912a970c/artifacts/tables/aggregated_predictions.json" top_k: - mmci_sl_group_1: "mlflow-artifacts:/65/d63f6775ddf44a4c93a934298fe10d22/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/2bf6522353e34344aed0222c346f7230/artifacts/tables/aggregated_predictions.json" span: - mmci_sl_group_1: "mlflow-artifacts:/65/8d45a014dece415bababfabda2981a3f/artifacts/tables/aggregated_predictions_corrected.json" + mmci_sl_group_1: "mlflow-artifacts:/65/a392285e4bde413bb8bf03e03d921d71/artifacts/tables/aggregated_predictions.json" From da35f0ae61c6eb270abab13321e5b8b8f972f563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 09:16:20 +0000 Subject: [PATCH 223/284] feat: rename to 224x224 --- .../mmci_tl/{resnet50.yaml => resnet50_224.yaml} | 8 ++++---- .../{resnet50_stain.yaml => resnet50_stain_224.yaml} | 8 ++++---- .../checkpoints/mmci_tl/{vgg16.yaml => vgg16_224.yaml} | 8 ++++---- .../mmci_tl/{vgg16_stain.yaml => vgg16_stain_224.yaml} | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) rename configs/model/checkpoints/mmci_tl/{resnet50.yaml => resnet50_224.yaml} (87%) rename configs/model/checkpoints/mmci_tl/{resnet50_stain.yaml => resnet50_stain_224.yaml} (87%) rename configs/model/checkpoints/mmci_tl/{vgg16.yaml => vgg16_224.yaml} (87%) rename configs/model/checkpoints/mmci_tl/{vgg16_stain.yaml => vgg16_stain_224.yaml} (87%) diff --git a/configs/model/checkpoints/mmci_tl/resnet50.yaml b/configs/model/checkpoints/mmci_tl/resnet50_224.yaml similarity index 87% rename from configs/model/checkpoints/mmci_tl/resnet50.yaml rename to configs/model/checkpoints/mmci_tl/resnet50_224.yaml index 7c4c761d..49911bc4 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_224.yaml @@ -3,15 +3,15 @@ defaults: - _self_ tile_threshold: 0.82 -slide_max_threshold: 0.9540096521 -slide_mpm_threshold: 0.324303627 -slide_top_k_threshold: 0.4309533238 +slide_max_threshold: 0 +slide_mpm_threshold: 0 +slide_top_k_threshold: 0 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/d9bd39a0207c4b9484ad92574c50b503/artifacts/checkpoints/epoch=0-step=13889/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_checkpoint.ckpt" -model_name: "ResNet50" +model_name: "ResNet50 (224x224)" slide_level_prediction_uris: mean_pool_max: mmci_sl_group_1: "mlflow-artifacts:/65/e87e9b41dfaf4a878069af2045f164ef/artifacts/tables/aggregated_predictions.json" diff --git a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml b/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml similarity index 87% rename from configs/model/checkpoints/mmci_tl/resnet50_stain.yaml rename to configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml index c9cab50f..e139901a 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml @@ -3,15 +3,15 @@ defaults: - _self_ tile_threshold: 0.85 -slide_max_threshold: 0.9963486195 -slide_mpm_threshold: 0.7526295781 -slide_top_k_threshold: 0.9902788997 +slide_max_threshold: 0 +slide_mpm_threshold: 0 +slide_top_k_threshold: 0 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/80543b4ec2d14ca48384e6d52a7418fb/artifacts/checkpoints/epoch=0-step=55556/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_stain_checkpoint.ckpt" -model_name: "ResNet50 Stain" +model_name: "ResNet50 Stain (224x224)" slide_level_prediction_uris: mean_pool_max: mmci_sl_group_1: "mlflow-artifacts:/65/a6e128babae44dab9d2785624e0107a0/artifacts/tables/aggregated_predictions.json" diff --git a/configs/model/checkpoints/mmci_tl/vgg16.yaml b/configs/model/checkpoints/mmci_tl/vgg16_224.yaml similarity index 87% rename from configs/model/checkpoints/mmci_tl/vgg16.yaml rename to configs/model/checkpoints/mmci_tl/vgg16_224.yaml index bb517841..cd4df99d 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_224.yaml @@ -3,15 +3,15 @@ defaults: - _self_ tile_threshold: 0.96 -slide_max_threshold: 0.9917962551 -slide_mpm_threshold: 0.9917962551 -slide_top_k_threshold: 0.9871470928 +slide_max_threshold: 0 +slide_mpm_threshold: 0 +slide_top_k_threshold: 0 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/2b4986681bb24e1abd85a9108a7a49bb/artifacts/checkpoints/epoch=1-step=97226/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_checkpoint.ckpt" -model_name: "VGG-16" +model_name: "VGG-16 (224x224)" slide_level_prediction_uris: mean_pool_max: mmci_sl_group_1: "mlflow-artifacts:/65/8ee0c7d3c61d46acad41262ce6a065ec/artifacts/tables/aggregated_predictions.json" diff --git a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml b/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml similarity index 87% rename from configs/model/checkpoints/mmci_tl/vgg16_stain.yaml rename to configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml index 138cef8b..60e020fc 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml @@ -3,16 +3,16 @@ defaults: - _self_ tile_threshold: 0.93 -slide_max_threshold: 0.9859181643 -slide_mpm_threshold: 0.9827958941 -slide_top_k_threshold: 0.9819370508 +slide_max_threshold: 0 +slide_mpm_threshold: 0 +slide_top_k_threshold: 0 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/98e0d43027664084aef1beebaeac7fff/artifacts/checkpoints/epoch=1-step=111115/checkpoint.ckpt" public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_stain_checkpoint.ckpt" -model_name: "VGG-16 Stain" +model_name: "VGG-16 Stain (224x224)" slide_level_prediction_uris: mean_pool_max: mmci_sl_group_1: "mlflow-artifacts:/65/b7a34bdffb2b4f729b5c8ce98d9921ce/artifacts/tables/aggregated_predictions.json" From 2089426bbefd19d4bdaa7c58995091e3ee3e112c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 09:22:01 +0000 Subject: [PATCH 224/284] feat: revert to 512x512 --- configs/experiment/train/images/base.yaml | 5 ----- configs/experiment/train/images/google_vit/base.yaml | 8 ++++++++ .../experiment/train/images/google_vit/base_stain.yaml | 8 ++++++++ configs/experiment/train/images/pgp/base.yaml | 8 ++++++++ configs/experiment/train/images/resnet50/base.yaml | 8 ++++++++ configs/experiment/train/images/resnet50/base_stain.yaml | 8 ++++++++ configs/experiment/train/images/vgg16/base.yaml | 8 ++++++++ configs/experiment/train/images/vgg16/base_stain.yaml | 8 ++++++++ configs/experiment/train/images/virchow2/base.yaml | 8 ++++++++ 9 files changed, 64 insertions(+), 5 deletions(-) diff --git a/configs/experiment/train/images/base.yaml b/configs/experiment/train/images/base.yaml index 9c4d1183..dc9c5267 100644 --- a/configs/experiment/train/images/base.yaml +++ b/configs/experiment/train/images/base.yaml @@ -11,8 +11,3 @@ datamodule: train: transforms: ${transforms} - uris: - - ${train_data.tiles_filtered_uri_224} - val: - uris: - - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/google_vit/base.yaml b/configs/experiment/train/images/google_vit/base.yaml index ad84f3f5..d5530967 100644 --- a/configs/experiment/train/images/google_vit/base.yaml +++ b/configs/experiment/train/images/google_vit/base.yaml @@ -7,3 +7,11 @@ defaults: - _self_ model_name: "Google ViT" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/google_vit/base_stain.yaml b/configs/experiment/train/images/google_vit/base_stain.yaml index 0ee0dc4b..80ff80a8 100644 --- a/configs/experiment/train/images/google_vit/base_stain.yaml +++ b/configs/experiment/train/images/google_vit/base_stain.yaml @@ -7,3 +7,11 @@ defaults: - _self_ model_name: "Google ViT (Stain)" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/pgp/base.yaml b/configs/experiment/train/images/pgp/base.yaml index 46ab3eac..8342ffd6 100644 --- a/configs/experiment/train/images/pgp/base.yaml +++ b/configs/experiment/train/images/pgp/base.yaml @@ -8,3 +8,11 @@ defaults: # ImageNet stats mean: [123.675, 116.280, 103.530] std: [58.395, 57.120, 57.375] + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/resnet50/base.yaml b/configs/experiment/train/images/resnet50/base.yaml index 02412a1e..bfa6d390 100644 --- a/configs/experiment/train/images/resnet50/base.yaml +++ b/configs/experiment/train/images/resnet50/base.yaml @@ -7,3 +7,11 @@ defaults: - _self_ model_name: "ResNet50" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_512} + val: + uris: + - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/resnet50/base_stain.yaml b/configs/experiment/train/images/resnet50/base_stain.yaml index 5f724341..34855979 100644 --- a/configs/experiment/train/images/resnet50/base_stain.yaml +++ b/configs/experiment/train/images/resnet50/base_stain.yaml @@ -7,3 +7,11 @@ defaults: - _self_ model_name: "ResNet50 Stain" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_512} + val: + uris: + - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/vgg16/base.yaml b/configs/experiment/train/images/vgg16/base.yaml index fb7ce56c..38ab882b 100644 --- a/configs/experiment/train/images/vgg16/base.yaml +++ b/configs/experiment/train/images/vgg16/base.yaml @@ -7,3 +7,11 @@ defaults: - _self_ model_name: "VGG-16" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_512} + val: + uris: + - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/vgg16/base_stain.yaml b/configs/experiment/train/images/vgg16/base_stain.yaml index a46e8f4d..cfea58c9 100644 --- a/configs/experiment/train/images/vgg16/base_stain.yaml +++ b/configs/experiment/train/images/vgg16/base_stain.yaml @@ -7,3 +7,11 @@ defaults: - _self_ model_name: "VGG-16 Stain" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_512} + val: + uris: + - ${val_data.tiles_filtered_uri_512} diff --git a/configs/experiment/train/images/virchow2/base.yaml b/configs/experiment/train/images/virchow2/base.yaml index 65516ebb..c37e4f2b 100644 --- a/configs/experiment/train/images/virchow2/base.yaml +++ b/configs/experiment/train/images/virchow2/base.yaml @@ -8,3 +8,11 @@ defaults: # ImageNet stats mean: [123.675, 116.280, 103.530] std: [58.395, 57.120, 57.375] + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} From 13e6cf367bdbf09d77f5d750c758cf8df08c6f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 09:32:03 +0000 Subject: [PATCH 225/284] feat: 224 traning configs --- .../experiment/train/images/vgg16/base_224.yaml | 17 +++++++++++++++++ .../train/images/vgg16/base_stain_224.yaml | 17 +++++++++++++++++ .../experiment/train/images/vgg16/mmci_224.yaml | 7 +++++++ .../train/images/vgg16/mmci_stain_224.yaml | 7 +++++++ 4 files changed, 48 insertions(+) create mode 100644 configs/experiment/train/images/vgg16/base_224.yaml create mode 100644 configs/experiment/train/images/vgg16/base_stain_224.yaml create mode 100644 configs/experiment/train/images/vgg16/mmci_224.yaml create mode 100644 configs/experiment/train/images/vgg16/mmci_stain_224.yaml diff --git a/configs/experiment/train/images/vgg16/base_224.yaml b/configs/experiment/train/images/vgg16/base_224.yaml new file mode 100644 index 00000000..28986510 --- /dev/null +++ b/configs/experiment/train/images/vgg16/base_224.yaml @@ -0,0 +1,17 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /datamodule/transforms/normalize_w_augments@transforms + - /model/vgg16_model@model + - _self_ + +model_name: "VGG-16 (224x224)" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/vgg16/base_stain_224.yaml b/configs/experiment/train/images/vgg16/base_stain_224.yaml new file mode 100644 index 00000000..994d5e18 --- /dev/null +++ b/configs/experiment/train/images/vgg16/base_stain_224.yaml @@ -0,0 +1,17 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /datamodule/transforms/normalize_w_stain_augments@transforms + - /model/vgg16_model@model + - _self_ + +model_name: "VGG-16 Stain (224x224)" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/vgg16/mmci_224.yaml b/configs/experiment/train/images/vgg16/mmci_224.yaml new file mode 100644 index 00000000..95128f7b --- /dev/null +++ b/configs/experiment/train/images/vgg16/mmci_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/vgg16/base_224 + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ diff --git a/configs/experiment/train/images/vgg16/mmci_stain_224.yaml b/configs/experiment/train/images/vgg16/mmci_stain_224.yaml new file mode 100644 index 00000000..86c9ad80 --- /dev/null +++ b/configs/experiment/train/images/vgg16/mmci_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/vgg16/base_stain_224 + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ From a6e1e002995ec6549f663b566003357604441942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 09:34:55 +0000 Subject: [PATCH 226/284] feat: resnet configs --- .../train/images/resnet50/base_224.yaml | 17 +++++++++++++++++ .../train/images/resnet50/base_stain_224.yaml | 17 +++++++++++++++++ .../train/images/resnet50/mmci_224.yaml | 7 +++++++ .../train/images/resnet50/mmci_stain_224.yaml | 7 +++++++ 4 files changed, 48 insertions(+) create mode 100644 configs/experiment/train/images/resnet50/base_224.yaml create mode 100644 configs/experiment/train/images/resnet50/base_stain_224.yaml create mode 100644 configs/experiment/train/images/resnet50/mmci_224.yaml create mode 100644 configs/experiment/train/images/resnet50/mmci_stain_224.yaml diff --git a/configs/experiment/train/images/resnet50/base_224.yaml b/configs/experiment/train/images/resnet50/base_224.yaml new file mode 100644 index 00000000..f2eee477 --- /dev/null +++ b/configs/experiment/train/images/resnet50/base_224.yaml @@ -0,0 +1,17 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /datamodule/transforms/normalize_w_augments@transforms + - /model/resnet50_model@model + - _self_ + +model_name: "ResNet50 (224x224)" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/resnet50/base_stain_224.yaml b/configs/experiment/train/images/resnet50/base_stain_224.yaml new file mode 100644 index 00000000..d0a9ab98 --- /dev/null +++ b/configs/experiment/train/images/resnet50/base_stain_224.yaml @@ -0,0 +1,17 @@ +# @package _global_ + +defaults: + - /experiment/train/images/base + - /datamodule/transforms/normalize_w_stain_augments@transforms + - /model/resnet50_model@model + - _self_ + +model_name: "ResNet50 Stain (224x224)" + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_uri_224} + val: + uris: + - ${val_data.tiles_filtered_uri_224} diff --git a/configs/experiment/train/images/resnet50/mmci_224.yaml b/configs/experiment/train/images/resnet50/mmci_224.yaml new file mode 100644 index 00000000..f5c06567 --- /dev/null +++ b/configs/experiment/train/images/resnet50/mmci_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/resnet50/base_224 + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ diff --git a/configs/experiment/train/images/resnet50/mmci_stain_224.yaml b/configs/experiment/train/images/resnet50/mmci_stain_224.yaml new file mode 100644 index 00000000..14a400c0 --- /dev/null +++ b/configs/experiment/train/images/resnet50/mmci_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/train/images/resnet50/base_stain_224 + - /data/mmci/tl_train@train_data + - /data/mmci/tl_val@val_data + - _self_ From 23dff275c9bd45c43b99fc0a283a8f5982a84222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 09:37:47 +0000 Subject: [PATCH 227/284] feat: bring back 512 models --- .../model/checkpoints/mmci_tl/resnet50.yaml | 23 ++++++++++++++++++ .../checkpoints/mmci_tl/resnet50_stain.yaml | 23 ++++++++++++++++++ configs/model/checkpoints/mmci_tl/vgg16.yaml | 23 ++++++++++++++++++ .../checkpoints/mmci_tl/vgg16_stain.yaml | 24 +++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 configs/model/checkpoints/mmci_tl/resnet50.yaml create mode 100644 configs/model/checkpoints/mmci_tl/resnet50_stain.yaml create mode 100644 configs/model/checkpoints/mmci_tl/vgg16.yaml create mode 100644 configs/model/checkpoints/mmci_tl/vgg16_stain.yaml diff --git a/configs/model/checkpoints/mmci_tl/resnet50.yaml b/configs/model/checkpoints/mmci_tl/resnet50.yaml new file mode 100644 index 00000000..108cd36a --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/resnet50.yaml @@ -0,0 +1,23 @@ +defaults: + - base + - _self_ + +tile_threshold: 0.44 +slide_max_threshold: 0.9540096521 +slide_mpm_threshold: 0.324303627 +slide_top_k_threshold: 0.4309533238 +slide_span_threshold: 0.5 # arbitrary - span predictions are binary + +checkpoint: "mlflow-artifacts:/65/aebc892f526047249b972f200bef4381/artifacts/checkpoints/epoch=0-step=4648/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_checkpoint.ckpt" + +model_name: "ResNet50" +slide_level_prediction_uris: + mean_pool_max: + mmci_sl_group_1: "mlflow-artifacts:/65/46a594a334ef4bbe9fdc7ba73250b12a/artifacts/tables/aggregated_predictions_corrected.json" + max: + mmci_sl_group_1: "mlflow-artifacts:/65/94561b6417ea4f888495fdcb799555e2/artifacts/tables/aggregated_predictions_corrected.json" + top_k: + mmci_sl_group_1: "mlflow-artifacts:/65/bcf0312c738c460381bde996e5cb93d8/artifacts/tables/aggregated_predictions_corrected.json" + span: + mmci_sl_group_1: "mlflow-artifacts:/65/ccd4a8001874439f8c860eefa5d41fbe/artifacts/tables/aggregated_predictions_corrected.json" diff --git a/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml new file mode 100644 index 00000000..6e894773 --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/resnet50_stain.yaml @@ -0,0 +1,23 @@ +defaults: + - base + - _self_ + +tile_threshold: 0.76 +slide_max_threshold: 0.9963486195 +slide_mpm_threshold: 0.7526295781 +slide_top_k_threshold: 0.9902788997 +slide_span_threshold: 0.5 # arbitrary - span predictions are binary + +checkpoint: "mlflow-artifacts:/65/0d6af27cf5d848279c891eb3039b11c8/artifacts/checkpoints/epoch=0-step=6972/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_stain_checkpoint.ckpt" + +model_name: "ResNet50 Stain" +slide_level_prediction_uris: + mean_pool_max: + mmci_sl_group_1: "mlflow-artifacts:/65/bdbd2d8aa5fa44a581281f2396afa4a7/artifacts/tables/aggregated_predictions_corrected.json" + max: + mmci_sl_group_1: "mlflow-artifacts:/65/b982f0b741b442a5b7c7f16cae8fb155/artifacts/tables/aggregated_predictions_corrected.json" + top_k: + mmci_sl_group_1: "mlflow-artifacts:/65/51be8706f3fb401ab455ecf2c7b64b93/artifacts/tables/aggregated_predictions_corrected.json" + span: + mmci_sl_group_1: "mlflow-artifacts:/65/647659f90b8144989062a73882e50d1f/artifacts/tables/aggregated_predictions_corrected.json" diff --git a/configs/model/checkpoints/mmci_tl/vgg16.yaml b/configs/model/checkpoints/mmci_tl/vgg16.yaml new file mode 100644 index 00000000..78dbca99 --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/vgg16.yaml @@ -0,0 +1,23 @@ +defaults: + - base + - _self_ + +tile_threshold: 0.94 +slide_max_threshold: 0.9917962551 +slide_mpm_threshold: 0.9917962551 +slide_top_k_threshold: 0.9871470928 +slide_span_threshold: 0.5 # arbitrary - span predictions are binary + +checkpoint: "mlflow-artifacts:/65/7b52930515c14710855962f8882fb4d3/artifacts/checkpoints/epoch=0-step=9296/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_checkpoint.ckpt" + +model_name: "VGG-16" +slide_level_prediction_uris: + mean_pool_max: + mmci_sl_group_1: "mlflow-artifacts:/65/1a1eeddaf5864f6aa8b0d9e8a7f887ec/artifacts/tables/aggregated_predictions_corrected.json" + max: + mmci_sl_group_1: "mlflow-artifacts:/65/4c57d5df00fb4b26baa7ae761f4824c8/artifacts/tables/aggregated_predictions_corrected.json" + top_k: + mmci_sl_group_1: "mlflow-artifacts:/65/3e2d76776e2e4ce5ac25041d7b97fd7d/artifacts/tables/aggregated_predictions_corrected.json" + span: + mmci_sl_group_1: "mlflow-artifacts:/65/1981e514a7be46d7a1188bd93c34eefe/artifacts/tables/aggregated_predictions_corrected.json" diff --git a/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml new file mode 100644 index 00000000..4ad8eb63 --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/vgg16_stain.yaml @@ -0,0 +1,24 @@ +defaults: + - base + - _self_ + +tile_threshold: 0.73 +slide_max_threshold: 0.9859181643 +slide_mpm_threshold: 0.9827958941 +slide_top_k_threshold: 0.9819370508 +slide_span_threshold: 0.5 # arbitrary - span predictions are binary + +checkpoint: "mlflow-artifacts:/65/3d053bb91d4841e28afe2c93dfe0b273/artifacts/checkpoints/epoch=2-step=25564/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_stain_checkpoint.ckpt" + + +model_name: "VGG-16 Stain" +slide_level_prediction_uris: + mean_pool_max: + mmci_sl_group_1: "mlflow-artifacts:/65/de09807020fa4d4ca15b840692fedf82/artifacts/tables/aggregated_predictions_corrected.json" + max: + mmci_sl_group_1: "mlflow-artifacts:/65/1c08494a5de648d4b7f025aa89ac1be9/artifacts/tables/aggregated_predictions_corrected.json" + top_k: + mmci_sl_group_1: "mlflow-artifacts:/65/d63f6775ddf44a4c93a934298fe10d22/artifacts/tables/aggregated_predictions_corrected.json" + span: + mmci_sl_group_1: "mlflow-artifacts:/65/8d45a014dece415bababfabda2981a3f/artifacts/tables/aggregated_predictions_corrected.json" From 1d45334279e54dc7222d571affd948c00670d297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 09:55:15 +0000 Subject: [PATCH 228/284] feat: test add 224 --- configs/experiment/test/images/base.yaml | 4 ---- configs/experiment/test/images/resnet50/base.yaml | 5 +++++ .../experiment/test/images/resnet50/base_224.yaml | 12 ++++++++++++ .../test/images/resnet50/stain_base.yaml | 5 +++++ .../test/images/resnet50/stain_base_224.yaml | 12 ++++++++++++ .../images/resnet50/stain_tile_threshold_224.yaml | 14 ++++++++++++++ .../test/images/resnet50/tile_threshold_224.yaml | 14 ++++++++++++++ configs/experiment/test/images/vgg16/base.yaml | 4 ++++ configs/experiment/test/images/vgg16/base_224.yaml | 12 ++++++++++++ .../experiment/test/images/vgg16/stain_base.yaml | 5 +++++ .../test/images/vgg16/stain_base_224.yaml | 12 ++++++++++++ .../images/vgg16/stain_tile_threshold_224.yaml | 14 ++++++++++++++ .../test/images/vgg16/tile_threshold_224.yaml | 14 ++++++++++++++ 13 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 configs/experiment/test/images/resnet50/base_224.yaml create mode 100644 configs/experiment/test/images/resnet50/stain_base_224.yaml create mode 100644 configs/experiment/test/images/resnet50/stain_tile_threshold_224.yaml create mode 100644 configs/experiment/test/images/resnet50/tile_threshold_224.yaml create mode 100644 configs/experiment/test/images/vgg16/base_224.yaml create mode 100644 configs/experiment/test/images/vgg16/stain_base_224.yaml create mode 100644 configs/experiment/test/images/vgg16/stain_tile_threshold_224.yaml create mode 100644 configs/experiment/test/images/vgg16/tile_threshold_224.yaml diff --git a/configs/experiment/test/images/base.yaml b/configs/experiment/test/images/base.yaml index 711fbc27..7f93f311 100644 --- a/configs/experiment/test/images/base.yaml +++ b/configs/experiment/test/images/base.yaml @@ -8,7 +8,3 @@ defaults: datamodule: batch_size: 64 num_workers: 8 - - test: - uris: - - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/resnet50/base.yaml b/configs/experiment/test/images/resnet50/base.yaml index 070ed49f..76e4816d 100644 --- a/configs/experiment/test/images/resnet50/base.yaml +++ b/configs/experiment/test/images/resnet50/base.yaml @@ -5,3 +5,8 @@ defaults: - /model/resnet50_model@model - /model/checkpoints/mmci_tl/resnet50@model_data - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_512} diff --git a/configs/experiment/test/images/resnet50/base_224.yaml b/configs/experiment/test/images/resnet50/base_224.yaml new file mode 100644 index 00000000..f69ae438 --- /dev/null +++ b/configs/experiment/test/images/resnet50/base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/images/base + - /model/resnet50_model@model + - /model/checkpoints/mmci_tl/resnet50_224@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/resnet50/stain_base.yaml b/configs/experiment/test/images/resnet50/stain_base.yaml index 4e0be257..d8a52f6d 100644 --- a/configs/experiment/test/images/resnet50/stain_base.yaml +++ b/configs/experiment/test/images/resnet50/stain_base.yaml @@ -5,3 +5,8 @@ defaults: - /model/resnet50_model@model - /model/checkpoints/mmci_tl/resnet50_stain@model_data - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_512} diff --git a/configs/experiment/test/images/resnet50/stain_base_224.yaml b/configs/experiment/test/images/resnet50/stain_base_224.yaml new file mode 100644 index 00000000..04d57f7c --- /dev/null +++ b/configs/experiment/test/images/resnet50/stain_base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/images/base + - /model/resnet50_model@model + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/resnet50/stain_tile_threshold_224.yaml b/configs/experiment/test/images/resnet50/stain_tile_threshold_224.yaml new file mode 100644 index 00000000..5e8a0883 --- /dev/null +++ b/configs/experiment/test/images/resnet50/stain_tile_threshold_224.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/images/resnet50/stain_base_224 + - /data/mmci/tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.8 # Pathologist's estimate +use_case: "Tile Threshold Estimation" diff --git a/configs/experiment/test/images/resnet50/tile_threshold_224.yaml b/configs/experiment/test/images/resnet50/tile_threshold_224.yaml new file mode 100644 index 00000000..65a1482e --- /dev/null +++ b/configs/experiment/test/images/resnet50/tile_threshold_224.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/images/resnet50/base_224 + - /data/mmci/tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.9 # Pathologist's estimate +use_case: "Tile Threshold Estimation" diff --git a/configs/experiment/test/images/vgg16/base.yaml b/configs/experiment/test/images/vgg16/base.yaml index 4f8d4beb..589951af 100644 --- a/configs/experiment/test/images/vgg16/base.yaml +++ b/configs/experiment/test/images/vgg16/base.yaml @@ -6,3 +6,7 @@ defaults: - /model/checkpoints/mmci_tl/vgg16@model_data - _self_ +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_512} diff --git a/configs/experiment/test/images/vgg16/base_224.yaml b/configs/experiment/test/images/vgg16/base_224.yaml new file mode 100644 index 00000000..0c243799 --- /dev/null +++ b/configs/experiment/test/images/vgg16/base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/images/base + - /model/vgg16_model@model + - /model/checkpoints/mmci_tl/vgg16_224@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/vgg16/stain_base.yaml b/configs/experiment/test/images/vgg16/stain_base.yaml index 6ab40e7e..5fa31c6b 100644 --- a/configs/experiment/test/images/vgg16/stain_base.yaml +++ b/configs/experiment/test/images/vgg16/stain_base.yaml @@ -5,3 +5,8 @@ defaults: - /model/vgg16_model@model - /model/checkpoints/mmci_tl/vgg16_stain@model_data - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_512} diff --git a/configs/experiment/test/images/vgg16/stain_base_224.yaml b/configs/experiment/test/images/vgg16/stain_base_224.yaml new file mode 100644 index 00000000..b0ccf0f1 --- /dev/null +++ b/configs/experiment/test/images/vgg16/stain_base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/images/base + - /model/vgg16_model@model + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_uri_224} diff --git a/configs/experiment/test/images/vgg16/stain_tile_threshold_224.yaml b/configs/experiment/test/images/vgg16/stain_tile_threshold_224.yaml new file mode 100644 index 00000000..60a3fba5 --- /dev/null +++ b/configs/experiment/test/images/vgg16/stain_tile_threshold_224.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/images/vgg16/stain_base_224 + - /data/mmci/tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.8 # Pathologist's estimate +use_case: "Tile Threshold Estimation" diff --git a/configs/experiment/test/images/vgg16/tile_threshold_224.yaml b/configs/experiment/test/images/vgg16/tile_threshold_224.yaml new file mode 100644 index 00000000..45f714a9 --- /dev/null +++ b/configs/experiment/test/images/vgg16/tile_threshold_224.yaml @@ -0,0 +1,14 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold@callbacks + - /experiment/test/images/vgg16/base_224 + - /data/mmci/tl_val@test_data + - _self_ + +datamodule: + test: + stratified_filter: true # tile threshold not estimated from negative tiles in positive slides + +tile_threshold: 0.9 # Pathologist's estimate +use_case: "Tile Threshold Estimation" From 8cd6fb2a7a5b3293d2cb7a85672bd6fd918bfd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 10:04:40 +0000 Subject: [PATCH 229/284] feat: estimations nest --- configs/experiment/predict/images/base.yaml | 4 ---- .../predict/images/resnet50/base.yaml | 5 +++++ .../predict/images/resnet50/base_224.yaml | 12 +++++++++++ .../resnet50/estimations/mpm_estimation.yaml | 20 +++++++++++++++++++ .../estimations/mpm_estimation_224.yaml | 20 +++++++++++++++++++ .../{ => estimations}/span_estimation.yaml | 4 ++-- .../estimations/span_estimation_224.yaml | 19 ++++++++++++++++++ .../stain_mpm_estimation.yaml | 4 ++-- .../estimations/stain_mpm_estimation_224.yaml | 20 +++++++++++++++++++ .../stain_span_estimation.yaml | 4 ++-- .../stain_span_estimation_224.yaml | 19 ++++++++++++++++++ .../stain_top_k_estimation.yaml | 4 ++-- .../stain_top_k_estimation_224.yaml | 19 ++++++++++++++++++ .../estimations/top_k_estimation.yaml | 19 ++++++++++++++++++ .../estimations/top_k_estimation_224.yaml | 19 ++++++++++++++++++ .../predict/images/resnet50/stain_base.yaml | 5 +++++ .../images/resnet50/stain_base_224.yaml | 12 +++++++++++ .../experiment/predict/images/vgg16/base.yaml | 5 +++++ .../predict/images/vgg16/base_224.yaml | 12 +++++++++++ .../{ => estimations}/mpm_estimation.yaml | 4 ++-- .../estimations/mpm_estimation_224.yaml} | 2 +- .../vgg16/estimations/span_estimation.yaml | 19 ++++++++++++++++++ .../span_estimation_224.yaml} | 2 +- .../stain_mpm_estimation.yaml | 4 ++-- .../estimations/stain_mpm_estimation_224.yaml | 20 +++++++++++++++++++ .../stain_span_estimation.yaml | 4 ++-- .../stain_span_estimation_224.yaml | 19 ++++++++++++++++++ .../stain_top_k_estimation.yaml | 4 ++-- .../stain_top_k_estimation_224.yaml | 19 ++++++++++++++++++ .../{ => estimations}/top_k_estimation.yaml | 4 ++-- .../estimations/top_k_estimation_224.yaml} | 2 +- .../predict/images/vgg16/stain_base.yaml | 5 +++++ .../predict/images/vgg16/stain_base_224.yaml | 12 +++++++++++ 33 files changed, 321 insertions(+), 25 deletions(-) create mode 100644 configs/experiment/predict/images/resnet50/base_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/estimations/mpm_estimation.yaml create mode 100644 configs/experiment/predict/images/resnet50/estimations/mpm_estimation_224.yaml rename configs/experiment/predict/images/resnet50/{ => estimations}/span_estimation.yaml (90%) create mode 100644 configs/experiment/predict/images/resnet50/estimations/span_estimation_224.yaml rename configs/experiment/predict/images/resnet50/{ => estimations}/stain_mpm_estimation.yaml (89%) create mode 100644 configs/experiment/predict/images/resnet50/estimations/stain_mpm_estimation_224.yaml rename configs/experiment/predict/images/resnet50/{ => estimations}/stain_span_estimation.yaml (90%) create mode 100644 configs/experiment/predict/images/resnet50/estimations/stain_span_estimation_224.yaml rename configs/experiment/predict/images/resnet50/{ => estimations}/stain_top_k_estimation.yaml (90%) create mode 100644 configs/experiment/predict/images/resnet50/estimations/stain_top_k_estimation_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/estimations/top_k_estimation.yaml create mode 100644 configs/experiment/predict/images/resnet50/estimations/top_k_estimation_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/stain_base_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/base_224.yaml rename configs/experiment/predict/images/vgg16/{ => estimations}/mpm_estimation.yaml (89%) rename configs/experiment/predict/images/{resnet50/mpm_estimation.yaml => vgg16/estimations/mpm_estimation_224.yaml} (89%) create mode 100644 configs/experiment/predict/images/vgg16/estimations/span_estimation.yaml rename configs/experiment/predict/images/vgg16/{span_estimation.yaml => estimations/span_estimation_224.yaml} (90%) rename configs/experiment/predict/images/vgg16/{ => estimations}/stain_mpm_estimation.yaml (89%) create mode 100644 configs/experiment/predict/images/vgg16/estimations/stain_mpm_estimation_224.yaml rename configs/experiment/predict/images/vgg16/{ => estimations}/stain_span_estimation.yaml (90%) create mode 100644 configs/experiment/predict/images/vgg16/estimations/stain_span_estimation_224.yaml rename configs/experiment/predict/images/vgg16/{ => estimations}/stain_top_k_estimation.yaml (90%) create mode 100644 configs/experiment/predict/images/vgg16/estimations/stain_top_k_estimation_224.yaml rename configs/experiment/predict/images/vgg16/{ => estimations}/top_k_estimation.yaml (89%) rename configs/experiment/predict/images/{resnet50/top_k_estimation.yaml => vgg16/estimations/top_k_estimation_224.yaml} (90%) create mode 100644 configs/experiment/predict/images/vgg16/stain_base_224.yaml diff --git a/configs/experiment/predict/images/base.yaml b/configs/experiment/predict/images/base.yaml index 6fe6306d..6a355729 100644 --- a/configs/experiment/predict/images/base.yaml +++ b/configs/experiment/predict/images/base.yaml @@ -8,7 +8,3 @@ defaults: datamodule: batch_size: 64 num_workers: 8 - - predict: - uris: - - ${predict_data.tiles_filtered_uri_224} diff --git a/configs/experiment/predict/images/resnet50/base.yaml b/configs/experiment/predict/images/resnet50/base.yaml index 72164123..e61a8d50 100644 --- a/configs/experiment/predict/images/resnet50/base.yaml +++ b/configs/experiment/predict/images/resnet50/base.yaml @@ -5,3 +5,8 @@ defaults: - /model/resnet50_model@model - /model/checkpoints/mmci_tl/resnet50@model_data - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_512} diff --git a/configs/experiment/predict/images/resnet50/base_224.yaml b/configs/experiment/predict/images/resnet50/base_224.yaml new file mode 100644 index 00000000..57d18cf7 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/base + - /model/resnet50_model@model + - /model/checkpoints/mmci_tl/resnet50_224@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_224} diff --git a/configs/experiment/predict/images/resnet50/estimations/mpm_estimation.yaml b/configs/experiment/predict/images/resnet50/estimations/mpm_estimation.yaml new file mode 100644 index 00000000..edc6fce5 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/mpm_estimation.yaml @@ -0,0 +1,20 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator + + static: + extent_tile: 512 + stride_tile: 256 + to_estimate: + kernel_size: [1, 2, 3, 4, 5, 6, 7] + +use_case: "Kernel Size Estimation (MPM Aggregator)" + diff --git a/configs/experiment/predict/images/resnet50/estimations/mpm_estimation_224.yaml b/configs/experiment/predict/images/resnet50/estimations/mpm_estimation_224.yaml new file mode 100644 index 00000000..7becb63b --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/mpm_estimation_224.yaml @@ -0,0 +1,20 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator + + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5, 6, 7] + +use_case: "Kernel Size Estimation (MPM Aggregator)" + diff --git a/configs/experiment/predict/images/resnet50/span_estimation.yaml b/configs/experiment/predict/images/resnet50/estimations/span_estimation.yaml similarity index 90% rename from configs/experiment/predict/images/resnet50/span_estimation.yaml rename to configs/experiment/predict/images/resnet50/estimations/span_estimation.yaml index b7e12a39..769f8347 100644 --- a/configs/experiment/predict/images/resnet50/span_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/estimations/span_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 cell_threshold: ${model_data.tile_threshold} to_estimate: k: [2, 3, 4, 5] diff --git a/configs/experiment/predict/images/resnet50/estimations/span_estimation_224.yaml b/configs/experiment/predict/images/resnet50/estimations/span_estimation_224.yaml new file mode 100644 index 00000000..8aa88a5e --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/span_estimation_224.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator + static: + extent_tile: 224 + stride_tile: 112 + cell_threshold: ${model_data.tile_threshold} + to_estimate: + k: [2, 3, 4, 5] + +use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/images/resnet50/stain_mpm_estimation.yaml b/configs/experiment/predict/images/resnet50/estimations/stain_mpm_estimation.yaml similarity index 89% rename from configs/experiment/predict/images/resnet50/stain_mpm_estimation.yaml rename to configs/experiment/predict/images/resnet50/estimations/stain_mpm_estimation.yaml index 255ce978..4b2500f0 100644 --- a/configs/experiment/predict/images/resnet50/stain_mpm_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/estimations/stain_mpm_estimation.yaml @@ -11,8 +11,8 @@ callbacks: aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 to_estimate: kernel_size: [1, 2, 3, 4, 5, 6, 7] diff --git a/configs/experiment/predict/images/resnet50/estimations/stain_mpm_estimation_224.yaml b/configs/experiment/predict/images/resnet50/estimations/stain_mpm_estimation_224.yaml new file mode 100644 index 00000000..c7a53eb4 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/stain_mpm_estimation_224.yaml @@ -0,0 +1,20 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/stain_base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator + + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5, 6, 7] + +use_case: "Kernel Size Estimation (MPM Aggregator)" + diff --git a/configs/experiment/predict/images/resnet50/stain_span_estimation.yaml b/configs/experiment/predict/images/resnet50/estimations/stain_span_estimation.yaml similarity index 90% rename from configs/experiment/predict/images/resnet50/stain_span_estimation.yaml rename to configs/experiment/predict/images/resnet50/estimations/stain_span_estimation.yaml index 4c8237c0..e492e813 100644 --- a/configs/experiment/predict/images/resnet50/stain_span_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/estimations/stain_span_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 cell_threshold: ${model_data.tile_threshold} to_estimate: k: [2, 3, 4, 5] diff --git a/configs/experiment/predict/images/resnet50/estimations/stain_span_estimation_224.yaml b/configs/experiment/predict/images/resnet50/estimations/stain_span_estimation_224.yaml new file mode 100644 index 00000000..e532684a --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/stain_span_estimation_224.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/stain_base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator + static: + extent_tile: 224 + stride_tile: 112 + cell_threshold: ${model_data.tile_threshold} + to_estimate: + k: [2, 3, 4, 5] + +use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/images/resnet50/stain_top_k_estimation.yaml b/configs/experiment/predict/images/resnet50/estimations/stain_top_k_estimation.yaml similarity index 90% rename from configs/experiment/predict/images/resnet50/stain_top_k_estimation.yaml rename to configs/experiment/predict/images/resnet50/estimations/stain_top_k_estimation.yaml index f1d0beee..c4c54b97 100644 --- a/configs/experiment/predict/images/resnet50/stain_top_k_estimation.yaml +++ b/configs/experiment/predict/images/resnet50/estimations/stain_top_k_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 to_estimate: kernel_size: [1, 2, 3, 4, 5] k: [2, 3, 4] diff --git a/configs/experiment/predict/images/resnet50/estimations/stain_top_k_estimation_224.yaml b/configs/experiment/predict/images/resnet50/estimations/stain_top_k_estimation_224.yaml new file mode 100644 index 00000000..f057b7f1 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/stain_top_k_estimation_224.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/stain_base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5] + k: [2, 3, 4] + +use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/images/resnet50/estimations/top_k_estimation.yaml b/configs/experiment/predict/images/resnet50/estimations/top_k_estimation.yaml new file mode 100644 index 00000000..71dedc59 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/top_k_estimation.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator + static: + extent_tile: 512 + stride_tile: 256 + to_estimate: + kernel_size: [1, 2, 3, 4, 5] + k: [2, 3, 4] + +use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/images/resnet50/estimations/top_k_estimation_224.yaml b/configs/experiment/predict/images/resnet50/estimations/top_k_estimation_224.yaml new file mode 100644 index 00000000..4c406a33 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/estimations/top_k_estimation_224.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/resnet50/base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5] + k: [2, 3, 4] + +use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/images/resnet50/stain_base.yaml b/configs/experiment/predict/images/resnet50/stain_base.yaml index ffbde6eb..c700662b 100644 --- a/configs/experiment/predict/images/resnet50/stain_base.yaml +++ b/configs/experiment/predict/images/resnet50/stain_base.yaml @@ -5,3 +5,8 @@ defaults: - /model/resnet50_model@model - /model/checkpoints/mmci_tl/resnet50_stain@model_data - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_512} diff --git a/configs/experiment/predict/images/resnet50/stain_base_224.yaml b/configs/experiment/predict/images/resnet50/stain_base_224.yaml new file mode 100644 index 00000000..214cb1b3 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/stain_base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/base + - /model/resnet50_model@model + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_224} diff --git a/configs/experiment/predict/images/vgg16/base.yaml b/configs/experiment/predict/images/vgg16/base.yaml index 4165e069..7e2c7086 100644 --- a/configs/experiment/predict/images/vgg16/base.yaml +++ b/configs/experiment/predict/images/vgg16/base.yaml @@ -5,3 +5,8 @@ defaults: - /model/vgg16_model@model - /model/checkpoints/mmci_tl/vgg16@model_data - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_512} diff --git a/configs/experiment/predict/images/vgg16/base_224.yaml b/configs/experiment/predict/images/vgg16/base_224.yaml new file mode 100644 index 00000000..2262e324 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/base + - /model/vgg16_model@model + - /model/checkpoints/mmci_tl/vgg16_224@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_224} diff --git a/configs/experiment/predict/images/vgg16/mpm_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/mpm_estimation.yaml similarity index 89% rename from configs/experiment/predict/images/vgg16/mpm_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/mpm_estimation.yaml index d73ef67d..bce6e294 100644 --- a/configs/experiment/predict/images/vgg16/mpm_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/mpm_estimation.yaml @@ -11,8 +11,8 @@ callbacks: aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 to_estimate: kernel_size: [1, 2, 3, 4, 5, 6, 7] diff --git a/configs/experiment/predict/images/resnet50/mpm_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/mpm_estimation_224.yaml similarity index 89% rename from configs/experiment/predict/images/resnet50/mpm_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/mpm_estimation_224.yaml index 80b81078..3ec1b3e4 100644 --- a/configs/experiment/predict/images/resnet50/mpm_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/mpm_estimation_224.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/images/resnet50/base + - /experiment/predict/images/vgg16/base_224 - /data/mmci/sl_group_1@predict_data - _self_ diff --git a/configs/experiment/predict/images/vgg16/estimations/span_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/span_estimation.yaml new file mode 100644 index 00000000..d24a26d8 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/estimations/span_estimation.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/vgg16/base + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator + static: + extent_tile: 512 + stride_tile: 256 + cell_threshold: ${model_data.tile_threshold} + to_estimate: + k: [2, 3, 4, 5] + +use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/images/vgg16/span_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/span_estimation_224.yaml similarity index 90% rename from configs/experiment/predict/images/vgg16/span_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/span_estimation_224.yaml index 185906a0..99a8ddf8 100644 --- a/configs/experiment/predict/images/vgg16/span_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/span_estimation_224.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/images/vgg16/base + - /experiment/predict/images/vgg16/base_224 - /data/mmci/sl_group_1@predict_data - _self_ diff --git a/configs/experiment/predict/images/vgg16/stain_mpm_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/stain_mpm_estimation.yaml similarity index 89% rename from configs/experiment/predict/images/vgg16/stain_mpm_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/stain_mpm_estimation.yaml index b9fb9248..aaea610d 100644 --- a/configs/experiment/predict/images/vgg16/stain_mpm_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/stain_mpm_estimation.yaml @@ -11,8 +11,8 @@ callbacks: aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 to_estimate: kernel_size: [1, 2, 3, 4, 5, 6, 7] diff --git a/configs/experiment/predict/images/vgg16/estimations/stain_mpm_estimation_224.yaml b/configs/experiment/predict/images/vgg16/estimations/stain_mpm_estimation_224.yaml new file mode 100644 index 00000000..f3e7d01d --- /dev/null +++ b/configs/experiment/predict/images/vgg16/estimations/stain_mpm_estimation_224.yaml @@ -0,0 +1,20 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/vgg16/stain_base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator + + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5, 6, 7] + +use_case: "Kernel Size Estimation (MPM Aggregator)" + diff --git a/configs/experiment/predict/images/vgg16/stain_span_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/stain_span_estimation.yaml similarity index 90% rename from configs/experiment/predict/images/vgg16/stain_span_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/stain_span_estimation.yaml index c674cd13..69b13b48 100644 --- a/configs/experiment/predict/images/vgg16/stain_span_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/stain_span_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 cell_threshold: ${model_data.tile_threshold} to_estimate: k: [2, 3, 4, 5] diff --git a/configs/experiment/predict/images/vgg16/estimations/stain_span_estimation_224.yaml b/configs/experiment/predict/images/vgg16/estimations/stain_span_estimation_224.yaml new file mode 100644 index 00000000..0c7db32d --- /dev/null +++ b/configs/experiment/predict/images/vgg16/estimations/stain_span_estimation_224.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/vgg16/stain_base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator + static: + extent_tile: 224 + stride_tile: 112 + cell_threshold: ${model_data.tile_threshold} + to_estimate: + k: [2, 3, 4, 5] + +use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/images/vgg16/stain_top_k_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/stain_top_k_estimation.yaml similarity index 90% rename from configs/experiment/predict/images/vgg16/stain_top_k_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/stain_top_k_estimation.yaml index 44bdde00..94e64b97 100644 --- a/configs/experiment/predict/images/vgg16/stain_top_k_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/stain_top_k_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 to_estimate: kernel_size: [1, 2, 3, 4, 5] k: [2, 3, 4] diff --git a/configs/experiment/predict/images/vgg16/estimations/stain_top_k_estimation_224.yaml b/configs/experiment/predict/images/vgg16/estimations/stain_top_k_estimation_224.yaml new file mode 100644 index 00000000..eae21493 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/estimations/stain_top_k_estimation_224.yaml @@ -0,0 +1,19 @@ +# @package _global_ + +defaults: + - /callbacks/aggregator_estimation@callbacks + - /experiment/predict/images/vgg16/stain_base_224 + - /data/mmci/sl_group_1@predict_data + - _self_ + +callbacks: + estimation_callback: + aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator + static: + extent_tile: 224 + stride_tile: 112 + to_estimate: + kernel_size: [1, 2, 3, 4, 5] + k: [2, 3, 4] + +use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/images/vgg16/top_k_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/top_k_estimation.yaml similarity index 89% rename from configs/experiment/predict/images/vgg16/top_k_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/top_k_estimation.yaml index ee387894..ed8d9a40 100644 --- a/configs/experiment/predict/images/vgg16/top_k_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/top_k_estimation.yaml @@ -10,8 +10,8 @@ callbacks: estimation_callback: aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator static: - extent_tile: 224 - stride_tile: 112 + extent_tile: 512 + stride_tile: 256 to_estimate: kernel_size: [1, 2, 3, 4, 5] k: [2, 3, 4] diff --git a/configs/experiment/predict/images/resnet50/top_k_estimation.yaml b/configs/experiment/predict/images/vgg16/estimations/top_k_estimation_224.yaml similarity index 90% rename from configs/experiment/predict/images/resnet50/top_k_estimation.yaml rename to configs/experiment/predict/images/vgg16/estimations/top_k_estimation_224.yaml index ad646c46..97217c4b 100644 --- a/configs/experiment/predict/images/resnet50/top_k_estimation.yaml +++ b/configs/experiment/predict/images/vgg16/estimations/top_k_estimation_224.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/images/resnet50/base + - /experiment/predict/images/vgg16/base_224 - /data/mmci/sl_group_1@predict_data - _self_ diff --git a/configs/experiment/predict/images/vgg16/stain_base.yaml b/configs/experiment/predict/images/vgg16/stain_base.yaml index 308d2f1d..20782b52 100644 --- a/configs/experiment/predict/images/vgg16/stain_base.yaml +++ b/configs/experiment/predict/images/vgg16/stain_base.yaml @@ -5,3 +5,8 @@ defaults: - /model/vgg16_model@model - /model/checkpoints/mmci_tl/vgg16_stain@model_data - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_512} diff --git a/configs/experiment/predict/images/vgg16/stain_base_224.yaml b/configs/experiment/predict/images/vgg16/stain_base_224.yaml new file mode 100644 index 00000000..792aca11 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/stain_base_224.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/base + - /model/vgg16_model@model + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_uri_224} From ced444ffdc5e3f9dacf80e098ec9e08ddd7d87fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 10:09:31 +0000 Subject: [PATCH 230/284] feat: restructure and delete --- .../pgp/{ => estimations}/mpm_estimation.yaml | 0 .../pgp/{ => estimations}/span_estimation.yaml | 0 .../pgp/{ => estimations}/stain_mpm_estimation.yaml | 0 .../pgp/{ => estimations}/stain_span_estimation.yaml | 0 .../{ => estimations}/stain_top_k_estimation.yaml | 0 .../pgp/{ => estimations}/top_k_estimation.yaml | 0 .../virchow2/{ => estimations}/mpm_estimation.yaml | 0 .../virchow2/{ => estimations}/span_estimation.yaml | 0 .../{ => estimations}/stain_mpm_estimation.yaml | 0 .../{ => estimations}/stain_span_estimation.yaml | 0 .../{ => estimations}/stain_top_k_estimation.yaml | 0 .../virchow2/{ => estimations}/top_k_estimation.yaml | 0 .../experiment/test/embeddings/pgp/curves/base.yaml | 8 -------- .../test/embeddings/pgp/nested_metrics/base.yaml | 8 -------- .../test/embeddings/pgp/tile_histograms/base.yaml | 12 ------------ .../test/embeddings/virchow2/curves/base.yaml | 8 -------- .../embeddings/virchow2/nested_metrics/base.yaml | 8 -------- .../embeddings/virchow2/tile_histograms/base.yaml | 12 ------------ .../test/images/google_vit/curves/stain_base.yaml | 8 -------- .../images/google_vit/nested_metrics/stain_base.yaml | 8 -------- .../google_vit/tile_histograms/stain_base.yaml | 12 ------------ .../experiment/test/images/resnet50/curves/base.yaml | 8 -------- .../test/images/resnet50/curves/stain_base.yaml | 8 -------- .../test/images/resnet50/nested_metrics/base.yaml | 8 -------- .../images/resnet50/nested_metrics/stain_base.yaml | 8 -------- .../test/images/resnet50/tile_histograms/base.yaml | 12 ------------ .../images/resnet50/tile_histograms/stain_base.yaml | 12 ------------ .../experiment/test/images/vgg16/curves/base.yaml | 8 -------- .../test/images/vgg16/curves/stain_base.yaml | 8 -------- .../test/images/vgg16/nested_metrics/base.yaml | 8 -------- .../test/images/vgg16/nested_metrics/stain_base.yaml | 8 -------- .../test/images/vgg16/tile_histograms/base.yaml | 12 ------------ .../images/vgg16/tile_histograms/stain_base.yaml | 12 ------------ 33 files changed, 196 deletions(-) rename configs/experiment/predict/embeddings/pgp/{ => estimations}/mpm_estimation.yaml (100%) rename configs/experiment/predict/embeddings/pgp/{ => estimations}/span_estimation.yaml (100%) rename configs/experiment/predict/embeddings/pgp/{ => estimations}/stain_mpm_estimation.yaml (100%) rename configs/experiment/predict/embeddings/pgp/{ => estimations}/stain_span_estimation.yaml (100%) rename configs/experiment/predict/embeddings/pgp/{ => estimations}/stain_top_k_estimation.yaml (100%) rename configs/experiment/predict/embeddings/pgp/{ => estimations}/top_k_estimation.yaml (100%) rename configs/experiment/predict/embeddings/virchow2/{ => estimations}/mpm_estimation.yaml (100%) rename configs/experiment/predict/embeddings/virchow2/{ => estimations}/span_estimation.yaml (100%) rename configs/experiment/predict/embeddings/virchow2/{ => estimations}/stain_mpm_estimation.yaml (100%) rename configs/experiment/predict/embeddings/virchow2/{ => estimations}/stain_span_estimation.yaml (100%) rename configs/experiment/predict/embeddings/virchow2/{ => estimations}/stain_top_k_estimation.yaml (100%) rename configs/experiment/predict/embeddings/virchow2/{ => estimations}/top_k_estimation.yaml (100%) delete mode 100644 configs/experiment/test/embeddings/pgp/curves/base.yaml delete mode 100644 configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml delete mode 100644 configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml delete mode 100644 configs/experiment/test/embeddings/virchow2/curves/base.yaml delete mode 100644 configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml delete mode 100644 configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml delete mode 100644 configs/experiment/test/images/google_vit/curves/stain_base.yaml delete mode 100644 configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml delete mode 100644 configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml delete mode 100644 configs/experiment/test/images/resnet50/curves/base.yaml delete mode 100644 configs/experiment/test/images/resnet50/curves/stain_base.yaml delete mode 100644 configs/experiment/test/images/resnet50/nested_metrics/base.yaml delete mode 100644 configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml delete mode 100644 configs/experiment/test/images/resnet50/tile_histograms/base.yaml delete mode 100644 configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/curves/base.yaml delete mode 100644 configs/experiment/test/images/vgg16/curves/stain_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/nested_metrics/base.yaml delete mode 100644 configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml delete mode 100644 configs/experiment/test/images/vgg16/tile_histograms/base.yaml delete mode 100644 configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml diff --git a/configs/experiment/predict/embeddings/pgp/mpm_estimation.yaml b/configs/experiment/predict/embeddings/pgp/estimations/mpm_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/pgp/mpm_estimation.yaml rename to configs/experiment/predict/embeddings/pgp/estimations/mpm_estimation.yaml diff --git a/configs/experiment/predict/embeddings/pgp/span_estimation.yaml b/configs/experiment/predict/embeddings/pgp/estimations/span_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/pgp/span_estimation.yaml rename to configs/experiment/predict/embeddings/pgp/estimations/span_estimation.yaml diff --git a/configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml b/configs/experiment/predict/embeddings/pgp/estimations/stain_mpm_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml rename to configs/experiment/predict/embeddings/pgp/estimations/stain_mpm_estimation.yaml diff --git a/configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml b/configs/experiment/predict/embeddings/pgp/estimations/stain_span_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml rename to configs/experiment/predict/embeddings/pgp/estimations/stain_span_estimation.yaml diff --git a/configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml b/configs/experiment/predict/embeddings/pgp/estimations/stain_top_k_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml rename to configs/experiment/predict/embeddings/pgp/estimations/stain_top_k_estimation.yaml diff --git a/configs/experiment/predict/embeddings/pgp/top_k_estimation.yaml b/configs/experiment/predict/embeddings/pgp/estimations/top_k_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/pgp/top_k_estimation.yaml rename to configs/experiment/predict/embeddings/pgp/estimations/top_k_estimation.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/mpm_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/estimations/mpm_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/virchow2/mpm_estimation.yaml rename to configs/experiment/predict/embeddings/virchow2/estimations/mpm_estimation.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/span_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/estimations/span_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/virchow2/span_estimation.yaml rename to configs/experiment/predict/embeddings/virchow2/estimations/span_estimation.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/estimations/stain_mpm_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml rename to configs/experiment/predict/embeddings/virchow2/estimations/stain_mpm_estimation.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/estimations/stain_span_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml rename to configs/experiment/predict/embeddings/virchow2/estimations/stain_span_estimation.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/estimations/stain_top_k_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml rename to configs/experiment/predict/embeddings/virchow2/estimations/stain_top_k_estimation.yaml diff --git a/configs/experiment/predict/embeddings/virchow2/top_k_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/estimations/top_k_estimation.yaml similarity index 100% rename from configs/experiment/predict/embeddings/virchow2/top_k_estimation.yaml rename to configs/experiment/predict/embeddings/virchow2/estimations/top_k_estimation.yaml diff --git a/configs/experiment/test/embeddings/pgp/curves/base.yaml b/configs/experiment/test/embeddings/pgp/curves/base.yaml deleted file mode 100644 index 4519f4be..00000000 --- a/configs/experiment/test/embeddings/pgp/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/embeddings/pgp/base - - _self_ - -use_case: "TL ROC & PR Curves" diff --git a/configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml b/configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml deleted file mode 100644 index 42d6cfdf..00000000 --- a/configs/experiment/test/embeddings/pgp/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/embeddings/pgp/base - - _self_ - -use_case: Tile-Level Per-Slide Metrics diff --git a/configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml b/configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml deleted file mode 100644 index 45e65999..00000000 --- a/configs/experiment/test/embeddings/pgp/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/embeddings/pgp/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/embeddings/virchow2/curves/base.yaml b/configs/experiment/test/embeddings/virchow2/curves/base.yaml deleted file mode 100644 index 488fcf2f..00000000 --- a/configs/experiment/test/embeddings/virchow2/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/embeddings/virchow2/base - - _self_ - -use_case: "TL ROC & PR Curves" diff --git a/configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml b/configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml deleted file mode 100644 index 893544a4..00000000 --- a/configs/experiment/test/embeddings/virchow2/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/embeddings/virchow2/base - - _self_ - -use_case: "Per-Slide Metrics" diff --git a/configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml b/configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml deleted file mode 100644 index b8d7bfbf..00000000 --- a/configs/experiment/test/embeddings/virchow2/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/embeddings/virchow2/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/google_vit/curves/stain_base.yaml b/configs/experiment/test/images/google_vit/curves/stain_base.yaml deleted file mode 100644 index 347e5996..00000000 --- a/configs/experiment/test/images/google_vit/curves/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/google_vit/stain_base - - _self_ - -use_case: "TL Curves" \ No newline at end of file diff --git a/configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml b/configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml deleted file mode 100644 index b5f3e066..00000000 --- a/configs/experiment/test/images/google_vit/nested_metrics/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/google_vit/stain_base - - _self_ - -use_case: "TL Per-Slide metrics" \ No newline at end of file diff --git a/configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml b/configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml deleted file mode 100644 index ca5e3cde..00000000 --- a/configs/experiment/test/images/google_vit/tile_histograms/stain_base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/google_vit/stain_base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/resnet50/curves/base.yaml b/configs/experiment/test/images/resnet50/curves/base.yaml deleted file mode 100644 index df1ec0ee..00000000 --- a/configs/experiment/test/images/resnet50/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/resnet50/base - - _self_ - -use_case: "TL Curves" diff --git a/configs/experiment/test/images/resnet50/curves/stain_base.yaml b/configs/experiment/test/images/resnet50/curves/stain_base.yaml deleted file mode 100644 index 3e445080..00000000 --- a/configs/experiment/test/images/resnet50/curves/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/resnet50/stain_base - - _self_ - -use_case: "TL Curves" diff --git a/configs/experiment/test/images/resnet50/nested_metrics/base.yaml b/configs/experiment/test/images/resnet50/nested_metrics/base.yaml deleted file mode 100644 index 4d21ea5c..00000000 --- a/configs/experiment/test/images/resnet50/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/resnet50/base - - _self_ - -use_case: "Per-Slide Metrics" diff --git a/configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml b/configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml deleted file mode 100644 index bda966bc..00000000 --- a/configs/experiment/test/images/resnet50/nested_metrics/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/resnet50/stain_base - - _self_ - -use_case: "Per-Slide Metrics" diff --git a/configs/experiment/test/images/resnet50/tile_histograms/base.yaml b/configs/experiment/test/images/resnet50/tile_histograms/base.yaml deleted file mode 100644 index ea44646d..00000000 --- a/configs/experiment/test/images/resnet50/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/resnet50/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml b/configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml deleted file mode 100644 index 9b113e55..00000000 --- a/configs/experiment/test/images/resnet50/tile_histograms/stain_base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/resnet50/stain_base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/vgg16/curves/base.yaml b/configs/experiment/test/images/vgg16/curves/base.yaml deleted file mode 100644 index fe5fc878..00000000 --- a/configs/experiment/test/images/vgg16/curves/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/vgg16/base - - _self_ - -use_case: "TL Curves" \ No newline at end of file diff --git a/configs/experiment/test/images/vgg16/curves/stain_base.yaml b/configs/experiment/test/images/vgg16/curves/stain_base.yaml deleted file mode 100644 index 5daeb4fb..00000000 --- a/configs/experiment/test/images/vgg16/curves/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/curves@callbacks - - /experiment/test/images/vgg16/stain_base - - _self_ - -use_case: "TL Curves" \ No newline at end of file diff --git a/configs/experiment/test/images/vgg16/nested_metrics/base.yaml b/configs/experiment/test/images/vgg16/nested_metrics/base.yaml deleted file mode 100644 index f38abc29..00000000 --- a/configs/experiment/test/images/vgg16/nested_metrics/base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/vgg16/base - - _self_ - -use_case: "TL Per-Slide metrics" \ No newline at end of file diff --git a/configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml b/configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml deleted file mode 100644 index 81e92e83..00000000 --- a/configs/experiment/test/images/vgg16/nested_metrics/stain_base.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/nested_metrics@callbacks - - /experiment/test/images/vgg16/stain_base - - _self_ - -use_case: "TL Per-Slide metrics" diff --git a/configs/experiment/test/images/vgg16/tile_histograms/base.yaml b/configs/experiment/test/images/vgg16/tile_histograms/base.yaml deleted file mode 100644 index 3780470a..00000000 --- a/configs/experiment/test/images/vgg16/tile_histograms/base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/vgg16/base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" diff --git a/configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml b/configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml deleted file mode 100644 index 6c6913b3..00000000 --- a/configs/experiment/test/images/vgg16/tile_histograms/stain_base.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/tile_histograms@callbacks - - /experiment/test/images/vgg16/stain_base - - _self_ - -datamodule: - test: - stratified_filter: true - -use_case: "TL Histograms" From 8bc425440206fbd59e27e1b01ad94b563bc430dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 10:33:27 +0000 Subject: [PATCH 231/284] feat: fix typo --- .../test/bag_of_embeddings/virchow2/tile_threshold.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml index e2909112..8d5ac728 100644 --- a/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml +++ b/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /callbacks/tile_threshold@callbacks + - /callbacks/tile_threshold_mil@callbacks - /experiment/test/bag_of_embeddings/virchow2/base - /data/mmci/tl_val@test_data - _self_ From e8646e1517f2732713c154264e0ed668c43b8367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 12:54:43 +0000 Subject: [PATCH 232/284] feat: tl threshold --- configs/experiment/test/bag_of_embeddings/base.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configs/experiment/test/bag_of_embeddings/base.yaml b/configs/experiment/test/bag_of_embeddings/base.yaml index 6ca0fd75..919deb91 100644 --- a/configs/experiment/test/bag_of_embeddings/base.yaml +++ b/configs/experiment/test/bag_of_embeddings/base.yaml @@ -5,6 +5,10 @@ defaults: - /datamodule/bag_of_embeddings_datamodule@datamodule - _self_ +slide_threshold: ${model_data.slide_threshold} +model: + sl_threshold: ${slide_threshold} + datamodule: batch_size: 4 num_workers: 8 From f885c7bd296cc9a875111b251712031fba634afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Fri, 17 Jul 2026 20:26:20 +0000 Subject: [PATCH 233/284] fix: imports --- prostate_cancer/callbacks/__init__.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/prostate_cancer/callbacks/__init__.py b/prostate_cancer/callbacks/__init__.py index b2b3d3e8..d3505081 100644 --- a/prostate_cancer/callbacks/__init__.py +++ b/prostate_cancer/callbacks/__init__.py @@ -3,7 +3,8 @@ from prostate_cancer.callbacks.carcinoma_prediction_table_callback import ( CarcinomaPredictionTableCallback, ) -from prostate_cancer.callbacks.curves_callback import CurvesCallback +from prostate_cancer.callbacks.curves_callback_mil import CurvesCallbackMIL +from prostate_cancer.callbacks.curves_callback_tl import CurvesCallbackTile from prostate_cancer.callbacks.estimation_callback import ( EstimationCallback, ) @@ -11,18 +12,26 @@ from prostate_cancer.callbacks.mil_prediction_callback import MILPredictionCallback from prostate_cancer.callbacks.nested_metrics_callback import NestedMetricsCallback from prostate_cancer.callbacks.num_positive_callback import NumPositiveCallback -from prostate_cancer.callbacks.tile_histograms_callback import TileHistogramsCallback +from prostate_cancer.callbacks.tile_histograms_callback_mil import ( + TileHistogramsCallbackMIL, +) +from prostate_cancer.callbacks.tile_histograms_callback_tl import ( + TileHistogramsCallbackTile, +) __all__ = [ "AggregatorCallback", "CAMExplainer", "CarcinomaPredictionTableCallback", - "CurvesCallback", + "CurvesCallbackMIL", + "CurvesCallbackTile", "EstimationCallback", "HeatmapCallback", "MILPredictionCallback", "NestedMetricsCallback", "NumPositiveCallback", "TileHistogramsCallback", + "TileHistogramsCallbackMIL", + "TileHistogramsCallbackTile", ] From 1382510d50298e95cb707af7b4b1427ab7c46418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 18 Jul 2026 15:02:25 +0000 Subject: [PATCH 234/284] fix: missing out --- prostate_cancer/attention_mil_model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index e385234b..189eb533 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -203,10 +203,10 @@ def validation_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: self.val_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) - def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: + def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: bags, tl_labels, sl_labels, _ = batch - sl_outputs, tl_outputs, mask, _ = self(bags) + sl_outputs, tl_outputs, mask, attention = self(bags) self.test_metrics_sl.update(sl_outputs, sl_labels) self.test_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) @@ -217,6 +217,7 @@ def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: self.log_dict( self.test_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) + return sl_outputs.sigmoid(), tl_outputs.sigmoid(), mask, attention def predict_step(self, batch: UnlabeledBagOfTilesSampleBatch) -> MILModelOutput: sl_preds_raw, tl_preds_raw, mask, attention = self(batch[0]) From e5a91d2342c19d85b82cab6dd56fc29e6987e3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 18 Jul 2026 18:45:47 +0000 Subject: [PATCH 235/284] feat: aggr eval --- .../postprocessing/eval_estimation/mpm/resnet50_224.yaml | 7 +++++++ .../eval_estimation/mpm/resnet50_stain_224.yaml | 7 +++++++ .../postprocessing/eval_estimation/mpm/vgg16_224.yaml | 7 +++++++ .../eval_estimation/mpm/vgg16_stain_224.yaml | 7 +++++++ .../eval_estimation/top_k/resnet50_224.yaml | 7 +++++++ .../eval_estimation/top_k/resnet50_stain_224.yaml | 7 +++++++ .../postprocessing/eval_estimation/top_k/vgg16_224.yaml | 7 +++++++ .../eval_estimation/top_k/vgg16_stain_224.yaml | 7 +++++++ .../max_aggregations/resnet50/base_224.yaml | 5 +++++ .../max_aggregations/resnet50/base_stain_224.yaml | 5 +++++ .../max_aggregations/resnet50/mmci_sl_group_1_224.yaml | 8 ++++++++ .../resnet50/stain_mmci_sl_group_1_224.yaml | 8 ++++++++ .../max_aggregations/vgg16/base_224.yaml | 5 +++++ .../max_aggregations/vgg16/base_stain_224.yaml | 5 +++++ .../max_aggregations/vgg16/mmci_sl_group_1_224.yaml | 8 ++++++++ .../vgg16/stain_mmci_sl_group_1_224.yaml | 8 ++++++++ .../mean_pool_max_aggregations/resnet50/base_224.yaml | 4 ++++ .../resnet50/base_stain_224.yaml | 4 ++++ .../resnet50/mmci_sl_group_1_224.yaml | 9 +++++++++ .../resnet50/stain_mmci_sl_group_1_224.yaml | 9 +++++++++ .../mean_pool_max_aggregations/vgg16/base_224.yaml | 4 ++++ .../mean_pool_max_aggregations/vgg16/base_stain_224.yaml | 4 ++++ .../vgg16/mmci_sl_group_1_224.yaml | 9 +++++++++ .../vgg16/stain_mmci_sl_group_1_224.yaml | 9 +++++++++ .../top_k_aggregations/resnet50/base_224.yaml | 4 ++++ .../top_k_aggregations/resnet50/base_stain_224.yaml | 4 ++++ .../top_k_aggregations/resnet50/mmci_sl_group_1_224.yaml | 9 +++++++++ .../resnet50/stain_mmci_sl_group_1_224.yaml | 9 +++++++++ .../top_k_aggregations/vgg16/base_224.yaml | 4 ++++ .../top_k_aggregations/vgg16/base_stain_224.yaml | 4 ++++ .../top_k_aggregations/vgg16/mmci_sl_group_1_224.yaml | 9 +++++++++ .../vgg16/stain_mmci_sl_group_1_224.yaml | 9 +++++++++ 32 files changed, 212 insertions(+) create mode 100644 configs/experiment/postprocessing/eval_estimation/mpm/resnet50_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/mpm/resnet50_stain_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/mpm/vgg16_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/mpm/vgg16_stain_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/top_k/resnet50_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/top_k/resnet50_stain_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/top_k/vgg16_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/top_k/vgg16_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/mmci_sl_group_1_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml diff --git a/configs/experiment/postprocessing/eval_estimation/mpm/resnet50_224.yaml b/configs/experiment/postprocessing/eval_estimation/mpm/resnet50_224.yaml new file mode 100644 index 00000000..2f29c2e2 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/mpm/resnet50_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/mpm/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/resnet50_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/mpm/resnet50_stain_224.yaml b/configs/experiment/postprocessing/eval_estimation/mpm/resnet50_stain_224.yaml new file mode 100644 index 00000000..ba6195a9 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/mpm/resnet50_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/mpm/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/mpm/vgg16_224.yaml b/configs/experiment/postprocessing/eval_estimation/mpm/vgg16_224.yaml new file mode 100644 index 00000000..2513d5b3 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/mpm/vgg16_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/mpm/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/vgg16_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/mpm/vgg16_stain_224.yaml b/configs/experiment/postprocessing/eval_estimation/mpm/vgg16_stain_224.yaml new file mode 100644 index 00000000..4db31a7b --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/mpm/vgg16_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/mpm/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/top_k/resnet50_224.yaml b/configs/experiment/postprocessing/eval_estimation/top_k/resnet50_224.yaml new file mode 100644 index 00000000..8386fc40 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/top_k/resnet50_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/top_k/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/resnet50_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/top_k/resnet50_stain_224.yaml b/configs/experiment/postprocessing/eval_estimation/top_k/resnet50_stain_224.yaml new file mode 100644 index 00000000..bd540de0 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/top_k/resnet50_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/top_k/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/top_k/vgg16_224.yaml b/configs/experiment/postprocessing/eval_estimation/top_k/vgg16_224.yaml new file mode 100644 index 00000000..a02c4080 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/top_k/vgg16_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/top_k/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/vgg16_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/top_k/vgg16_stain_224.yaml b/configs/experiment/postprocessing/eval_estimation/top_k/vgg16_stain_224.yaml new file mode 100644 index 00000000..f8c4e5f8 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/top_k/vgg16_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/top_k/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_224.yaml new file mode 100644 index 00000000..8ac87a27 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_224.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/base + - /model/checkpoints/mmci_tl/resnet50_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_stain_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_stain_224.yaml new file mode 100644 index 00000000..e54c70e1 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_stain_224.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/base + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..de9a5f39 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/mmci_sl_group_1_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..b6081695 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/resnet50/base_stain_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_224.yaml new file mode 100644 index 00000000..3b571bda --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_224.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/base + - /model/checkpoints/mmci_tl/vgg16_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_stain_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_stain_224.yaml new file mode 100644 index 00000000..c460f2c8 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_stain_224.yaml @@ -0,0 +1,5 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/base + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..850a4f58 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/mmci_sl_group_1_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..9f1aede5 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/max_aggregations/vgg16/base_stain_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_224.yaml new file mode 100644 index 00000000..d388eac1 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/resnet50_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_stain_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_stain_224.yaml new file mode 100644 index 00000000..d9d95385 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_stain_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..89996763 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} +pred_column: "pred_kernel_size=6" diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..b405fece --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/resnet50/base_stain_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} +pred_column: "pred_kernel_size=1" diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_224.yaml new file mode 100644 index 00000000..dabfe82f --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/vgg16_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_stain_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_stain_224.yaml new file mode 100644 index 00000000..fe5e2fa9 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_stain_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..3959a107 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} +pred_column: "pred_kernel_size=7" diff --git a/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..1c9e7e1f --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/mean_pool_max_aggregations/vgg16/base_stain_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} +pred_column: "pred_kernel_size=1" diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_224.yaml new file mode 100644 index 00000000..d388eac1 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/resnet50_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_stain_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_stain_224.yaml new file mode 100644 index 00000000..d9d95385 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_stain_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..f0697bfc --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} +pred_column: "pred_kernel_size=4_k=4" diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..15c69bce --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/stain_mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/top_k_aggregations/resnet50/base_stain_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} +pred_column: "pred_kernel_size=1_k=4" diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_224.yaml new file mode 100644 index 00000000..dabfe82f --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/vgg16_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_stain_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_stain_224.yaml new file mode 100644 index 00000000..fe5e2fa9 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_stain_224.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..71f48a99 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} +pred_column: "pred_kernel_size=5_k=4" diff --git a/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml new file mode 100644 index 00000000..472be9d3 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/stain_mmci_sl_group_1_224.yaml @@ -0,0 +1,9 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_curves/top_k_aggregations/vgg16/base_stain_224 + - /data/mmci/sl_group_1@data + - _self_ + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} +pred_column: "pred_kernel_size=1_k=4" From d324277fee8d3c7171bbd3a5f0e646390c4f060c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 06:53:37 +0000 Subject: [PATCH 236/284] feat: mmci sl group 1 work --- .../postprocessing/eval_estimation/span/resnet50_224.yaml | 7 +++++++ .../eval_estimation/span/resnet50_stain_224.yaml | 7 +++++++ .../postprocessing/eval_estimation/span/vgg16_224.yaml | 7 +++++++ .../eval_estimation/span/vgg16_stain_224.yaml | 7 +++++++ .../slide_level_eval/resnet50_224/base.yaml | 4 ++++ .../resnet50_224/max_aggregations/base.yaml | 8 ++++++++ .../resnet50_224/max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../resnet50_224/max_aggregations/mmci_tl_test.yaml | 7 +++++++ .../resnet50_224/mean_pool_max_aggregations/base.yaml | 8 ++++++++ .../mean_pool_max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../resnet50_224/span_aggregations/base.yaml | 8 ++++++++ .../resnet50_224/span_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../resnet50_224/top_k_aggregations/base.yaml | 8 ++++++++ .../resnet50_224/top_k_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../slide_level_eval/resnet50_stain_224/base.yaml | 4 ++++ .../resnet50_stain_224/max_aggregations/base.yaml | 8 ++++++++ .../max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../resnet50_stain_224/max_aggregations/mmci_tl_test.yaml | 7 +++++++ .../mean_pool_max_aggregations/base.yaml | 8 ++++++++ .../mean_pool_max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../resnet50_stain_224/span_aggregations/base.yaml | 8 ++++++++ .../span_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../resnet50_stain_224/top_k_aggregations/base.yaml | 8 ++++++++ .../top_k_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../postprocessing/slide_level_eval/vgg16_224/base.yaml | 4 ++++ .../slide_level_eval/vgg16_224/max_aggregations/base.yaml | 8 ++++++++ .../vgg16_224/max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../vgg16_224/max_aggregations/mmci_tl_test.yaml | 7 +++++++ .../vgg16_224/mean_pool_max_aggregations/base.yaml | 8 ++++++++ .../mean_pool_max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../vgg16_224/span_aggregations/base.yaml | 8 ++++++++ .../vgg16_224/span_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../vgg16_224/top_k_aggregations/base.yaml | 8 ++++++++ .../vgg16_224/top_k_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../slide_level_eval/vgg16_stain_224/base.yaml | 4 ++++ .../vgg16_stain_224/max_aggregations/base.yaml | 8 ++++++++ .../vgg16_stain_224/max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../vgg16_stain_224/max_aggregations/mmci_tl_test.yaml | 7 +++++++ .../vgg16_stain_224/mean_pool_max_aggregations/base.yaml | 8 ++++++++ .../mean_pool_max_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../vgg16_stain_224/span_aggregations/base.yaml | 8 ++++++++ .../span_aggregations/mmci_sl_group_1.yaml | 7 +++++++ .../vgg16_stain_224/top_k_aggregations/base.yaml | 8 ++++++++ .../top_k_aggregations/mmci_sl_group_1.yaml | 7 +++++++ configs/model/checkpoints/mmci_tl/resnet50_224.yaml | 6 +++--- configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml | 6 +++--- configs/model/checkpoints/mmci_tl/vgg16_224.yaml | 6 +++--- configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml | 6 +++--- 48 files changed, 324 insertions(+), 12 deletions(-) create mode 100644 configs/experiment/postprocessing/eval_estimation/span/resnet50_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/span/resnet50_stain_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/span/vgg16_224.yaml create mode 100644 configs/experiment/postprocessing/eval_estimation/span/vgg16_stain_224.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_tl_test.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_tl_test.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_tl_test.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_tl_test.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/mmci_sl_group_1.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/base.yaml create mode 100644 configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/mmci_sl_group_1.yaml diff --git a/configs/experiment/postprocessing/eval_estimation/span/resnet50_224.yaml b/configs/experiment/postprocessing/eval_estimation/span/resnet50_224.yaml new file mode 100644 index 00000000..473915aa --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/span/resnet50_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/span/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/resnet50_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/span/resnet50_stain_224.yaml b/configs/experiment/postprocessing/eval_estimation/span/resnet50_stain_224.yaml new file mode 100644 index 00000000..8b4fc22d --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/span/resnet50_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/span/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/span/vgg16_224.yaml b/configs/experiment/postprocessing/eval_estimation/span/vgg16_224.yaml new file mode 100644 index 00000000..904d2a9a --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/span/vgg16_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/span/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/vgg16_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/eval_estimation/span/vgg16_stain_224.yaml b/configs/experiment/postprocessing/eval_estimation/span/vgg16_stain_224.yaml new file mode 100644 index 00000000..e4d00a26 --- /dev/null +++ b/configs/experiment/postprocessing/eval_estimation/span/vgg16_stain_224.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/eval_estimation/span/base + - /data/mmci/sl_group_1@data + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data + - _self_ diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/base.yaml new file mode 100644 index 00000000..d388eac1 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/base.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/resnet50_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/base.yaml new file mode 100644 index 00000000..2885603d --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/base + +aggregation_method: "Max" +pred_column: "prediction" +t: ${model_data.slide_max_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..07285114 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_tl_test.yaml new file mode 100644 index 00000000..84c33573 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/mmci_tl_test.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/max_aggregations/base + - /data/mmci/tl_test@data + +preds_uri: null diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/base.yaml new file mode 100644 index 00000000..a98f533e --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/base + +aggregation_method: "Mean Pool Max" +pred_column: "pred_kernel_size=6" +t: ${model_data.slide_mpm_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..36f5c1cf --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/mean_pool_max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/base.yaml new file mode 100644 index 00000000..f2fed72f --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/base + +aggregation_method: "Span" +pred_column: "pred_k=5" +t: ${model_data.slide_span_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..d337beba --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/span_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.span.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/base.yaml new file mode 100644 index 00000000..ff629be5 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/base + +aggregation_method: "Top-k" +pred_column: "pred_kernel_size=5_k=4" +t: ${model_data.slide_top_k_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..b36ad3bc --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_224/top_k_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/base.yaml new file mode 100644 index 00000000..d9d95385 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/base.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/resnet50_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/base.yaml new file mode 100644 index 00000000..47a4fe86 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/base + +aggregation_method: "Max" +pred_column: "prediction" +t: ${model_data.slide_max_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..050de748 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_tl_test.yaml new file mode 100644 index 00000000..13dbd0db --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/mmci_tl_test.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/max_aggregations/base + - /data/mmci/tl_test@data + +preds_uri: null diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/base.yaml new file mode 100644 index 00000000..06233b06 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/base + +aggregation_method: "Mean Pool Max" +pred_column: "pred_kernel_size=1" +t: ${model_data.slide_mpm_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..a2008124 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/mean_pool_max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/base.yaml new file mode 100644 index 00000000..6c68075a --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/base + +aggregation_method: "Span" +pred_column: "pred_k=5" +t: ${model_data.slide_span_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..b923102d --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/span_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.span.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/base.yaml new file mode 100644 index 00000000..91329a79 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/base + +aggregation_method: "Top-k" +pred_column: "pred_kernel_size=1_k=2" +t: ${model_data.slide_top_k_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..37ce1b31 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/resnet50_stain_224/top_k_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/base.yaml new file mode 100644 index 00000000..dabfe82f --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/base.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/vgg16_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/base.yaml new file mode 100644 index 00000000..c999d254 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/base + +aggregation_method: "Max" +pred_column: "prediction" +t: ${model_data.slide_max_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..b7152834 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_tl_test.yaml new file mode 100644 index 00000000..827208a6 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/mmci_tl_test.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/max_aggregations/base + - /data/mmci/tl_test@data + +preds_uri: null diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/base.yaml new file mode 100644 index 00000000..dc21cebc --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/base + +aggregation_method: "Mean Pool Max" +pred_column: "pred_kernel_size=7" +t: ${model_data.slide_mpm_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..4dc48cbf --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/mean_pool_max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/base.yaml new file mode 100644 index 00000000..828f5aa5 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/base + +aggregation_method: "Span" +pred_column: "pred_k=5" +t: ${model_data.slide_span_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..a7d6ab62 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/span_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.span.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/base.yaml new file mode 100644 index 00000000..bbfa7fe9 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/base + +aggregation_method: "Top-k" +pred_column: "pred_kernel_size=5_k=4" +t: ${model_data.slide_top_k_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..c0a75c58 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_224/top_k_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/base.yaml new file mode 100644 index 00000000..fe5e2fa9 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/base.yaml @@ -0,0 +1,4 @@ +# @package _global_ + +defaults: + - /model/checkpoints/mmci_tl/vgg16_stain_224@model_data diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/base.yaml new file mode 100644 index 00000000..fcc351ec --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/base + +aggregation_method: "Max" +pred_column: "prediction" +t: ${model_data.slide_max_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..68507789 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_tl_test.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_tl_test.yaml new file mode 100644 index 00000000..ea6f6f28 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/mmci_tl_test.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/max_aggregations/base + - /data/mmci/tl_test@data + +preds_uri: null diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/base.yaml new file mode 100644 index 00000000..65bd3b4f --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/base + +aggregation_method: "Mean Pool Max" +pred_column: "pred_kernel_size=1" +t: ${model_data.slide_mpm_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..0ab876d8 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/mean_pool_max_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.mean_pool_max.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/base.yaml new file mode 100644 index 00000000..d52a4823 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/base + +aggregation_method: "Span" +pred_column: "pred_k=5" +t: ${model_data.slide_span_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..58b0e796 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/span_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.span.mmci_sl_group_1} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/base.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/base.yaml new file mode 100644 index 00000000..16a0bd9a --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/base + +aggregation_method: "Top-k" +pred_column: "pred_kernel_size=1_k=4" +t: ${model_data.slide_top_k_threshold} diff --git a/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/mmci_sl_group_1.yaml b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/mmci_sl_group_1.yaml new file mode 100644 index 00000000..4b22c9b2 --- /dev/null +++ b/configs/experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/mmci_sl_group_1.yaml @@ -0,0 +1,7 @@ +# @package _global_ + +defaults: + - /experiment/postprocessing/slide_level_eval/vgg16_stain_224/top_k_aggregations/base + - /data/mmci/sl_group_1@data + +preds_uri: ${model_data.slide_level_prediction_uris.top_k.mmci_sl_group_1} diff --git a/configs/model/checkpoints/mmci_tl/resnet50_224.yaml b/configs/model/checkpoints/mmci_tl/resnet50_224.yaml index 49911bc4..533eb066 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_224.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_224.yaml @@ -3,9 +3,9 @@ defaults: - _self_ tile_threshold: 0.82 -slide_max_threshold: 0 -slide_mpm_threshold: 0 -slide_top_k_threshold: 0 +slide_max_threshold: 0.9999972582 +slide_mpm_threshold: 0.9702595472 +slide_top_k_threshold: 0.9986075759 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/d9bd39a0207c4b9484ad92574c50b503/artifacts/checkpoints/epoch=0-step=13889/checkpoint.ckpt" diff --git a/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml b/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml index e139901a..da21046e 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml @@ -3,9 +3,9 @@ defaults: - _self_ tile_threshold: 0.85 -slide_max_threshold: 0 -slide_mpm_threshold: 0 -slide_top_k_threshold: 0 +slide_max_threshold: 0.9999930859 +slide_mpm_threshold: 0.9998937845 +slide_top_k_threshold: 0.9997090101 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/80543b4ec2d14ca48384e6d52a7418fb/artifacts/checkpoints/epoch=0-step=55556/checkpoint.ckpt" diff --git a/configs/model/checkpoints/mmci_tl/vgg16_224.yaml b/configs/model/checkpoints/mmci_tl/vgg16_224.yaml index cd4df99d..993ba92f 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_224.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_224.yaml @@ -3,9 +3,9 @@ defaults: - _self_ tile_threshold: 0.96 -slide_max_threshold: 0 -slide_mpm_threshold: 0 -slide_top_k_threshold: 0 +slide_max_threshold: 0.999999999 +slide_mpm_threshold: 0.9995754957 +slide_top_k_threshold: 0.9999379516 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/2b4986681bb24e1abd85a9108a7a49bb/artifacts/checkpoints/epoch=1-step=97226/checkpoint.ckpt" diff --git a/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml b/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml index 60e020fc..912791ca 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml @@ -3,9 +3,9 @@ defaults: - _self_ tile_threshold: 0.93 -slide_max_threshold: 0 -slide_mpm_threshold: 0 -slide_top_k_threshold: 0 +slide_max_threshold: 0.999835372 +slide_mpm_threshold: 0.999294281 +slide_top_k_threshold: 0.9988395572 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/98e0d43027664084aef1beebaeac7fff/artifacts/checkpoints/epoch=1-step=111115/checkpoint.ckpt" From 96247738fd83bd0c75c5feab958ca4a979f365b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 07:18:30 +0000 Subject: [PATCH 237/284] feat: non inferiority --- notebooks/eval/non_inferiority.ipynb | 566 ++++++++++++++++++++++++--- 1 file changed, 513 insertions(+), 53 deletions(-) diff --git a/notebooks/eval/non_inferiority.ipynb b/notebooks/eval/non_inferiority.ipynb index dd6c7a45..e2e25338 100644 --- a/notebooks/eval/non_inferiority.ipynb +++ b/notebooks/eval/non_inferiority.ipynb @@ -170,6 +170,78 @@ " \"pred_k=3\",\n", " ),\n", " },\n", + " \"resnet50_224\": {\n", + " \"max\": (\n", + " \"mlflow-artifacts:/65/098f0fb5aad246389e39bfd2265247a4/artifacts/tables/aggregated_predictions.json\",\n", + " \"prediction\",\n", + " ),\n", + " \"mpm\": (\n", + " \"mlflow-artifacts:/65/e87e9b41dfaf4a878069af2045f164ef/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=6\",\n", + " ),\n", + " \"topk\": (\n", + " \"mlflow-artifacts:/65/f79c58db2f9f48148a61c9948bd04541/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=5_k=4\",\n", + " ),\n", + " \"span\": (\n", + " \"mlflow-artifacts:/65/423dd217a0b94fbabaee1e1bdda156d8/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_k=5\",\n", + " ),\n", + " },\n", + " \"resnet50_stain_224\": {\n", + " \"max\": (\n", + " \"mlflow-artifacts:/65/eec60275b3c247a2b8fc486d1a46aa6f/artifacts/tables/aggregated_predictions.json\",\n", + " \"prediction\",\n", + " ),\n", + " \"mpm\": (\n", + " \"mlflow-artifacts:/65/a6e128babae44dab9d2785624e0107a0/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=1\",\n", + " ),\n", + " \"topk\": (\n", + " \"mlflow-artifacts:/65/d97f25bcecc24ad8bd223b3ae0af970f/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=1_k=2\",\n", + " ),\n", + " \"span\": (\n", + " \"mlflow-artifacts:/65/254fc6852ea845c1b11d799e5d24d947/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_k=5\",\n", + " ),\n", + " },\n", + " \"vgg16_224\": {\n", + " \"max\": (\n", + " \"mlflow-artifacts:/65/8b607e5204f548d28d11ac392fd4a835/artifacts/tables/aggregated_predictions.json\",\n", + " \"prediction\",\n", + " ),\n", + " \"mpm\": (\n", + " \"mlflow-artifacts:/65/8ee0c7d3c61d46acad41262ce6a065ec/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=7\",\n", + " ),\n", + " \"topk\": (\n", + " \"mlflow-artifacts:/65/17b3a4b3c9bc420ca4fff7c185063f36/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=5_k=4\",\n", + " ),\n", + " \"span\": (\n", + " \"mlflow-artifacts:/65/ce1bdc240623403aa45cca7465d47b29/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_k=5\",\n", + " ),\n", + " },\n", + " \"vgg16_stain_224\": {\n", + " \"max\": (\n", + " \"mlflow-artifacts:/65/a7495ce0ac2b4fb99d0cc55e912a970c/artifacts/tables/aggregated_predictions.json\",\n", + " \"prediction\",\n", + " ),\n", + " \"mpm\": (\n", + " \"mlflow-artifacts:/65/b7a34bdffb2b4f729b5c8ce98d9921ce/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=1\",\n", + " ),\n", + " \"topk\": (\n", + " \"mlflow-artifacts:/65/2bf6522353e34344aed0222c346f7230/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_kernel_size=1_k=4\",\n", + " ),\n", + " \"span\": (\n", + " \"mlflow-artifacts:/65/a392285e4bde413bb8bf03e03d921d71/artifacts/tables/aggregated_predictions.json\",\n", + " \"pred_k=5\",\n", + " ),\n", + " },\n", " \"gvit\": {\n", " \"max\": (\n", " \"mlflow-artifacts:/65/14059f41cc864b84b0c45a8967150b4a/artifacts/tables/aggregated_predictions.json\",\n", @@ -229,46 +301,62 @@ "name": "stderr", "output_type": "stream", "text": [ - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 53.89it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 43.36it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 40.78it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 50.16it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 48.69it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.31it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 42.49it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 42.13it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 58.64it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 48.73it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 37.54it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 48.54it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.72it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 41.15it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 41.69it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 123.36it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.48it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 62.81it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 55.45it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.05it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 49.42it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 38.48it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 26.07it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 47.28it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 34.40it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 41.97it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 24.47it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 35.65it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 34.76it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 65.69it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 46.21it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 45.24it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 42.87it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 35.36it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 47.84it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 38.83it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 14.45it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.24it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 37.00it/s]\n", - "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.78it/s]\n" + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 46.19it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 35.32it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.93it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.18it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 39.64it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 44.64it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 47.95it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 40.67it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 56.68it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 45.20it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 52.19it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 56.79it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 56.17it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 41.66it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 49.27it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 32.32it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 56.79it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 65.45it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 44.34it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 59.96it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 62.58it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 36.81it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 45.03it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 30.51it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 60.96it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 40.22it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 34.88it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 48.02it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.68it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 47.38it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 36.28it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 44.85it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 161.24it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 156.74it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 203.21it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 268.69it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 242.49it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 224.58it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 188.36it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 236.59it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 188.20it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 176.54it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 193.14it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 178.19it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 185.47it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 177.95it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 166.49it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 187.96it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 40.89it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 53.15it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 67.52it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 34.52it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 49.28it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 51.53it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 35.06it/s]\n", + "Downloading artifacts: 100%|██████████| 1/1 [00:00<00:00, 40.00it/s]\n" ] } ], @@ -284,7 +372,7 @@ " return mlflow.artifacts.download_artifacts(uri)\n", "\n", "\n", - "assert len(DATA) == 10 # 10 models\n", + "assert len(DATA) == 14 # 14 models\n", "\n", "for model_name, methods in DATA.items():\n", " assert len(methods) == 4 # 4 aggr. methods\n", @@ -458,6 +546,10 @@ "resnet50_stain_df = evaluate_single_model(master_df, \"resnet50_stain\", margin)\n", "vgg16_df = evaluate_single_model(master_df, \"vgg16\", margin)\n", "vgg16_stain_df = evaluate_single_model(master_df, \"vgg16_stain\", margin)\n", + "resnet50_224_df = evaluate_single_model(master_df, \"resnet50_224\", margin)\n", + "resnet50_stain_224_df = evaluate_single_model(master_df, \"resnet50_stain_224\", margin)\n", + "vgg16_224_df = evaluate_single_model(master_df, \"vgg16_224\", margin)\n", + "vgg16_stain_224_df = evaluate_single_model(master_df, \"vgg16_stain_224\", margin)\n", "gvit_df = evaluate_single_model(master_df, \"gvit\", margin)\n", "gvit_stain_df = evaluate_single_model(master_df, \"gvit_stain\", margin)" ] @@ -827,7 +919,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "id": "6ebe9ce8-e139-4092-b555-b838be8af66b", "metadata": {}, "outputs": [ @@ -908,7 +1000,7 @@ "2 span " ] }, - "execution_count": 9, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -919,7 +1011,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "id": "ec92f096-2f05-495b-aac9-4b66bd26f2f1", "metadata": {}, "outputs": [ @@ -1000,7 +1092,7 @@ "2 resnet50_stain span " ] }, - "execution_count": 10, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -1011,7 +1103,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "id": "10af5800-e5c7-49e4-8b64-ded5ad423a37", "metadata": {}, "outputs": [ @@ -1087,7 +1179,7 @@ "2 -0.040223 -0.061173 -0.021482 -0.024334 True vgg16 span" ] }, - "execution_count": 11, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -1098,7 +1190,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "id": "734fc8b4-b2e2-4638-90fc-25b9e6281eed", "metadata": {}, "outputs": [ @@ -1179,7 +1271,7 @@ "2 span " ] }, - "execution_count": 12, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -1190,7 +1282,375 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 17, + "id": "0b0ca27f-6c8b-4dfd-baff-a93b67e1cf9e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mean_deltaci_95_lowerci_95_upperupper_95non_inferiormodelmethod
00.0452470.0169510.0737680.069177Falseresnet50_224mpm
10.0441700.0169000.0717390.066910Falseresnet50_224topk
2-0.136158-0.185576-0.085008-0.093033Trueresnet50_224span
\n", + "
" + ], + "text/plain": [ + " mean_delta ci_95_lower ci_95_upper upper_95 non_inferior model \\\n", + "0 0.045247 0.016951 0.073768 0.069177 False resnet50_224 \n", + "1 0.044170 0.016900 0.071739 0.066910 False resnet50_224 \n", + "2 -0.136158 -0.185576 -0.085008 -0.093033 True resnet50_224 \n", + "\n", + " method \n", + "0 mpm \n", + "1 topk \n", + "2 span " + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "resnet50_224_df" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "64c39222-675f-4bbd-b79c-f900a5e9e794", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mean_deltaci_95_lowerci_95_upperupper_95non_inferiormodelmethod
0-0.008204-0.0204030.0034670.001623Trueresnet50_stain_224mpm
1-0.003095-0.0141590.0075110.005709Trueresnet50_stain_224topk
2-0.375799-0.403981-0.346074-0.350694Trueresnet50_stain_224span
\n", + "
" + ], + "text/plain": [ + " mean_delta ci_95_lower ci_95_upper upper_95 non_inferior \\\n", + "0 -0.008204 -0.020403 0.003467 0.001623 True \n", + "1 -0.003095 -0.014159 0.007511 0.005709 True \n", + "2 -0.375799 -0.403981 -0.346074 -0.350694 True \n", + "\n", + " model method \n", + "0 resnet50_stain_224 mpm \n", + "1 resnet50_stain_224 topk \n", + "2 resnet50_stain_224 span " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "resnet50_stain_224_df" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "c7d79d14-a6e4-430b-a8ea-091cd3ddeda6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mean_deltaci_95_lowerci_95_upperupper_95non_inferiormodelmethod
00.1465710.1090780.1836450.177566Falsevgg16_224mpm
10.1420040.1065660.1770230.171288Falsevgg16_224topk
2-0.090265-0.130140-0.050020-0.056164Truevgg16_224span
\n", + "
" + ], + "text/plain": [ + " mean_delta ci_95_lower ci_95_upper upper_95 non_inferior model \\\n", + "0 0.146571 0.109078 0.183645 0.177566 False vgg16_224 \n", + "1 0.142004 0.106566 0.177023 0.171288 False vgg16_224 \n", + "2 -0.090265 -0.130140 -0.050020 -0.056164 True vgg16_224 \n", + "\n", + " method \n", + "0 mpm \n", + "1 topk \n", + "2 span " + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vgg16_224_df" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "3accfa19-ff09-4287-88b6-682bbdc791c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mean_deltaci_95_lowerci_95_upperupper_95non_inferiormodelmethod
0-0.004084-0.0121540.0031790.002107Truevgg16_stain_224mpm
10.000383-0.0064480.0072170.006022Truevgg16_stain_224topk
2-0.173048-0.201552-0.143990-0.148353Truevgg16_stain_224span
\n", + "
" + ], + "text/plain": [ + " mean_delta ci_95_lower ci_95_upper upper_95 non_inferior \\\n", + "0 -0.004084 -0.012154 0.003179 0.002107 True \n", + "1 0.000383 -0.006448 0.007217 0.006022 True \n", + "2 -0.173048 -0.201552 -0.143990 -0.148353 True \n", + "\n", + " model method \n", + "0 vgg16_stain_224 mpm \n", + "1 vgg16_stain_224 topk \n", + "2 vgg16_stain_224 span " + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vgg16_stain_224_df" + ] + }, + { + "cell_type": "code", + "execution_count": 15, "id": "5b6b384a-4d60-4c8a-b1e4-1dc1e281fe53", "metadata": {}, "outputs": [ @@ -1266,7 +1726,7 @@ "2 -0.403856 -0.428283 -0.377248 -0.382055 True gvit span" ] }, - "execution_count": 13, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -1277,7 +1737,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "id": "e79890e4-99df-4049-a394-fb9f1b298949", "metadata": {}, "outputs": [ @@ -1358,7 +1818,7 @@ "2 span " ] }, - "execution_count": 14, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } From 691677449a5f9ff713ca29090ad1c9e1a5c11fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 07:28:59 +0000 Subject: [PATCH 238/284] feat: tl val heatmaps --- .../predict/images/vgg16/heatmaps/base_224.yaml | 8 ++++++++ .../predict/images/vgg16/heatmaps/mmci_tl_val_224.yaml | 6 ++++++ .../predict/images/vgg16/heatmaps/stain_base_224.yaml | 8 ++++++++ .../images/vgg16/heatmaps/stain_mmci_tl_val_224.yaml | 6 ++++++ 4 files changed, 28 insertions(+) create mode 100644 configs/experiment/predict/images/vgg16/heatmaps/base_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/heatmaps/mmci_tl_val_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/heatmaps/stain_base_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/heatmaps/stain_mmci_tl_val_224.yaml diff --git a/configs/experiment/predict/images/vgg16/heatmaps/base_224.yaml b/configs/experiment/predict/images/vgg16/heatmaps/base_224.yaml new file mode 100644 index 00000000..8fad03ef --- /dev/null +++ b/configs/experiment/predict/images/vgg16/heatmaps/base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/base_224 + - /callbacks/heatmap@callbacks + - _self_ + +use_case: "Heatmaps" diff --git a/configs/experiment/predict/images/vgg16/heatmaps/mmci_tl_val_224.yaml b/configs/experiment/predict/images/vgg16/heatmaps/mmci_tl_val_224.yaml new file mode 100644 index 00000000..40109d33 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/heatmaps/mmci_tl_val_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/heatmaps/base_224 + - /data/mmci/tl_val@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/heatmaps/stain_base_224.yaml b/configs/experiment/predict/images/vgg16/heatmaps/stain_base_224.yaml new file mode 100644 index 00000000..00680527 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/heatmaps/stain_base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/stain_base_224 + - /callbacks/heatmap@callbacks + - _self_ + +use_case: "Heatmaps" diff --git a/configs/experiment/predict/images/vgg16/heatmaps/stain_mmci_tl_val_224.yaml b/configs/experiment/predict/images/vgg16/heatmaps/stain_mmci_tl_val_224.yaml new file mode 100644 index 00000000..07257527 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/heatmaps/stain_mmci_tl_val_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/heatmaps/stain_base_224 + - /data/mmci/tl_val@predict_data + - _self_ From bd78d3c4c69168bf3bfb6d78eb02601e062b4210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 07:32:19 +0000 Subject: [PATCH 239/284] feat: resnet50 --- .../predict/images/resnet50/heatmaps/base_224.yaml | 8 ++++++++ .../predict/images/resnet50/heatmaps/mmci_tl_val_224.yaml | 6 ++++++ .../predict/images/resnet50/heatmaps/stain_base_224.yaml | 8 ++++++++ .../images/resnet50/heatmaps/stain_mmci_tl_val_224.yaml | 6 ++++++ 4 files changed, 28 insertions(+) create mode 100644 configs/experiment/predict/images/resnet50/heatmaps/base_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/heatmaps/mmci_tl_val_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/heatmaps/stain_base_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/heatmaps/stain_mmci_tl_val_224.yaml diff --git a/configs/experiment/predict/images/resnet50/heatmaps/base_224.yaml b/configs/experiment/predict/images/resnet50/heatmaps/base_224.yaml new file mode 100644 index 00000000..dc08bd8c --- /dev/null +++ b/configs/experiment/predict/images/resnet50/heatmaps/base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/base_224 + - /callbacks/heatmap@callbacks + - _self_ + +use_case: "Heatmaps" diff --git a/configs/experiment/predict/images/resnet50/heatmaps/mmci_tl_val_224.yaml b/configs/experiment/predict/images/resnet50/heatmaps/mmci_tl_val_224.yaml new file mode 100644 index 00000000..236de55a --- /dev/null +++ b/configs/experiment/predict/images/resnet50/heatmaps/mmci_tl_val_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/heatmaps/base_224 + - /data/mmci/tl_val@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/heatmaps/stain_base_224.yaml b/configs/experiment/predict/images/resnet50/heatmaps/stain_base_224.yaml new file mode 100644 index 00000000..02380023 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/heatmaps/stain_base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/stain_base_224 + - /callbacks/heatmap@callbacks + - _self_ + +use_case: "Heatmaps" diff --git a/configs/experiment/predict/images/resnet50/heatmaps/stain_mmci_tl_val_224.yaml b/configs/experiment/predict/images/resnet50/heatmaps/stain_mmci_tl_val_224.yaml new file mode 100644 index 00000000..3f925e49 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/heatmaps/stain_mmci_tl_val_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/heatmaps/stain_base_224 + - /data/mmci/tl_val@predict_data + - _self_ From 807541efd905737728be7ddaec4d79f3249103b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 10:07:07 +0000 Subject: [PATCH 240/284] feat: tl test --- .../test/images/resnet50/test_suite/base_224.yaml | 8 ++++++++ .../test/images/resnet50/test_suite/mmci_tl_224.yaml | 6 ++++++ .../test/images/resnet50/test_suite/stain_base_224.yaml | 8 ++++++++ .../images/resnet50/test_suite/stain_mmci_tl_224.yaml | 6 ++++++ .../experiment/test/images/vgg16/test_suite/base_224.yaml | 8 ++++++++ .../test/images/vgg16/test_suite/mmci_tl_224.yaml | 6 ++++++ .../test/images/vgg16/test_suite/stain_base_224.yaml | 8 ++++++++ .../test/images/vgg16/test_suite/stain_mmci_tl_224.yaml | 6 ++++++ 8 files changed, 56 insertions(+) create mode 100644 configs/experiment/test/images/resnet50/test_suite/base_224.yaml create mode 100644 configs/experiment/test/images/resnet50/test_suite/mmci_tl_224.yaml create mode 100644 configs/experiment/test/images/resnet50/test_suite/stain_base_224.yaml create mode 100644 configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/base_224.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/mmci_tl_224.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/stain_base_224.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/stain_mmci_tl_224.yaml diff --git a/configs/experiment/test/images/resnet50/test_suite/base_224.yaml b/configs/experiment/test/images/resnet50/test_suite/base_224.yaml new file mode 100644 index 00000000..41756bbe --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/images/resnet50/base_224 + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/images/resnet50/test_suite/mmci_tl_224.yaml b/configs/experiment/test/images/resnet50/test_suite/mmci_tl_224.yaml new file mode 100644 index 00000000..fe006f05 --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/mmci_tl_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/images/resnet50/test_suite/base_224 + - /data/mmci/tl_test@test_data + - _self_ diff --git a/configs/experiment/test/images/resnet50/test_suite/stain_base_224.yaml b/configs/experiment/test/images/resnet50/test_suite/stain_base_224.yaml new file mode 100644 index 00000000..4c100cd6 --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/stain_base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/images/resnet50/stain_base_224 + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml b/configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml new file mode 100644 index 00000000..e24e123f --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/images/resnet50/test_suite/stain_base + - /data/mmci/tl_test@test_data + - _self_ diff --git a/configs/experiment/test/images/vgg16/test_suite/base_224.yaml b/configs/experiment/test/images/vgg16/test_suite/base_224.yaml new file mode 100644 index 00000000..ad5b6bc0 --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/images/vgg16/base_224 + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/images/vgg16/test_suite/mmci_tl_224.yaml b/configs/experiment/test/images/vgg16/test_suite/mmci_tl_224.yaml new file mode 100644 index 00000000..48860f1c --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/mmci_tl_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/images/vgg16/test_suite/base_224 + - /data/mmci/tl_test@test_data + - _self_ diff --git a/configs/experiment/test/images/vgg16/test_suite/stain_base_224.yaml b/configs/experiment/test/images/vgg16/test_suite/stain_base_224.yaml new file mode 100644 index 00000000..2473fd0c --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/stain_base_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite@callbacks + - /experiment/test/images/vgg16/stain_base_224 + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/images/vgg16/test_suite/stain_mmci_tl_224.yaml b/configs/experiment/test/images/vgg16/test_suite/stain_mmci_tl_224.yaml new file mode 100644 index 00000000..5b5c9ed7 --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/stain_mmci_tl_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/images/vgg16/test_suite/stain_base_224 + - /data/mmci/tl_test@test_data + - _self_ From a595f8126099f97b05d357521084d8f753884650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 12:10:21 +0000 Subject: [PATCH 241/284] fix: mmci tl --- .../test/images/resnet50/test_suite/stain_mmci_tl_224.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml b/configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml index e24e123f..92ebcecc 100644 --- a/configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml +++ b/configs/experiment/test/images/resnet50/test_suite/stain_mmci_tl_224.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/test/images/resnet50/test_suite/stain_base + - /experiment/test/images/resnet50/test_suite/stain_base_224 - /data/mmci/tl_test@test_data - _self_ From 7222e79257cb4129526b4872fe18bf68686f0759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 20:06:09 +0000 Subject: [PATCH 242/284] feat: eval script --- configs/callbacks/multi_aggregator_eval.yaml | 8 + .../vgg16/multi_aggregator_eval/base.yaml | 21 ++ .../vgg16/multi_aggregator_eval/base_224.yaml | 21 ++ prostate_cancer/callbacks/__init__.py | 4 + .../multi_aggregator_eval_callback.py | 190 ++++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 configs/callbacks/multi_aggregator_eval.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/base.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/base_224.yaml create mode 100644 prostate_cancer/callbacks/multi_aggregator_eval_callback.py diff --git a/configs/callbacks/multi_aggregator_eval.yaml b/configs/callbacks/multi_aggregator_eval.yaml new file mode 100644 index 00000000..3ea0cf37 --- /dev/null +++ b/configs/callbacks/multi_aggregator_eval.yaml @@ -0,0 +1,8 @@ +multi_aggregator_eval_callback: + _target_: prostate_cancer.callbacks.MultiAggregatorEvalCallback + max_aggregator: ${max_aggregator} + mean_pool_max_aggregator: ${mean_pool_max_aggregator} + top_k_aggregator: ${top_k_aggregator} + max_threshold: ${model_data.slide_max_threshold} + mean_pool_max_threshold: ${model_data.slide_mpm_threshold} + top_k_threshold: ${model_data.slide_top_k_threshold} diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/base.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/base.yaml new file mode 100644 index 00000000..2dfca5b9 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/vgg16/base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 1 + extent_tile: 512 + stride_tile: 256 +top_k_aggregator: + kernel_size: 1 + extent_tile: 512 + stride_tile: 256 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/base_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/base_224.yaml new file mode 100644 index 00000000..38fff8c1 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/base_224.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/vgg16/base_224 + - _self_ + +mean_pool_max_aggregator: + kernel_size: 7 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 5 + extent_tile: 224 + stride_tile: 112 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/prostate_cancer/callbacks/__init__.py b/prostate_cancer/callbacks/__init__.py index b2b3d3e8..60089480 100644 --- a/prostate_cancer/callbacks/__init__.py +++ b/prostate_cancer/callbacks/__init__.py @@ -9,6 +9,9 @@ ) from prostate_cancer.callbacks.heatmap_callback import HeatmapCallback from prostate_cancer.callbacks.mil_prediction_callback import MILPredictionCallback +from prostate_cancer.callbacks.multi_aggregator_eval_callback import ( + MultiAggregatorEvalCallback, +) from prostate_cancer.callbacks.nested_metrics_callback import NestedMetricsCallback from prostate_cancer.callbacks.num_positive_callback import NumPositiveCallback from prostate_cancer.callbacks.tile_histograms_callback import TileHistogramsCallback @@ -22,6 +25,7 @@ "EstimationCallback", "HeatmapCallback", "MILPredictionCallback", + "MultiAggregatorEvalCallback", "NestedMetricsCallback", "NumPositiveCallback", "TileHistogramsCallback", diff --git a/prostate_cancer/callbacks/multi_aggregator_eval_callback.py b/prostate_cancer/callbacks/multi_aggregator_eval_callback.py new file mode 100644 index 00000000..47ae2dcf --- /dev/null +++ b/prostate_cancer/callbacks/multi_aggregator_eval_callback.py @@ -0,0 +1,190 @@ +from copy import deepcopy +from pathlib import Path +from typing import TYPE_CHECKING, Any, cast + +import lightning.pytorch as pl +import mlflow +import pandas as pd +import torch +from rationai.mlkit.lightning.callbacks import MultiloaderLifecycle +from rationai.mlkit.metrics.aggregators import Aggregator +from torchmetrics import ( + AUROC, + Accuracy, + NegativePredictiveValue, + Precision, + Recall, + Specificity, +) + +from prostate_cancer.typing import TilingSlideMetadata, UnlabeledTileSampleBatch + + +if TYPE_CHECKING: + from prostate_cancer.datamodule import TileDataModule + + +class MultiAggregatorEvalCallback(MultiloaderLifecycle): + """Aggregates TL predictions into SL predictions with the max, mean_pool_max + and top_k aggregators in parallel, evaluates each against the SL target and + a majority vote across the three, and logs SL metrics to MLflow. + """ + + def __init__( + self, + max_aggregator: Aggregator, + mean_pool_max_aggregator: Aggregator, + top_k_aggregator: Aggregator, + max_threshold: float, + mean_pool_max_threshold: float, + top_k_threshold: float, + ) -> None: + super().__init__() + self.aggregators_original = { + "max": max_aggregator, + "mean_pool_max": mean_pool_max_aggregator, + "top_k": top_k_aggregator, + } + self.thresholds = { + "max": max_threshold, + "mean_pool_max": mean_pool_max_threshold, + "top_k": top_k_threshold, + } + + def setup( + self, + trainer: pl.Trainer, + pl_module: pl.LightningModule, + stage: str | None = None, + ) -> None: + self.tables: dict[str, dict[str, Any]] = { + name: { + "slide_name": [], + "prediction": [], + "prediction_binary": [], + "target": [], + } + for name in self.aggregators_original + } + self.majority_table: dict[str, Any] = { + "slide_name": [], + "prediction": [], + "target": [], + } + + def on_predict_dataloader_start( + self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int + ) -> None: + if not hasattr(trainer, "datamodule"): + raise ValueError("Trainer should have datamodule attribute") + # aggregators cannot be reset, thus their original state is copied for each slide + self.aggregators = { + name: deepcopy(aggregator) + for name, aggregator in self.aggregators_original.items() + } + datamodule = cast("TileDataModule", trainer.datamodule) + self.slide = cast( + "TilingSlideMetadata", datamodule.predict.slides[dataloader_idx] + ) + + def on_predict_batch_end( + self, + trainer: pl.Trainer, + pl_module: pl.LightningModule, + outputs: torch.Tensor, + batch: UnlabeledTileSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + _, metadata = batch + + targets = torch.zeros_like(outputs) + for aggregator in self.aggregators.values(): + aggregator.update( + preds=outputs, + targets=targets, + x=metadata["x"], + y=metadata["y"], + ) + + def on_predict_dataloader_end( + self, trainer: pl.Trainer, pl_module: pl.LightningModule, dataloader_idx: int + ) -> None: + slide_name = Path(self.slide["path"]).stem + target = self.slide.get("carcinoma", None) + + votes = [] + for name, aggregator in self.aggregators.items(): + pred, _ = aggregator.compute() + pred_binary = bool(pred.item() >= self.thresholds[name]) + votes.append(pred_binary) + + table = self.tables[name] + table["slide_name"].append(slide_name) + table["prediction"].append(pred.item()) + table["prediction_binary"].append(pred_binary) + table["target"].append(target) + + self.majority_table["slide_name"].append(slide_name) + self.majority_table["prediction"].append(sum(votes) >= 2) + self.majority_table["target"].append(target) + + def on_predict_epoch_end( + self, trainer: pl.Trainer, pl_module: pl.LightningModule + ) -> None: + super().on_predict_epoch_end(trainer, pl_module) + + metrics: dict[str, float] = {} + + for name, table in self.tables.items(): + df = pd.DataFrame(table) + filename = f"sl_predictions_{name}.json" + df.to_json(filename, orient="split") + mlflow.log_artifact(filename, artifact_path="tables") + metrics.update( + self._compute_metrics( + name, table["target"], table["prediction_binary"], table["prediction"] + ) + ) + + majority_df = pd.DataFrame(self.majority_table) + majority_df.to_json("sl_predictions_majority_vote.json", orient="split") + mlflow.log_artifact("sl_predictions_majority_vote.json", artifact_path="tables") + metrics.update( + self._compute_metrics( + "majority_vote", + self.majority_table["target"], + self.majority_table["prediction"], + score=None, + ) + ) + + mlflow.log_metrics(metrics) + + @staticmethod + def _compute_metrics( + name: str, + target: list[Any], + prediction_binary: list[bool], + score: list[float] | None = None, + ) -> dict[str, float]: + target_t = torch.tensor(target, dtype=torch.long) + pred_binary_t = torch.tensor(prediction_binary, dtype=torch.long) + + binary_metrics = { + "accuracy": Accuracy("binary"), + "precision": Precision("binary"), + "recall": Recall("binary"), + "specificity": Specificity("binary"), + "negative_predictive_value": NegativePredictiveValue("binary"), + } + results = { + f"sl/{name}/{metric_name}": metric(pred_binary_t, target_t).item() + for metric_name, metric in binary_metrics.items() + } + + if score is not None: + score_t = torch.tensor(score, dtype=torch.float) + results[f"sl/{name}/AUC"] = AUROC("binary")(score_t, target_t).item() + + return results From a433acb8af4d8bfb5427b944c5e260bfa78eb857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 20:16:36 +0000 Subject: [PATCH 243/284] feat: multi aggregate sl eval --- .../pgp/multi_aggregator_eval/base.yaml | 21 +++++++++++++++++++ .../pgp/multi_aggregator_eval/stain_base.yaml | 21 +++++++++++++++++++ .../embeddings/pgp/stain_mpm_estimation.yaml | 18 ---------------- .../embeddings/pgp/stain_span_estimation.yaml | 19 ----------------- .../pgp/stain_top_k_estimation.yaml | 19 ----------------- .../virchow2/multi_aggregator_eval/base.yaml | 21 +++++++++++++++++++ .../multi_aggregator_eval/stain_base.yaml | 21 +++++++++++++++++++ .../virchow2/stain_mpm_estimation.yaml | 19 ----------------- .../virchow2/stain_span_estimation.yaml | 19 ----------------- .../virchow2/stain_top_k_estimation.yaml | 19 ----------------- .../{ => estimations}/mpm_estimation.yaml | 0 .../{ => estimations}/span_estimation.yaml | 0 .../stain_mpm_estimation.yaml | 0 .../stain_span_estimation.yaml | 0 .../stain_top_k_estimation.yaml | 0 .../{ => estimations}/top_k_estimation.yaml | 0 .../multi_aggregator_eval/base.yaml | 21 +++++++++++++++++++ .../multi_aggregator_eval/stain_base.yaml | 21 +++++++++++++++++++ .../resnet50/multi_aggregator_eval/base.yaml | 21 +++++++++++++++++++ .../multi_aggregator_eval/base_224.yaml | 21 +++++++++++++++++++ .../multi_aggregator_eval/stain_base.yaml | 21 +++++++++++++++++++ .../multi_aggregator_eval/stain_base_224.yaml | 21 +++++++++++++++++++ .../multi_aggregator_eval/stain_base.yaml | 21 +++++++++++++++++++ .../multi_aggregator_eval/stain_base_224.yaml | 21 +++++++++++++++++++ 24 files changed, 252 insertions(+), 113 deletions(-) create mode 100644 configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/base.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_base.yaml delete mode 100644 configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml delete mode 100644 configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml delete mode 100644 configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/base.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_base.yaml delete mode 100644 configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml delete mode 100644 configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml delete mode 100644 configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml rename configs/experiment/predict/images/google_vit/{ => estimations}/mpm_estimation.yaml (100%) rename configs/experiment/predict/images/google_vit/{ => estimations}/span_estimation.yaml (100%) rename configs/experiment/predict/images/google_vit/{ => estimations}/stain_mpm_estimation.yaml (100%) rename configs/experiment/predict/images/google_vit/{ => estimations}/stain_span_estimation.yaml (100%) rename configs/experiment/predict/images/google_vit/{ => estimations}/stain_top_k_estimation.yaml (100%) rename configs/experiment/predict/images/google_vit/{ => estimations}/top_k_estimation.yaml (100%) create mode 100644 configs/experiment/predict/images/google_vit/multi_aggregator_eval/base.yaml create mode 100644 configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_base.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/base.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/base_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/base.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/base.yaml new file mode 100644 index 00000000..20d624e1 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/embeddings/pgp/base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 1 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 1 + extent_tile: 224 + stride_tile: 112 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_base.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_base.yaml new file mode 100644 index 00000000..82a24fa6 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/embeddings/pgp/stain_base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 3 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 2 + extent_tile: 224 + stride_tile: 112 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml b/configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml deleted file mode 100644 index 0d70a8c8..00000000 --- a/configs/experiment/predict/embeddings/pgp/stain_mpm_estimation.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/embeddings/pgp/stain_base - - /data/mmci/sl_group_1@predict_data - - _self_ - -callbacks: - estimation_callback: - aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator - static: - extent_tile: 224 - stride_tile: 112 - to_estimate: - kernel_size: [1, 2, 3, 4, 5, 6, 7] - -use_case: "Kernel Size Estimation (MPM Aggregator)" diff --git a/configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml b/configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml deleted file mode 100644 index 781fbfa7..00000000 --- a/configs/experiment/predict/embeddings/pgp/stain_span_estimation.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/embeddings/pgp/stain_base - - /data/mmci/sl_group_1@predict_data - - _self_ - -callbacks: - estimation_callback: - aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator - static: - extent_tile: 224 - stride_tile: 112 - cell_threshold: ${model_data.tile_threshold} - to_estimate: - k: [2, 3, 4, 5] - -use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml b/configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml deleted file mode 100644 index ecc34900..00000000 --- a/configs/experiment/predict/embeddings/pgp/stain_top_k_estimation.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/embeddings/pgp/stain_base - - /data/mmci/sl_group_1@predict_data - - _self_ - -callbacks: - estimation_callback: - aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator - static: - extent_tile: 224 - stride_tile: 112 - to_estimate: - kernel_size: [1, 2, 3, 4, 5] - k: [2, 3, 4] - -use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/base.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/base.yaml new file mode 100644 index 00000000..fa2e2f87 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/embeddings/virchow2/base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 2 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 2 + extent_tile: 224 + stride_tile: 112 + k: 3 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_base.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_base.yaml new file mode 100644 index 00000000..cccc68b0 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/embeddings/virchow2/stain_base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 2 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 2 + extent_tile: 224 + stride_tile: 112 + k: 2 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml deleted file mode 100644 index 13ffc0b6..00000000 --- a/configs/experiment/predict/embeddings/virchow2/stain_mpm_estimation.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/embeddings/virchow2/stain_base - - /data/mmci/sl_group_1@predict_data - - _self_ - -callbacks: - estimation_callback: - aggregator_cls_path: rationai.mlkit.metrics.MeanPoolMaxAggregator - - static: - extent_tile: 224 - stride_tile: 112 - to_estimate: - kernel_size: [1, 2, 3, 4, 5, 6, 7] - -use_case: "Kernel Size Estimation (MPM Aggregator)" diff --git a/configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml deleted file mode 100644 index 1cd269bc..00000000 --- a/configs/experiment/predict/embeddings/virchow2/stain_span_estimation.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/embeddings/virchow2/stain_base - - /data/mmci/sl_group_1@predict_data - - _self_ - -callbacks: - estimation_callback: - aggregator_cls_path: prostate_cancer.aggregators.span.SpanAggregator - static: - extent_tile: 224 - stride_tile: 112 - cell_threshold: ${model_data.tile_threshold} - to_estimate: - k: [2, 3, 4, 5] - -use_case: "K Estimation (Span Aggregator)" diff --git a/configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml b/configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml deleted file mode 100644 index e7ee4722..00000000 --- a/configs/experiment/predict/embeddings/virchow2/stain_top_k_estimation.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# @package _global_ - -defaults: - - /callbacks/aggregator_estimation@callbacks - - /experiment/predict/embeddings/virchow2/stain_base - - /data/mmci/sl_group_1@predict_data - - _self_ - -callbacks: - estimation_callback: - aggregator_cls_path: rationai.mlkit.metrics.TopKAggregator - static: - extent_tile: 224 - stride_tile: 112 - to_estimate: - kernel_size: [1, 2, 3, 4, 5] - k: [2, 3, 4] - -use_case: "Kernel Size & K Estimation (Top K Aggregator)" diff --git a/configs/experiment/predict/images/google_vit/mpm_estimation.yaml b/configs/experiment/predict/images/google_vit/estimations/mpm_estimation.yaml similarity index 100% rename from configs/experiment/predict/images/google_vit/mpm_estimation.yaml rename to configs/experiment/predict/images/google_vit/estimations/mpm_estimation.yaml diff --git a/configs/experiment/predict/images/google_vit/span_estimation.yaml b/configs/experiment/predict/images/google_vit/estimations/span_estimation.yaml similarity index 100% rename from configs/experiment/predict/images/google_vit/span_estimation.yaml rename to configs/experiment/predict/images/google_vit/estimations/span_estimation.yaml diff --git a/configs/experiment/predict/images/google_vit/stain_mpm_estimation.yaml b/configs/experiment/predict/images/google_vit/estimations/stain_mpm_estimation.yaml similarity index 100% rename from configs/experiment/predict/images/google_vit/stain_mpm_estimation.yaml rename to configs/experiment/predict/images/google_vit/estimations/stain_mpm_estimation.yaml diff --git a/configs/experiment/predict/images/google_vit/stain_span_estimation.yaml b/configs/experiment/predict/images/google_vit/estimations/stain_span_estimation.yaml similarity index 100% rename from configs/experiment/predict/images/google_vit/stain_span_estimation.yaml rename to configs/experiment/predict/images/google_vit/estimations/stain_span_estimation.yaml diff --git a/configs/experiment/predict/images/google_vit/stain_top_k_estimation.yaml b/configs/experiment/predict/images/google_vit/estimations/stain_top_k_estimation.yaml similarity index 100% rename from configs/experiment/predict/images/google_vit/stain_top_k_estimation.yaml rename to configs/experiment/predict/images/google_vit/estimations/stain_top_k_estimation.yaml diff --git a/configs/experiment/predict/images/google_vit/top_k_estimation.yaml b/configs/experiment/predict/images/google_vit/estimations/top_k_estimation.yaml similarity index 100% rename from configs/experiment/predict/images/google_vit/top_k_estimation.yaml rename to configs/experiment/predict/images/google_vit/estimations/top_k_estimation.yaml diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/base.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/base.yaml new file mode 100644 index 00000000..22d71d31 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/google_vit/base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 1 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 1 + extent_tile: 224 + stride_tile: 112 + k: 2 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_base.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_base.yaml new file mode 100644 index 00000000..b2efbf1c --- /dev/null +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/google_vit/stain_base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 6 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 2 + extent_tile: 224 + stride_tile: 112 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/base.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/base.yaml new file mode 100644 index 00000000..7a068823 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/resnet50/base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 5 + extent_tile: 512 + stride_tile: 256 +top_k_aggregator: + kernel_size: 4 + extent_tile: 512 + stride_tile: 256 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/base_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/base_224.yaml new file mode 100644 index 00000000..3080b5f9 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/base_224.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/resnet50/base_224 + - _self_ + +mean_pool_max_aggregator: + kernel_size: 6 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 5 + extent_tile: 224 + stride_tile: 112 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base.yaml new file mode 100644 index 00000000..1ff1771b --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/resnet50/stain_base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 3 + extent_tile: 512 + stride_tile: 256 +top_k_aggregator: + kernel_size: 1 + extent_tile: 512 + stride_tile: 256 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base_224.yaml new file mode 100644 index 00000000..2b287e9c --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_base_224.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/resnet50/stain_base_224 + - _self_ + +mean_pool_max_aggregator: + kernel_size: 1 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 1 + extent_tile: 224 + stride_tile: 112 + k: 2 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml new file mode 100644 index 00000000..74f6b51a --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/vgg16/stain_base + - _self_ + +mean_pool_max_aggregator: + kernel_size: 1 + extent_tile: 512 + stride_tile: 256 +top_k_aggregator: + kernel_size: 1 + extent_tile: 512 + stride_tile: 256 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml new file mode 100644 index 00000000..1794ab68 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml @@ -0,0 +1,21 @@ +# @package _global_ + +defaults: + - /callbacks/multi_aggregator_eval@callbacks + - /aggregator/max@max_aggregator + - /aggregator/mean_pool_max@mean_pool_max_aggregator + - /aggregator/top_k@top_k_aggregator + - /experiment/predict/images/vgg16/stain_base_224 + - _self_ + +mean_pool_max_aggregator: + kernel_size: 7 + extent_tile: 224 + stride_tile: 112 +top_k_aggregator: + kernel_size: 5 + extent_tile: 224 + stride_tile: 112 + k: 4 + +use_case: "Multi-Aggregator SL Evaluation" From 603b7e1baeeeb20f6fb13371bffc554d3149b111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sun, 19 Jul 2026 20:28:23 +0000 Subject: [PATCH 244/284] feat: add test set --- .../embeddings/pgp/multi_aggregator_eval/mmci_tl_test.yaml | 6 ++++++ .../pgp/multi_aggregator_eval/stain_mmci_tl_test.yaml | 6 ++++++ .../virchow2/multi_aggregator_eval/mmci_tl_test.yaml | 6 ++++++ .../virchow2/multi_aggregator_eval/stain_mmci_tl_test.yaml | 6 ++++++ .../google_vit/multi_aggregator_eval/mmci_tl_test.yaml | 6 ++++++ .../multi_aggregator_eval/stain_mmci_tl_test.yaml | 6 ++++++ .../images/resnet50/multi_aggregator_eval/mmci_tl_test.yaml | 6 ++++++ .../resnet50/multi_aggregator_eval/mmci_tl_test_224.yaml | 6 ++++++ .../resnet50/multi_aggregator_eval/stain_mmci_tl_test.yaml | 6 ++++++ .../multi_aggregator_eval/stain_mmci_tl_test_224.yaml | 6 ++++++ .../images/vgg16/multi_aggregator_eval/mmci_tl_test.yaml | 6 ++++++ .../vgg16/multi_aggregator_eval/mmci_tl_test_224.yaml | 6 ++++++ .../vgg16/multi_aggregator_eval/stain_mmci_tl_test.yaml | 6 ++++++ .../vgg16/multi_aggregator_eval/stain_mmci_tl_test_224.yaml | 6 ++++++ 14 files changed, 84 insertions(+) create mode 100644 configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/mmci_tl_test.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_mmci_tl_test.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/mmci_tl_test.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_mmci_tl_test.yaml create mode 100644 configs/experiment/predict/images/google_vit/multi_aggregator_eval/mmci_tl_test.yaml create mode 100644 configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_mmci_tl_test.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test_224.yaml diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/mmci_tl_test.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/mmci_tl_test.yaml new file mode 100644 index 00000000..6b3172c5 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/multi_aggregator_eval/base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_mmci_tl_test.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..2c9cc2e2 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/mmci_tl_test.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/mmci_tl_test.yaml new file mode 100644 index 00000000..adf49986 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/virchow2/multi_aggregator_eval/base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_mmci_tl_test.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..4856694a --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/mmci_tl_test.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/mmci_tl_test.yaml new file mode 100644 index 00000000..7b9e66d0 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/google_vit/multi_aggregator_eval/base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_mmci_tl_test.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..8b0c1cc2 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/google_vit/multi_aggregator_eval/stain_base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test.yaml new file mode 100644 index 00000000..10b5f0dc --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test_224.yaml new file mode 100644 index 00000000..764577b3 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/mmci_tl_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/base_224 + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..8f78a41e --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/stain_base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test_224.yaml new file mode 100644 index 00000000..83cfd6b6 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_mmci_tl_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/stain_base_224 + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test.yaml new file mode 100644 index 00000000..0e585312 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test_224.yaml new file mode 100644 index 00000000..33168aef --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/mmci_tl_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/base_224 + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test.yaml new file mode 100644 index 00000000..f4c042ec --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/stain_base + - /data/mmci/tl_test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test_224.yaml new file mode 100644 index 00000000..ac6bfa41 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_mmci_tl_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224 + - /data/mmci/tl_test@predict_data + - _self_ From 4e155f5e8ca4478f9c17b7c1c3c5fc7d61f10ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 08:13:19 +0000 Subject: [PATCH 245/284] feat: fix hyperparams --- .../images/vgg16/multi_aggregator_eval/stain_base_224.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml index 1794ab68..89b12fe6 100644 --- a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224.yaml @@ -9,11 +9,11 @@ defaults: - _self_ mean_pool_max_aggregator: - kernel_size: 7 + kernel_size: 1 extent_tile: 224 stride_tile: 112 top_k_aggregator: - kernel_size: 5 + kernel_size: 1 extent_tile: 224 stride_tile: 112 k: 4 From 4c31b5a5773c26eec8c8b65e11649d0d1e64d454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 08:18:17 +0000 Subject: [PATCH 246/284] feat: fix typos --- .../predict/images/vgg16/multi_aggregator_eval/stain_base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml index 74f6b51a..828285c8 100644 --- a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_base.yaml @@ -16,6 +16,6 @@ top_k_aggregator: kernel_size: 1 extent_tile: 512 stride_tile: 256 - k: 4 + k: 2 use_case: "Multi-Aggregator SL Evaluation" From 12f90ef4cd7660415f045c5c5bc76da670bb6fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 10:55:06 +0000 Subject: [PATCH 247/284] fix: typo --- configs/experiment/predict/bag_of_embeddings/pgp/base.yaml | 4 ++-- .../experiment/predict/bag_of_embeddings/virchow2/base.yaml | 4 ++-- configs/experiment/test/bag_of_embeddings/pgp/base.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml index 318d8b41..773f83c1 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml @@ -2,8 +2,8 @@ defaults: - /experiment/predict/bag_of_embeddings/base - - /model/pgp_mil_model@model - - /model/checkpoints/pgp_mil@model_data + - /model/pgp_hybrid_mil_model@model + - /model/checkpoints/pgp_hybrid_mil@model_data - _self_ datamodule: diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml index 0748a564..f2a59802 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml @@ -2,8 +2,8 @@ defaults: - /experiment/predict/bag_of_embeddings/base - - /model/virchow2_mil_model@model - - /model/checkpoints/mmci_tl/virchow2_mil@model_data + - /model/virchow2_hybrid_mil_model@model + - /model/checkpoints/mmci_tl/virchow2_hybird_mil@model_data - _self_ datamodule: diff --git a/configs/experiment/test/bag_of_embeddings/pgp/base.yaml b/configs/experiment/test/bag_of_embeddings/pgp/base.yaml index 99151a94..25db044e 100644 --- a/configs/experiment/test/bag_of_embeddings/pgp/base.yaml +++ b/configs/experiment/test/bag_of_embeddings/pgp/base.yaml @@ -2,7 +2,7 @@ defaults: - /experiment/test/bag_of_embeddings/base - - /model/pgp_hybird_mil_model@model + - /model/pgp_hybrid_mil_model@model - /model/checkpoints/mmci_tl/pgp_hybrid_mil@model_data - _self_ From 9742cabcabe929a08038dce4ef481bb8b9099c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 12:23:10 +0000 Subject: [PATCH 248/284] fix: data path --- .../bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml index 353cf3c1..c0c2aeba 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml @@ -2,5 +2,5 @@ defaults: - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/base - - /data/mmci_tl_val@predict_data + - /data/mmci/tl_val@predict_data - _self_ From 3b049842ad0e7f89b04b6b2a5c7aad3a954cad17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 12:56:14 +0000 Subject: [PATCH 249/284] fix: typo --- configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml index f2a59802..cff0ca13 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml @@ -3,7 +3,7 @@ defaults: - /experiment/predict/bag_of_embeddings/base - /model/virchow2_hybrid_mil_model@model - - /model/checkpoints/mmci_tl/virchow2_hybird_mil@model_data + - /model/checkpoints/mmci_tl/virchow2_hybrid_mil@model_data - _self_ datamodule: From e80e146d08191dff265db577b2ea5ec24cd68845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 13:04:32 +0000 Subject: [PATCH 250/284] fix: checkpoint --- configs/experiment/predict/bag_of_embeddings/pgp/base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml index 773f83c1..78f975da 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml @@ -3,7 +3,7 @@ defaults: - /experiment/predict/bag_of_embeddings/base - /model/pgp_hybrid_mil_model@model - - /model/checkpoints/pgp_hybrid_mil@model_data + - /model/checkpoints/mmci_tl/pgp_hybrid_mil@model_data - _self_ datamodule: From 2ef77053aa22f96393e02d9d81930fe8f5dbaf01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 13:07:57 +0000 Subject: [PATCH 251/284] fix: callbkcs --- .../predict/bag_of_embeddings/pgp/mil_predictions/base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml index 35419b5f..3d239b1d 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /callbacks/mil_predictions@callbacks + - /callbacks/mil_prediction_callback@callbacks - /experiment/predict/bag_of_embeddings/pgp/base - _self_ From d4b91bcc0fe4208957b1f43ef339bf51f06eff43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 20 Jul 2026 15:22:02 +0000 Subject: [PATCH 252/284] feat: SL Threshold --- configs/callbacks/curves_sl.yaml | 4 ++ configs/callbacks/slide_histograms_mil.yaml | 2 + configs/callbacks/slide_threshold_mil.yaml | 7 +++ .../pgp/slide_threshold.yaml | 10 ++++ .../virchow2/slide_threshold.yaml | 10 ++++ prostate_cancer/callbacks/__init__.py | 7 ++- .../callbacks/curves_callback_base.py | 24 ++++------ .../callbacks/curves_callback_sl.py | 42 +++++++++++++++++ .../multi_aggregator_eval_callback.py | 10 ++-- .../slide_histograms_callback_mil.py | 46 +++++++++++++++++++ .../tile_histograms_callback_base.py | 24 ++++------ 11 files changed, 151 insertions(+), 35 deletions(-) create mode 100644 configs/callbacks/curves_sl.yaml create mode 100644 configs/callbacks/slide_histograms_mil.yaml create mode 100644 configs/callbacks/slide_threshold_mil.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/pgp/slide_threshold.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/virchow2/slide_threshold.yaml create mode 100644 prostate_cancer/callbacks/curves_callback_sl.py create mode 100644 prostate_cancer/callbacks/slide_histograms_callback_mil.py diff --git a/configs/callbacks/curves_sl.yaml b/configs/callbacks/curves_sl.yaml new file mode 100644 index 00000000..4604407f --- /dev/null +++ b/configs/callbacks/curves_sl.yaml @@ -0,0 +1,4 @@ +curves_callback: + _target_: prostate_cancer.callbacks.CurvesCallbackSL + threshold: ${slide_threshold} + optimal_seek: false diff --git a/configs/callbacks/slide_histograms_mil.yaml b/configs/callbacks/slide_histograms_mil.yaml new file mode 100644 index 00000000..6730cd43 --- /dev/null +++ b/configs/callbacks/slide_histograms_mil.yaml @@ -0,0 +1,2 @@ +slide_histograms_callback: + _target_: prostate_cancer.callbacks.SlideHistogramsCallbackMIL diff --git a/configs/callbacks/slide_threshold_mil.yaml b/configs/callbacks/slide_threshold_mil.yaml new file mode 100644 index 00000000..f442cf3b --- /dev/null +++ b/configs/callbacks/slide_threshold_mil.yaml @@ -0,0 +1,7 @@ +defaults: + - slide_histograms_mil + - curves_sl + - _self_ + +curves_callback: + optimal_seek: true diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/slide_threshold.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/slide_threshold.yaml new file mode 100644 index 00000000..11f1d550 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/pgp/slide_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/slide_threshold_mil@callbacks + - /experiment/predict/bag_of_embeddings/pgp/base + - /data/mmci/sl_group_1@predict_data + - _self_ + +slide_threshold: 0.99 # My estimate for other models +use_case: "SL Threshold Estimation" diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/slide_threshold.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/slide_threshold.yaml new file mode 100644 index 00000000..dbcd62b5 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/slide_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/slide_threshold_mil@callbacks + - /experiment/predict/bag_of_embeddings/virchow2/base + - /data/mmci/sl_group_1@predict_data + - _self_ + +slide_threshold: 0.99 # My estimate for other models +use_case: "SL Threshold Estimation" diff --git a/prostate_cancer/callbacks/__init__.py b/prostate_cancer/callbacks/__init__.py index 7b14d04d..706df620 100644 --- a/prostate_cancer/callbacks/__init__.py +++ b/prostate_cancer/callbacks/__init__.py @@ -4,6 +4,7 @@ CarcinomaPredictionTableCallback, ) from prostate_cancer.callbacks.curves_callback_mil import CurvesCallbackMIL +from prostate_cancer.callbacks.curves_callback_sl import CurvesCallbackSL from prostate_cancer.callbacks.curves_callback_tl import CurvesCallbackTile from prostate_cancer.callbacks.estimation_callback import ( EstimationCallback, @@ -15,6 +16,9 @@ ) from prostate_cancer.callbacks.nested_metrics_callback import NestedMetricsCallback from prostate_cancer.callbacks.num_positive_callback import NumPositiveCallback +from prostate_cancer.callbacks.slide_histograms_callback_mil import ( + SlideHistogramsCallbackMIL, +) from prostate_cancer.callbacks.tile_histograms_callback_mil import ( TileHistogramsCallbackMIL, ) @@ -28,6 +32,7 @@ "CAMExplainer", "CarcinomaPredictionTableCallback", "CurvesCallbackMIL", + "CurvesCallbackSL", "CurvesCallbackTile", "EstimationCallback", "HeatmapCallback", @@ -35,7 +40,7 @@ "MultiAggregatorEvalCallback", "NestedMetricsCallback", "NumPositiveCallback", - "TileHistogramsCallback", + "SlideHistogramsCallbackMIL", "TileHistogramsCallbackMIL", "TileHistogramsCallbackTile", ] diff --git a/prostate_cancer/callbacks/curves_callback_base.py b/prostate_cancer/callbacks/curves_callback_base.py index 8ff09761..e4077e2e 100644 --- a/prostate_cancer/callbacks/curves_callback_base.py +++ b/prostate_cancer/callbacks/curves_callback_base.py @@ -1,5 +1,4 @@ -from abc import ABC, abstractmethod -from typing import Any +from abc import ABC import mlflow import numpy as np @@ -25,17 +24,6 @@ def __init__(self, threshold: float, optimal_seek: bool = True) -> None: self.preds: list[torch.Tensor] = [] self.targets: list[torch.Tensor] = [] - @abstractmethod - def on_test_batch_end( - self, - trainer: Trainer, - pl_module: LightningModule, - outputs: Any, - batch: Any, - batch_idx: int, - dataloader_idx: int = 0, - ) -> None: ... - def _plot_roc( self, y_pred: NDArray[np.float32], y_true: NDArray[np.float32] ) -> None: @@ -118,7 +106,7 @@ def _plot_precision_recall( ) mlflow.log_artifact(plot_path, artifact_path="plots") - def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + def _plot_and_clear(self) -> None: y_pred = torch.cat(self.preds).numpy() y_true = torch.cat(self.targets).numpy() @@ -127,3 +115,11 @@ def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> Non self.preds.clear() self.targets.clear() + + def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + self._plot_and_clear() + + def on_predict_epoch_end( + self, trainer: Trainer, pl_module: LightningModule + ) -> None: + self._plot_and_clear() diff --git a/prostate_cancer/callbacks/curves_callback_sl.py b/prostate_cancer/callbacks/curves_callback_sl.py new file mode 100644 index 00000000..704adb07 --- /dev/null +++ b/prostate_cancer/callbacks/curves_callback_sl.py @@ -0,0 +1,42 @@ +from typing import TYPE_CHECKING, cast + +import torch +from lightning import LightningModule, Trainer + +from prostate_cancer.callbacks.curves_callback_base import CurvesCallbackBase +from prostate_cancer.typing import MILModelOutput, UnlabeledBagOfTilesSampleBatch + + +if TYPE_CHECKING: + from prostate_cancer.datamodule import BagOfTilesDataModule + + +class CurvesCallbackSL(CurvesCallbackBase): + def setup( + self, trainer: Trainer, pl_module: LightningModule, stage: str | None = None + ) -> None: + if not hasattr(trainer, "datamodule"): + raise ValueError("Trainer should have datamodule attribute") + + datamodule = cast("BagOfTilesDataModule", trainer.datamodule) + slides = datamodule.predict.slides + self._slide_targets = dict(zip(slides["id"], slides["carcinoma"], strict=True)) + + def on_predict_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, + batch: UnlabeledBagOfTilesSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + sl_outputs, _, _, _ = outputs + _, metadata_batch = batch + + targets = torch.tensor( + [float(self._slide_targets[m["slide_id"]]) for m in metadata_batch] + ) + + self.preds.append(sl_outputs.detach().cpu()) + self.targets.append(targets) diff --git a/prostate_cancer/callbacks/multi_aggregator_eval_callback.py b/prostate_cancer/callbacks/multi_aggregator_eval_callback.py index 47ae2dcf..4596caa7 100644 --- a/prostate_cancer/callbacks/multi_aggregator_eval_callback.py +++ b/prostate_cancer/callbacks/multi_aggregator_eval_callback.py @@ -25,11 +25,6 @@ class MultiAggregatorEvalCallback(MultiloaderLifecycle): - """Aggregates TL predictions into SL predictions with the max, mean_pool_max - and top_k aggregators in parallel, evaluates each against the SL target and - a majority vote across the three, and logs SL metrics to MLflow. - """ - def __init__( self, max_aggregator: Aggregator, @@ -143,7 +138,10 @@ def on_predict_epoch_end( mlflow.log_artifact(filename, artifact_path="tables") metrics.update( self._compute_metrics( - name, table["target"], table["prediction_binary"], table["prediction"] + name, + table["target"], + table["prediction_binary"], + table["prediction"], ) ) diff --git a/prostate_cancer/callbacks/slide_histograms_callback_mil.py b/prostate_cancer/callbacks/slide_histograms_callback_mil.py new file mode 100644 index 00000000..7f48386d --- /dev/null +++ b/prostate_cancer/callbacks/slide_histograms_callback_mil.py @@ -0,0 +1,46 @@ +from typing import TYPE_CHECKING, cast + +import numpy as np +from lightning import LightningModule, Trainer + +from prostate_cancer.callbacks.tile_histograms_callback_base import ( + TileHistogramsCallbackBase, +) +from prostate_cancer.typing import MILModelOutput, UnlabeledBagOfTilesSampleBatch + + +if TYPE_CHECKING: + from prostate_cancer.datamodule import BagOfTilesDataModule + + +class SlideHistogramsCallbackMIL(TileHistogramsCallbackBase): + def setup( + self, trainer: Trainer, pl_module: LightningModule, stage: str | None = None + ) -> None: + if not hasattr(trainer, "datamodule"): + raise ValueError("Trainer should have datamodule attribute") + + datamodule = cast("BagOfTilesDataModule", trainer.datamodule) + slides = datamodule.predict.slides + self._slide_targets = dict(zip(slides["id"], slides["carcinoma"], strict=True)) + + def on_predict_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, + batch: UnlabeledBagOfTilesSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + sl_outputs, _, _, _ = outputs + _, metadata_batch = batch + + preds = sl_outputs.detach().cpu().numpy().flatten() + labels = np.array( + [float(self._slide_targets[m["slide_id"]]) for m in metadata_batch], + dtype=np.float32, + ) + + self.all_preds.append(preds) + self.all_labels.append(labels) diff --git a/prostate_cancer/callbacks/tile_histograms_callback_base.py b/prostate_cancer/callbacks/tile_histograms_callback_base.py index 3db47fe9..a2afa365 100644 --- a/prostate_cancer/callbacks/tile_histograms_callback_base.py +++ b/prostate_cancer/callbacks/tile_histograms_callback_base.py @@ -1,6 +1,5 @@ -from abc import ABC, abstractmethod +from abc import ABC from pathlib import Path -from typing import Any import matplotlib.pyplot as plt import mlflow @@ -17,18 +16,7 @@ def __init__(self) -> None: self.all_preds: list[NDArray[np.floating]] = [] self.all_labels: list[NDArray[np.floating]] = [] - @abstractmethod - def on_test_batch_end( - self, - trainer: Trainer, - pl_module: LightningModule, - outputs: Any, - batch: Any, - batch_idx: int, - dataloader_idx: int = 0, - ) -> None: ... - - def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + def _plot_and_clear(self, trainer: Trainer) -> None: assert isinstance(trainer.logger, MLFlowLogger) preds = np.concatenate(self.all_preds) @@ -62,3 +50,11 @@ def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> Non self.all_preds.clear() self.all_labels.clear() + + def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + self._plot_and_clear(trainer) + + def on_predict_epoch_end( + self, trainer: Trainer, pl_module: LightningModule + ) -> None: + self._plot_and_clear(trainer) From a49a4e8e270ebbade1d99149f7cb06aaf087bdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 21 Jul 2026 08:00:38 +0000 Subject: [PATCH 253/284] feat: preds --- .../pgp/mil_predictions/mmci_sl_group_1.yaml | 6 ++++++ .../virchow2/mil_predictions/mmci_sl_group_1.yaml | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_sl_group_1.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_sl_group_1.yaml diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_sl_group_1.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_sl_group_1.yaml new file mode 100644 index 00000000..ad7964be --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_sl_group_1.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/base + - /data/mmci/sl_group_1@predict_data + - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_sl_group_1.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_sl_group_1.yaml new file mode 100644 index 00000000..fe9179fb --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_sl_group_1.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base + - /data/mmci/sl_group_1@predict_data + - _self_ From 90d85bbc62d6748b33e0f69e52a52f1611cdaeae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 21 Jul 2026 11:24:15 +0000 Subject: [PATCH 254/284] feat: tl test --- .../callbacks/combined_test_suite_mil.yaml | 6 +++ configs/callbacks/nested_metrics_mil.yaml | 3 ++ .../pgp/test_suite/base.yaml | 8 +++ .../pgp/test_suite/mmci_tl_test.yaml | 6 +++ .../virchow2/test_suite/base.yaml | 8 +++ .../virchow2/test_suite/mmci_tl_test.yaml | 6 +++ prostate_cancer/callbacks/__init__.py | 4 ++ .../callbacks/mil_prediction_callback.py | 51 +++++++++++++++---- .../callbacks/nested_metrics_callback.py | 51 +++---------------- .../callbacks/nested_metrics_callback_base.py | 42 +++++++++++++++ .../callbacks/nested_metrics_callback_mil.py | 34 +++++++++++++ 11 files changed, 164 insertions(+), 55 deletions(-) create mode 100644 configs/callbacks/combined_test_suite_mil.yaml create mode 100644 configs/callbacks/nested_metrics_mil.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/test_suite/base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/test_suite/mmci_tl_test.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/test_suite/base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/test_suite/mmci_tl_test.yaml create mode 100644 prostate_cancer/callbacks/nested_metrics_callback_base.py create mode 100644 prostate_cancer/callbacks/nested_metrics_callback_mil.py diff --git a/configs/callbacks/combined_test_suite_mil.yaml b/configs/callbacks/combined_test_suite_mil.yaml new file mode 100644 index 00000000..4efd9971 --- /dev/null +++ b/configs/callbacks/combined_test_suite_mil.yaml @@ -0,0 +1,6 @@ +defaults: + - curves_mil + - nested_metrics_mil + - tile_histograms_mil + - mil_prediction_callback + - _self_ diff --git a/configs/callbacks/nested_metrics_mil.yaml b/configs/callbacks/nested_metrics_mil.yaml new file mode 100644 index 00000000..a340547c --- /dev/null +++ b/configs/callbacks/nested_metrics_mil.yaml @@ -0,0 +1,3 @@ +nested_metrics_callback: + _target_: prostate_cancer.callbacks.NestedMetricsCallbackMIL + threshold: ${tile_threshold} diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/base.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/base.yaml new file mode 100644 index 00000000..32e8817b --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite_mil@callbacks + - /experiment/test/bag_of_embeddings/pgp/base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/mmci_tl_test.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/mmci_tl_test.yaml new file mode 100644 index 00000000..f5f43efc --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/pgp/test_suite/base + - /data/mmci/tl_test@test_data + - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/base.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/base.yaml new file mode 100644 index 00000000..2bc6d4e4 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite_mil@callbacks + - /experiment/test/bag_of_embeddings/virchow2/base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/mmci_tl_test.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/mmci_tl_test.yaml new file mode 100644 index 00000000..6bb9ae18 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/virchow2/test_suite/base + - /data/mmci/tl_test@test_data + - _self_ diff --git a/prostate_cancer/callbacks/__init__.py b/prostate_cancer/callbacks/__init__.py index 706df620..875e6156 100644 --- a/prostate_cancer/callbacks/__init__.py +++ b/prostate_cancer/callbacks/__init__.py @@ -15,6 +15,9 @@ MultiAggregatorEvalCallback, ) from prostate_cancer.callbacks.nested_metrics_callback import NestedMetricsCallback +from prostate_cancer.callbacks.nested_metrics_callback_mil import ( + NestedMetricsCallbackMIL, +) from prostate_cancer.callbacks.num_positive_callback import NumPositiveCallback from prostate_cancer.callbacks.slide_histograms_callback_mil import ( SlideHistogramsCallbackMIL, @@ -39,6 +42,7 @@ "MILPredictionCallback", "MultiAggregatorEvalCallback", "NestedMetricsCallback", + "NestedMetricsCallbackMIL", "NumPositiveCallback", "SlideHistogramsCallbackMIL", "TileHistogramsCallbackMIL", diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 2a642545..8a3e0b15 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -9,7 +9,11 @@ from rationai.masks.mask_builders import ScalarMaskBuilder from rationai.mlkit.lightning.loggers.mlflow import MLFlowLogger -from prostate_cancer.typing import MILModelOutput, UnlabeledBagOfTilesSampleBatch +from prostate_cancer.typing import ( + LabeledBagOfTilesSampleBatch, + MILModelOutput, + UnlabeledBagOfTilesSampleBatch, +) if TYPE_CHECKING: @@ -28,7 +32,8 @@ def setup( raise ValueError("Trainer should have datamodule attribute") datamodule = cast("BagOfTilesDataModule", trainer.datamodule) - slides = cast("HFDataset", datamodule.predict.slides) + dataset = datamodule.test if stage == "test" else datamodule.predict + slides = cast("HFDataset", dataset.slides) self._slide_index = { Path(path).stem: i for i, path in enumerate(slides["path"]) @@ -67,20 +72,16 @@ def get_mask_builder( stride=slide["stride_x"], ) - def on_predict_batch_end( + def _on_batch_end( self, trainer: Trainer, - pl_module: LightningModule, outputs: MILModelOutput, - batch: UnlabeledBagOfTilesSampleBatch, - batch_idx: int, - dataloader_idx: int = 0, + batch: UnlabeledBagOfTilesSampleBatch | LabeledBagOfTilesSampleBatch, ) -> None: - assert isinstance(trainer.logger, MLFlowLogger) sl_preds, tl_preds, batch_mask, batch_attention = outputs - _, metadata_batch = batch + metadata_batch = batch[-1] self.table["slide"].extend([m["slide_name"] for m in metadata_batch]) self.table["sl_prediction"].extend(sl_preds.tolist()) @@ -117,12 +118,40 @@ def on_predict_batch_end( artifact_path=str(mask_builder.save_dir), ) - def on_predict_epoch_end( - self, trainer: Trainer, pl_module: LightningModule + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, + batch: LabeledBagOfTilesSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + self._on_batch_end(trainer, outputs, batch) + + def on_predict_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, + batch: UnlabeledBagOfTilesSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, ) -> None: + self._on_batch_end(trainer, outputs, batch) + + def _on_epoch_end(self) -> None: df = pd.DataFrame(self.table) df.to_json("sl_predictions.json", orient="split") mlflow.log_artifact( "sl_predictions.json", artifact_path="tables", ) + + def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + self._on_epoch_end() + + def on_predict_epoch_end( + self, trainer: Trainer, pl_module: LightningModule + ) -> None: + self._on_epoch_end() diff --git a/prostate_cancer/callbacks/nested_metrics_callback.py b/prostate_cancer/callbacks/nested_metrics_callback.py index c17cdf0b..23fea58b 100644 --- a/prostate_cancer/callbacks/nested_metrics_callback.py +++ b/prostate_cancer/callbacks/nested_metrics_callback.py @@ -1,45 +1,18 @@ from typing import Any -import lightning.pytorch as pl -import mlflow -import pandas as pd -from lightning import Callback -from rationai.mlkit.lightning.loggers import MLFlowLogger -from rationai.mlkit.metrics import NestedMetricCollection -from torchmetrics import ( - AUROC, - Accuracy, - NegativePredictiveValue, - Precision, - Recall, - Specificity, -) +from lightning import LightningModule, Trainer +from prostate_cancer.callbacks.nested_metrics_callback_base import ( + NestedMetricsCallbackBase, +) from prostate_cancer.typing import LabeledTileSampleBatch -class NestedMetricsCallback(Callback): - """Calculates metrics using the `NestedMetricCollection` in the test stage.""" - - def __init__(self, threshold: float) -> None: - # In the test mode, log metrics for each slide - self.nested_test_metrics = NestedMetricCollection( - metrics={ - "AUC": AUROC("binary"), - "accuracy": Accuracy("binary", threshold), - "precision": Precision("binary", threshold), - "recall": Recall("binary", threshold), - "specificity": Specificity("binary", threshold), - "negative_predictive_value": NegativePredictiveValue( - "binary", threshold - ), - } - ) - +class NestedMetricsCallback(NestedMetricsCallbackBase): def on_test_batch_end( self, - trainer: pl.Trainer, - pl_module: pl.LightningModule, + trainer: Trainer, + pl_module: LightningModule, outputs: Any, batch: LabeledTileSampleBatch, batch_idx: int, @@ -49,13 +22,3 @@ def on_test_batch_end( # Update slide-level metrics self.nested_test_metrics.update(outputs, targets, metadata["slide"]) - - def on_test_epoch_end( - self, trainer: pl.Trainer, pl_module: pl.LightningModule - ) -> None: - assert isinstance(trainer.logger, MLFlowLogger) - - metrics = self.nested_test_metrics.compute() - pd.DataFrame(metrics).to_json("nested_metrics.json", orient="split") - mlflow.log_artifact("nested_metrics.json") - self.nested_test_metrics.reset() diff --git a/prostate_cancer/callbacks/nested_metrics_callback_base.py b/prostate_cancer/callbacks/nested_metrics_callback_base.py new file mode 100644 index 00000000..635e646d --- /dev/null +++ b/prostate_cancer/callbacks/nested_metrics_callback_base.py @@ -0,0 +1,42 @@ +from abc import ABC + +import mlflow +import pandas as pd +from lightning import Callback, LightningModule, Trainer +from rationai.mlkit.lightning.loggers import MLFlowLogger +from rationai.mlkit.metrics import NestedMetricCollection +from torchmetrics import ( + AUROC, + Accuracy, + NegativePredictiveValue, + Precision, + Recall, + Specificity, +) + + +class NestedMetricsCallbackBase(Callback, ABC): + """Calculates metrics using the `NestedMetricCollection` in the test stage, grouped per slide.""" + + def __init__(self, threshold: float) -> None: + super().__init__() + self.nested_test_metrics = NestedMetricCollection( + metrics={ + "AUC": AUROC("binary"), + "accuracy": Accuracy("binary", threshold), + "precision": Precision("binary", threshold), + "recall": Recall("binary", threshold), + "specificity": Specificity("binary", threshold), + "negative_predictive_value": NegativePredictiveValue( + "binary", threshold + ), + } + ) + + def on_test_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + assert isinstance(trainer.logger, MLFlowLogger) + + metrics = self.nested_test_metrics.compute() + pd.DataFrame(metrics).to_json("nested_metrics.json", orient="split") + mlflow.log_artifact("nested_metrics.json") + self.nested_test_metrics.reset() diff --git a/prostate_cancer/callbacks/nested_metrics_callback_mil.py b/prostate_cancer/callbacks/nested_metrics_callback_mil.py new file mode 100644 index 00000000..8710327e --- /dev/null +++ b/prostate_cancer/callbacks/nested_metrics_callback_mil.py @@ -0,0 +1,34 @@ +from lightning import LightningModule, Trainer + +from prostate_cancer.callbacks.nested_metrics_callback_base import ( + NestedMetricsCallbackBase, +) +from prostate_cancer.typing import LabeledBagOfTilesSampleBatch, MILModelOutput + + +class NestedMetricsCallbackMIL(NestedMetricsCallbackBase): + def on_test_batch_end( + self, + trainer: Trainer, + pl_module: LightningModule, + outputs: MILModelOutput, # type: ignore[override] + batch: LabeledBagOfTilesSampleBatch, + batch_idx: int, + dataloader_idx: int = 0, + ) -> None: + _, tl_targets, _, metadata = batch + _, tl_outputs_raw, mask, _ = outputs + + mask_bool = mask.bool() + tl_outputs_valid = tl_outputs_raw[mask_bool] + targets_valid = tl_targets[mask_bool] + + keys = [ + metadata[i]["slide_name"] + for i in range(mask_bool.shape[0]) + for j in range(mask_bool.shape[1]) + if mask_bool[i, j] + ] + + # Update slide-level metrics + self.nested_test_metrics.update(tl_outputs_valid.cpu(), targets_valid.cpu(), keys) From 8ada2a665609b7b26ca73f82e7f5bc03d3b883a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 21 Jul 2026 15:03:17 +0000 Subject: [PATCH 255/284] feat: new mil --- .../classic_bag_of_embeddings_datamodule.yaml | 12 ++ .../bag_of_embeddings_sl_labeled.yaml | 2 + configs/model/pgp_classic_mil_model.yaml | 4 + configs/model/virchow2_classic_mil_model.yaml | 4 + prostate_cancer/attention_mil_model.py | 138 ++---------------- .../callbacks/mil_prediction_callback.py | 2 +- .../callbacks/nested_metrics_callback_mil.py | 4 +- prostate_cancer/classic_mil_model.py | 49 +++++++ prostate_cancer/datamodule/__init__.py | 5 +- .../datamodule/bag_of_tiles_data_module.py | 57 +++++--- .../classic_bag_of_tiles_data_module.py | 34 +++++ .../datamodule/datasets/__init__.py | 4 + .../datasets/bag_of_embeddings_dataset.py | 117 ++++++++++----- ...iles_stratified_weighted_random_sampler.py | 7 +- prostate_cancer/mil_model_base.py | 126 ++++++++++++++++ prostate_cancer/typing.py | 4 + 16 files changed, 386 insertions(+), 183 deletions(-) create mode 100644 configs/datamodule/classic_bag_of_embeddings_datamodule.yaml create mode 100644 configs/datamodule/datasets/embeddings/bag_of_embeddings_sl_labeled.yaml create mode 100644 configs/model/pgp_classic_mil_model.yaml create mode 100644 configs/model/virchow2_classic_mil_model.yaml create mode 100644 prostate_cancer/classic_mil_model.py create mode 100644 prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py create mode 100644 prostate_cancer/mil_model_base.py diff --git a/configs/datamodule/classic_bag_of_embeddings_datamodule.yaml b/configs/datamodule/classic_bag_of_embeddings_datamodule.yaml new file mode 100644 index 00000000..2980fbfb --- /dev/null +++ b/configs/datamodule/classic_bag_of_embeddings_datamodule.yaml @@ -0,0 +1,12 @@ +defaults: + - datasets@train: embeddings/bag_of_embeddings_sl_labeled + - datasets@val: embeddings/bag_of_embeddings_sl_labeled + - datasets@test: embeddings/bag_of_embeddings_sl_labeled + - datasets@predict: embeddings/bag_of_embeddings_unlabeled + + - samplers@sampler: bag_of_tiles_stratified_weighted_random + +_target_: prostate_cancer.datamodule.ClassicBagOfTilesDataModule + +batch_size: ??? +num_workers: ??? diff --git a/configs/datamodule/datasets/embeddings/bag_of_embeddings_sl_labeled.yaml b/configs/datamodule/datasets/embeddings/bag_of_embeddings_sl_labeled.yaml new file mode 100644 index 00000000..ed8ade57 --- /dev/null +++ b/configs/datamodule/datasets/embeddings/bag_of_embeddings_sl_labeled.yaml @@ -0,0 +1,2 @@ +_target_: prostate_cancer.datamodule.datasets.SLLabeledBagOfEmbeddingsDataset +uris: ??? diff --git a/configs/model/pgp_classic_mil_model.yaml b/configs/model/pgp_classic_mil_model.yaml new file mode 100644 index 00000000..f9534646 --- /dev/null +++ b/configs/model/pgp_classic_mil_model.yaml @@ -0,0 +1,4 @@ +_target_: prostate_cancer.classic_mil_model.ProstateCancerClassicMIL +foundation: "pgp" +lr: ??? +sl_threshold: ??? diff --git a/configs/model/virchow2_classic_mil_model.yaml b/configs/model/virchow2_classic_mil_model.yaml new file mode 100644 index 00000000..13221fcf --- /dev/null +++ b/configs/model/virchow2_classic_mil_model.yaml @@ -0,0 +1,4 @@ +_target_: prostate_cancer.classic_mil_model.ProstateCancerClassicMIL +foundation: "virchow2" +lr: ??? +sl_threshold: ??? diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 189eb533..6005eaa7 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -3,134 +3,33 @@ from copy import deepcopy import torch -from lightning import LightningModule from torch import Tensor, nn -from torch.optim.adamw import AdamW -from torch.optim.optimizer import Optimizer -from torchmetrics import Metric, MetricCollection -from torchmetrics.classification import ( - AUROC, - Accuracy, - NegativePredictiveValue, - Precision, - Recall, - Specificity, -) - -from prostate_cancer.typing import ( - LabeledBagOfTilesSampleBatch, - MILModelOutput, - UnlabeledBagOfTilesSampleBatch, -) - - -class ProstateCancerAttentionMIL(LightningModule): +from torchmetrics import MetricCollection + +from prostate_cancer.mil_model_base import ProstateCancerMILBase, binary_metrics +from prostate_cancer.typing import LabeledBagOfTilesSampleBatch, MILModelOutput + + +class ProstateCancerAttentionMIL(ProstateCancerMILBase): + """Hybrid MIL: trained on both slide-level (SL) and tile-level (TL) labels.""" + def __init__( self, foundation: str, lr: float, tl_threshold: float, sl_threshold: float ) -> None: - super().__init__() - match foundation: - case "pgp": - self.input_dim = 1536 - case "virchow2": - self.input_dim = 2560 - case _: - raise ValueError(f"Unknown foundation model: {foundation}") - - self.input_dim_sqrt = torch.tensor(self.input_dim).sqrt() - - # if we did not precompute the embeddings, we would obtain it from this module - # (idendity replaced with foundation model) - self.encoder = nn.Identity() - - # from a paper - self.attention = nn.Sequential( - nn.Linear(self.input_dim, 512), - nn.Tanh(), - nn.Linear(512, 1), - ) + super().__init__(foundation=foundation, lr=lr, sl_threshold=sl_threshold) - # TL Classifier - self.classifier = nn.Linear(self.input_dim, 1) - - self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") self.tl_criterion = nn.BCEWithLogitsLoss( reduction="none", pos_weight=torch.tensor([9.65]) ) # handle padding - self.lr = lr - - metrics: dict[str, dict[str, Metric | MetricCollection]] = {} - - # both SL and TL metrics - for task_type, t in [("tl", tl_threshold), ("sl", sl_threshold)]: - metrics[task_type] = { - "AUC": AUROC("binary"), - "accuracy": Accuracy("binary", threshold=t), - "precision": Precision("binary", threshold=t), - "recall": Recall("binary", threshold=t), - "specificity": Specificity("binary", threshold=t), - "negative_predictive_value": NegativePredictiveValue( - "binary", threshold=t - ), - } - - self.train_metrics_sl = MetricCollection( - deepcopy(metrics["sl"]), prefix="sl_train/" - ) - self.val_metrics_sl = MetricCollection( - deepcopy(metrics["sl"]), prefix="sl_validation/" - ) - self.test_metrics_sl = MetricCollection( - deepcopy(metrics["sl"]), prefix="sl_test/" - ) + tl_metrics = binary_metrics(tl_threshold) self.train_metrics_tl = MetricCollection( - deepcopy(metrics["tl"]), prefix="tl_train/" + deepcopy(tl_metrics), prefix="tl_train/" ) self.val_metrics_tl = MetricCollection( - deepcopy(metrics["tl"]), prefix="tl_validation/" - ) - self.test_metrics_tl = MetricCollection( - deepcopy(metrics["tl"]), prefix="tl_test/" + deepcopy(tl_metrics), prefix="tl_validation/" ) - - def forward(self, x: Tensor) -> MILModelOutput: - # x has shape (batch_size, num_tiles_padded, embedding_dim) - - # Just identity - x = self.encoder(x) # (batch_size, num_tiles_padded, embedding_dim) - - # Do not attend to padded tiles (true for non-padded elements) - mask = ( - (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() - ) # (batch_size, num_tiles_padded, 1) - - # TL weights (which tiles to attend to) - raw_attn: Tensor = self.attention(x) # (batch_size, num_tiles_padded, 1) - raw_attn = raw_attn.masked_fill( - ~mask.bool(), float("-inf") - ) # (batch_size, num_tiles_padded, 1) - - # make it a distribution - attention_weights = torch.softmax( - raw_attn, dim=1 - ) # (batch_size, num_tiles_padded, 1) - - # TL predictions - tl_preds_raw: Tensor = self.classifier(x) # (batch_size, num_tiles_padded, 1) - tl_preds_valid_raw = tl_preds_raw * mask - - # weight TL predictions with attention - sl_pred_raw = torch.sum( - attention_weights * tl_preds_valid_raw, dim=1 - ) # (batch_size, 1) - - return ( - sl_pred_raw.squeeze(-1), - tl_preds_valid_raw.squeeze(-1), - mask.squeeze(-1), - attention_weights.squeeze(-1), - ) # (batch_size,), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded) + self.test_metrics_tl = MetricCollection(deepcopy(tl_metrics), prefix="tl_test/") def training_step(self, batch: LabeledBagOfTilesSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide @@ -203,7 +102,7 @@ def validation_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: self.val_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) - def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: + def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: # type: ignore[override] bags, tl_labels, sl_labels, _ = batch sl_outputs, tl_outputs, mask, attention = self(bags) @@ -218,10 +117,3 @@ def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: self.test_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) return sl_outputs.sigmoid(), tl_outputs.sigmoid(), mask, attention - - def predict_step(self, batch: UnlabeledBagOfTilesSampleBatch) -> MILModelOutput: - sl_preds_raw, tl_preds_raw, mask, attention = self(batch[0]) - return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid(), mask, attention - - def configure_optimizers(self) -> Optimizer: - return AdamW(self.parameters(), lr=self.lr) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 8a3e0b15..9e847262 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -122,7 +122,7 @@ def on_test_batch_end( self, trainer: Trainer, pl_module: LightningModule, - outputs: MILModelOutput, + outputs: MILModelOutput, # type: ignore[override] batch: LabeledBagOfTilesSampleBatch, batch_idx: int, dataloader_idx: int = 0, diff --git a/prostate_cancer/callbacks/nested_metrics_callback_mil.py b/prostate_cancer/callbacks/nested_metrics_callback_mil.py index 8710327e..954b1374 100644 --- a/prostate_cancer/callbacks/nested_metrics_callback_mil.py +++ b/prostate_cancer/callbacks/nested_metrics_callback_mil.py @@ -31,4 +31,6 @@ def on_test_batch_end( ] # Update slide-level metrics - self.nested_test_metrics.update(tl_outputs_valid.cpu(), targets_valid.cpu(), keys) + self.nested_test_metrics.update( + tl_outputs_valid.cpu(), targets_valid.cpu(), keys + ) diff --git a/prostate_cancer/classic_mil_model.py b/prostate_cancer/classic_mil_model.py new file mode 100644 index 00000000..5974ed9a --- /dev/null +++ b/prostate_cancer/classic_mil_model.py @@ -0,0 +1,49 @@ +from torch import Tensor + +from prostate_cancer.mil_model_base import ProstateCancerMILBase +from prostate_cancer.typing import MILModelOutput, SLLabeledBagOfTilesSampleBatch + + +class ProstateCancerClassicMIL(ProstateCancerMILBase): + """Classic MIL: trained only on slide-level (SL) labels, no TL supervision.""" + + def training_step(self, batch: SLLabeledBagOfTilesSampleBatch) -> Tensor: + # bag ~ all embeddings from a single slide + bags, sl_labels, _ = batch + + sl_outputs, _, _, _ = self(bags) + loss = self.sl_criterion(sl_outputs, sl_labels) + + self.log("train/loss", loss, on_step=True, prog_bar=True, batch_size=len(bags)) + + self.train_metrics_sl.update(sl_outputs, sl_labels) + self.log_dict( + self.train_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) + ) + + return loss + + def validation_step(self, batch: SLLabeledBagOfTilesSampleBatch) -> None: + bags, sl_labels, _ = batch + + sl_outputs, _, _, _ = self(bags) + loss = self.sl_criterion(sl_outputs, sl_labels) + + self.log("validation/loss", loss, prog_bar=True, batch_size=len(bags)) + + self.val_metrics_sl.update(sl_outputs, sl_labels) + self.log_dict( + self.val_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) + ) + + def test_step(self, batch: SLLabeledBagOfTilesSampleBatch) -> MILModelOutput: # type: ignore[override] + bags, sl_labels, _ = batch + + sl_outputs, tl_outputs, mask, attention = self(bags) + + self.test_metrics_sl.update(sl_outputs, sl_labels) + self.log_dict( + self.test_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) + ) + + return sl_outputs.sigmoid(), tl_outputs.sigmoid(), mask, attention diff --git a/prostate_cancer/datamodule/__init__.py b/prostate_cancer/datamodule/__init__.py index 7cd2bd5c..b83a1012 100644 --- a/prostate_cancer/datamodule/__init__.py +++ b/prostate_cancer/datamodule/__init__.py @@ -1,5 +1,8 @@ from prostate_cancer.datamodule.bag_of_tiles_data_module import BagOfTilesDataModule +from prostate_cancer.datamodule.classic_bag_of_tiles_data_module import ( + ClassicBagOfTilesDataModule, +) from prostate_cancer.datamodule.tile_data_module import TileDataModule -__all__ = ["BagOfTilesDataModule", "TileDataModule"] +__all__ = ["BagOfTilesDataModule", "ClassicBagOfTilesDataModule", "TileDataModule"] diff --git a/prostate_cancer/datamodule/bag_of_tiles_data_module.py b/prostate_cancer/datamodule/bag_of_tiles_data_module.py index 3879b1ed..20bef27a 100644 --- a/prostate_cancer/datamodule/bag_of_tiles_data_module.py +++ b/prostate_cancer/datamodule/bag_of_tiles_data_module.py @@ -1,5 +1,6 @@ +from abc import ABC, abstractmethod from collections.abc import Iterable -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Any, cast import torch from hydra.utils import instantiate @@ -10,19 +11,27 @@ if TYPE_CHECKING: from prostate_cancer.datamodule.datasets import ( - LabeledBagOfEmbeddingsDataset, + BagOfEmbeddingsDataset, UnlabeledBagOfEmbeddingsDataset, ) from prostate_cancer.typing import ( LabeledBagOfTilesSample, LabeledBagOfTilesSampleBatch, + SLLabeledBagOfTilesSampleBatch, UnlabeledBagOfTilesSample, UnlabeledBagOfTilesSampleBatch, ) -class BagOfTilesDataModule(LightningDataModule): +class BaseBagOfTilesDataModule(LightningDataModule, ABC): + """Shared plumbing for bag-of-tiles (MIL) datamodules. + + Subclasses only provide the collate function for labeled samples, since + that is the only part that depends on which labels (SL only, or SL+TL) + the underlying dataset produces. + """ + def __init__( self, batch_size: int, @@ -40,22 +49,18 @@ def setup(self, stage: str) -> None: match stage: case "fit": self.train = cast( - "LabeledBagOfEmbeddingsDataset", - instantiate(self.datasets["train"]), + "BagOfEmbeddingsDataset[Any]", instantiate(self.datasets["train"]) ) self.val = cast( - "LabeledBagOfEmbeddingsDataset", - instantiate(self.datasets["val"]), + "BagOfEmbeddingsDataset[Any]", instantiate(self.datasets["val"]) ) case "val": self.val = cast( - "LabeledBagOfEmbeddingsDataset", - instantiate(self.datasets["val"]), + "BagOfEmbeddingsDataset[Any]", instantiate(self.datasets["val"]) ) case "test": self.test = cast( - "LabeledBagOfEmbeddingsDataset", - instantiate(self.datasets["test"]), + "BagOfEmbeddingsDataset[Any]", instantiate(self.datasets["test"]) ) case "predict": self.predict = cast( @@ -63,7 +68,12 @@ def setup(self, stage: str) -> None: instantiate(self.datasets["predict"]), ) - def train_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: + @abstractmethod + def _collate_labeled(self, batch: list[Any]) -> Any: ... + + def train_dataloader( + self, + ) -> Iterable[LabeledBagOfTilesSampleBatch | SLLabeledBagOfTilesSampleBatch]: if self.sampler_partial: sampler = instantiate(self.sampler_partial)( @@ -78,27 +88,31 @@ def train_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: self.train, sampler=sampler, shuffle=shuffle, - collate_fn=collate_fn_labeled, + collate_fn=self._collate_labeled, batch_size=self.batch_size, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, drop_last=True, ) - def val_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: + def val_dataloader( + self, + ) -> Iterable[LabeledBagOfTilesSampleBatch | SLLabeledBagOfTilesSampleBatch]: return DataLoader( self.val, batch_size=self.batch_size, - collate_fn=collate_fn_labeled, + collate_fn=self._collate_labeled, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, ) - def test_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: + def test_dataloader( + self, + ) -> Iterable[LabeledBagOfTilesSampleBatch | SLLabeledBagOfTilesSampleBatch]: return DataLoader( self.test, batch_size=self.batch_size, - collate_fn=collate_fn_labeled, + collate_fn=self._collate_labeled, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, ) @@ -113,6 +127,15 @@ def predict_dataloader(self) -> Iterable[UnlabeledBagOfTilesSampleBatch]: ) +class BagOfTilesDataModule(BaseBagOfTilesDataModule): + """Datamodule for hybrid MIL: labeled samples carry both SL and TL labels.""" + + def _collate_labeled( + self, batch: list[LabeledBagOfTilesSample] + ) -> LabeledBagOfTilesSampleBatch: + return collate_fn_labeled(batch) + + def collate_fn_labeled( batch: list[LabeledBagOfTilesSample], ) -> LabeledBagOfTilesSampleBatch: diff --git a/prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py b/prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py new file mode 100644 index 00000000..03cc44cf --- /dev/null +++ b/prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py @@ -0,0 +1,34 @@ +import torch + +from prostate_cancer.datamodule.bag_of_tiles_data_module import ( + BaseBagOfTilesDataModule, +) +from prostate_cancer.typing import ( + SLLabeledBagOfTilesSample, + SLLabeledBagOfTilesSampleBatch, +) + + +class ClassicBagOfTilesDataModule(BaseBagOfTilesDataModule): + """Datamodule for classic MIL: labeled samples only carry SL labels.""" + + def _collate_labeled( + self, batch: list[SLLabeledBagOfTilesSample] + ) -> SLLabeledBagOfTilesSampleBatch: + return collate_fn_sl_labeled(batch) + + +def collate_fn_sl_labeled( + batch: list[SLLabeledBagOfTilesSample], +) -> SLLabeledBagOfTilesSampleBatch: + inputs = [] + sl_labels = [] + metadatas = [] + for input, sl_label, metadata in batch: + inputs.append(input) + sl_labels.append(sl_label) + metadatas.append(metadata) + + inputs_tensor = torch.stack(inputs) + sl_labels_tensor = torch.stack(sl_labels) + return inputs_tensor, sl_labels_tensor, metadatas diff --git a/prostate_cancer/datamodule/datasets/__init__.py b/prostate_cancer/datamodule/datasets/__init__.py index 32d3833b..4d63aae5 100644 --- a/prostate_cancer/datamodule/datasets/__init__.py +++ b/prostate_cancer/datamodule/datasets/__init__.py @@ -1,5 +1,7 @@ from prostate_cancer.datamodule.datasets.bag_of_embeddings_dataset import ( + BagOfEmbeddingsDataset, LabeledBagOfEmbeddingsDataset, + SLLabeledBagOfEmbeddingsDataset, UnlabeledBagOfEmbeddingsDataset, ) from prostate_cancer.datamodule.datasets.embeddings_dataset import ( @@ -13,9 +15,11 @@ __all__ = [ + "BagOfEmbeddingsDataset", "LabeledBagOfEmbeddingsDataset", "LabeledEmbeddingsDataset", "LabeledTilesDataset", + "SLLabeledBagOfEmbeddingsDataset", "UnlabeledBagOfEmbeddingsDataset", "UnlabeledEmbeddingsDataset", "UnlabeledTilesDataset", diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index a6bb5825..6b190c18 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -1,5 +1,6 @@ """These Datasets were taken from Adam Kukučka Ulcerative Colitis project and modified.""" +from abc import ABC, abstractmethod from collections import Counter from collections.abc import Iterable from pathlib import Path @@ -7,43 +8,43 @@ import torch import torch.nn.functional as F +from datasets import Dataset as HFDataset from rationai.mlkit.data.datasets.slides_tiles_loader import SlidesTilesLoader from torch.utils.data import Dataset from prostate_cancer.typing import ( LabeledBagOfTilesSample, SlideMetadata, + SLLabeledBagOfTilesSample, + TilingSlideMetadata, UnlabeledBagOfTilesSample, ) -T = TypeVar("T", bound=LabeledBagOfTilesSample | UnlabeledBagOfTilesSample) +T = TypeVar( + "T", + bound=LabeledBagOfTilesSample + | SLLabeledBagOfTilesSample + | UnlabeledBagOfTilesSample, +) + + +class BagOfEmbeddingsDataset(Dataset[T], Generic[T], ABC): + """Base for bag-of-embeddings (MIL) datasets: one item per slide. + Handles loading slide/tile metadata, assembling the (padded) bag of tile + embeddings and building the shared slide-level metadata. Subclasses only + decide which labels (if any) accompany the bag. + """ -class BagOfEmbeddingsDataset(Dataset[T], Generic[T]): def __init__( self, uris: Iterable[str], padding: bool = True, - carcinoma_roi_t: float | None = None, ) -> None: - self.include_labels = carcinoma_roi_t is not None - self.carcinoma_roi_t = carcinoma_roi_t - self._meta = SlidesTilesLoader(uris=uris) self.slides = self._meta.slides - tiles = self._meta.tiles - - if self.include_labels: - tiles = tiles.map( - lambda r: { - "carcinoma": (r["carcinoma_roi_percentage"] > self.carcinoma_roi_t) - } - ) - - self.tiles = tiles - self._meta.tiles = tiles - # no need to re-build index after .map + self.tiles = self._meta.tiles self.padding = padding @@ -55,7 +56,9 @@ def __init__( def __len__(self) -> int: return len(self.slides) - def __getitem__(self, idx: int) -> T: + def _load_bag( + self, idx: int + ) -> tuple[TilingSlideMetadata, HFDataset, torch.Tensor, SlideMetadata]: slide_metadata = self.slides[idx] slide_name = Path(slide_metadata["path"]).stem @@ -81,40 +84,78 @@ def __getitem__(self, idx: int) -> T: ys=torch.tensor(slide_tiles["y"]), ) - if not self.include_labels: - return slide_embeddings, metadata # type: ignore[return-value] + return slide_metadata, slide_tiles, slide_embeddings, metadata - sl_label = torch.tensor(slide_metadata["carcinoma"]).float() + @abstractmethod + def __getitem__(self, idx: int) -> T: ... - tl_labels = torch.zeros(len(slide_embeddings)).float() - tl_labels[: len(slide_tiles)] = torch.tensor(slide_tiles["carcinoma"]).float() - return slide_embeddings, tl_labels, sl_label, metadata # type: ignore[return-value] +class UnlabeledBagOfEmbeddingsDataset( + BagOfEmbeddingsDataset[UnlabeledBagOfTilesSample] +): + def __init__( + self, + uris: Iterable[str], + padding: bool = True, + ) -> None: + super().__init__(uris=uris, padding=padding) + def __getitem__(self, idx: int) -> UnlabeledBagOfTilesSample: + _, _, slide_embeddings, metadata = self._load_bag(idx) + return slide_embeddings, metadata + + +class SLLabeledBagOfEmbeddingsDataset( + BagOfEmbeddingsDataset[SLLabeledBagOfTilesSample] +): + """Bag-of-embeddings dataset carrying only slide-level (SL) labels. + + Unlike `LabeledBagOfEmbeddingsDataset`, this does not require tile-level + (TL) carcinoma annotations, so it can be used with data that only has + slide-level ground truth (classic MIL, no TL supervision). + """ -class LabeledBagOfEmbeddingsDataset(BagOfEmbeddingsDataset[LabeledBagOfTilesSample]): def __init__( self, uris: Iterable[str], - carcinoma_roi_t: float, padding: bool = True, ) -> None: - super().__init__( - uris=uris, - padding=padding, - carcinoma_roi_t=carcinoma_roi_t, - ) + super().__init__(uris=uris, padding=padding) + def __getitem__(self, idx: int) -> SLLabeledBagOfTilesSample: + slide_metadata, _, slide_embeddings, metadata = self._load_bag(idx) + + sl_label = torch.tensor(slide_metadata["carcinoma"]).float() + + return slide_embeddings, sl_label, metadata + + +class LabeledBagOfEmbeddingsDataset(BagOfEmbeddingsDataset[LabeledBagOfTilesSample]): + """Bag-of-embeddings dataset carrying both SL and TL labels (hybrid MIL).""" -class UnlabeledBagOfEmbeddingsDataset( - BagOfEmbeddingsDataset[UnlabeledBagOfTilesSample] -): def __init__( self, uris: Iterable[str], + carcinoma_roi_t: float, padding: bool = True, ) -> None: - super().__init__( - uris=uris, - padding=padding, + super().__init__(uris=uris, padding=padding) + self.carcinoma_roi_t = carcinoma_roi_t + + self.tiles = self.tiles.map( + lambda r: { + "carcinoma": (r["carcinoma_roi_percentage"] > self.carcinoma_roi_t) + } ) + self._meta.tiles = self.tiles + # no need to re-build index after .map + + def __getitem__(self, idx: int) -> LabeledBagOfTilesSample: + slide_metadata, slide_tiles, slide_embeddings, metadata = self._load_bag(idx) + + sl_label = torch.tensor(slide_metadata["carcinoma"]).float() + + tl_labels = torch.zeros(len(slide_embeddings)).float() + tl_labels[: len(slide_tiles)] = torch.tensor(slide_tiles["carcinoma"]).float() + + return slide_embeddings, tl_labels, sl_label, metadata diff --git a/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py index 78f1eb2f..1969e438 100644 --- a/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py +++ b/prostate_cancer/datamodule/samplers/bag_of_tiles_stratified_weighted_random_sampler.py @@ -1,15 +1,18 @@ from collections.abc import Sequence +from typing import Any from datasets import Dataset as HFDataset from torch.utils.data import WeightedRandomSampler -from prostate_cancer.datamodule.datasets import LabeledBagOfEmbeddingsDataset +from prostate_cancer.datamodule.datasets.bag_of_embeddings_dataset import ( + BagOfEmbeddingsDataset, +) class BagOfTilesStratifiedWeightedRandomSampler(WeightedRandomSampler): def __init__( self, - dataset: LabeledBagOfEmbeddingsDataset, + dataset: BagOfEmbeddingsDataset[Any], target_col: str, replacement: bool = True, ) -> None: diff --git a/prostate_cancer/mil_model_base.py b/prostate_cancer/mil_model_base.py new file mode 100644 index 00000000..2eb23a6b --- /dev/null +++ b/prostate_cancer/mil_model_base.py @@ -0,0 +1,126 @@ +"""Original was created by Adam Kukučka in Ulcerative Colitis project.""" + +from copy import deepcopy + +import torch +from lightning import LightningModule +from torch import Tensor, nn +from torch.optim.adamw import AdamW +from torch.optim.optimizer import Optimizer +from torchmetrics import Metric, MetricCollection +from torchmetrics.classification import ( + AUROC, + Accuracy, + NegativePredictiveValue, + Precision, + Recall, + Specificity, +) + +from prostate_cancer.typing import MILModelOutput, UnlabeledBagOfTilesSampleBatch + + +def binary_metrics(threshold: float) -> dict[str, Metric | MetricCollection]: + return { + "AUC": AUROC("binary"), + "accuracy": Accuracy("binary", threshold=threshold), + "precision": Precision("binary", threshold=threshold), + "recall": Recall("binary", threshold=threshold), + "specificity": Specificity("binary", threshold=threshold), + "negative_predictive_value": NegativePredictiveValue( + "binary", threshold=threshold + ), + } + + +class ProstateCancerMILBase(LightningModule): + """Attention-MIL architecture shared by hybrid (SL+TL) and classic (SL-only) models. + + The bag encoder/attention/classifier and the forward pass are identical + regardless of which labels supervise training - subclasses only differ in + which labels they consume in `training_step`/`validation_step`/`test_step` + and which losses/metrics they compute from them. + """ + + def __init__(self, foundation: str, lr: float, sl_threshold: float) -> None: + super().__init__() + match foundation: + case "pgp": + self.input_dim = 1536 + case "virchow2": + self.input_dim = 2560 + case _: + raise ValueError(f"Unknown foundation model: {foundation}") + + self.input_dim_sqrt = torch.tensor(self.input_dim).sqrt() + + # if we did not precompute the embeddings, we would obtain it from this module + # (idendity replaced with foundation model) + self.encoder = nn.Identity() + + # from a paper + self.attention = nn.Sequential( + nn.Linear(self.input_dim, 512), + nn.Tanh(), + nn.Linear(512, 1), + ) + + # per-tile classifier, attention-pooled into the bag (SL) prediction + self.classifier = nn.Linear(self.input_dim, 1) + + self.sl_criterion = nn.BCEWithLogitsLoss(reduction="mean") + self.lr = lr + + sl_metrics = binary_metrics(sl_threshold) + self.train_metrics_sl = MetricCollection( + deepcopy(sl_metrics), prefix="sl_train/" + ) + self.val_metrics_sl = MetricCollection( + deepcopy(sl_metrics), prefix="sl_validation/" + ) + self.test_metrics_sl = MetricCollection(deepcopy(sl_metrics), prefix="sl_test/") + + def forward(self, x: Tensor) -> MILModelOutput: + # x has shape (batch_size, num_tiles_padded, embedding_dim) + + # Just identity + x = self.encoder(x) # (batch_size, num_tiles_padded, embedding_dim) + + # Do not attend to padded tiles (true for non-padded elements) + mask = ( + (x.abs() > 1e-6).any(dim=-1, keepdim=True).float() + ) # (batch_size, num_tiles_padded, 1) + + # TL weights (which tiles to attend to) + raw_attn: Tensor = self.attention(x) # (batch_size, num_tiles_padded, 1) + raw_attn = raw_attn.masked_fill( + ~mask.bool(), float("-inf") + ) # (batch_size, num_tiles_padded, 1) + + # make it a distribution + attention_weights = torch.softmax( + raw_attn, dim=1 + ) # (batch_size, num_tiles_padded, 1) + + # TL predictions + tl_preds_raw: Tensor = self.classifier(x) # (batch_size, num_tiles_padded, 1) + tl_preds_valid_raw = tl_preds_raw * mask + + # weight TL predictions with attention + sl_pred_raw = torch.sum( + attention_weights * tl_preds_valid_raw, dim=1 + ) # (batch_size, 1) + + return ( + sl_pred_raw.squeeze(-1), + tl_preds_valid_raw.squeeze(-1), + mask.squeeze(-1), + attention_weights.squeeze(-1), + ) # (batch_size,), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded) + + def predict_step(self, batch: UnlabeledBagOfTilesSampleBatch) -> MILModelOutput: + sl_preds_raw, tl_preds_raw, mask, attention = self(batch[0]) + return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid(), mask, attention + + def configure_optimizers(self) -> Optimizer: + return AdamW(self.parameters(), lr=self.lr) diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index deadd360..f779a234 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -49,11 +49,15 @@ class SlideMetadata(TypedDict): LabeledBagOfTilesSample = tuple[ Tensor, Tensor, Tensor, SlideMetadata ] # tiles / embeddings, tl_labels, sl_labels, metadata +SLLabeledBagOfTilesSample = tuple[ + Tensor, Tensor, SlideMetadata +] # tiles / embeddings, sl_label, metadata (no TL labels) UnlabeledBagOfTilesSample = tuple[Tensor, SlideMetadata] # tiles / embeddings, metadata LabeledBagOfTilesSampleBatch: TypeAlias = tuple[ Tensor, Tensor, Tensor, list[SlideMetadata] ] +SLLabeledBagOfTilesSampleBatch: TypeAlias = tuple[Tensor, Tensor, list[SlideMetadata]] UnlabeledBagOfTilesSampleBatch: TypeAlias = tuple[Tensor, list[SlideMetadata]] MILModelOutput = tuple[ From 1b196e4a6a1be6f009dffb727b3b984bdf3fc738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 21 Jul 2026 15:11:19 +0000 Subject: [PATCH 256/284] feat: thresholds --- configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml | 4 ++-- configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml index 074c4921..90804e6b 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml @@ -2,8 +2,8 @@ defaults: - base_foundation - _self_ -tile_threshold: 0.5 -slide_threshold: 0.5 +tile_threshold: 0.88 +slide_threshold: 0.98 checkpoint: "mlflow-artifacts:/65/7cb992adfe4b422790becc5984ff2a6f/artifacts/checkpoints/epoch=8-step=1583/checkpoint.ckpt" model_name: "PGP (Hybrid-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml index 9a00458b..c0b0946a 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml @@ -2,8 +2,8 @@ defaults: - base_foundation - _self_ -tile_threshold: 0.5 -slide_threshold: 0.5 +tile_threshold: 0.85 +slide_threshold: 0.99 checkpoint: "mlflow-artifacts:/65/5e0c1d4699ca49c4938baf3c65e7f61c/artifacts/checkpoints/epoch=6-step=1176/checkpoint.ckpt" model_name: "Virchow2 (Hybrid-MIL)" From 6ecafd91a11ee6b0a73e4cfff8134ed56e8998a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 21 Jul 2026 15:32:32 +0000 Subject: [PATCH 257/284] feat: cleanup --- .../datamodule/datasets/bag_of_embeddings_dataset.py | 2 +- prostate_cancer/typing.py | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index 6b190c18..fb024831 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -155,7 +155,7 @@ def __getitem__(self, idx: int) -> LabeledBagOfTilesSample: sl_label = torch.tensor(slide_metadata["carcinoma"]).float() - tl_labels = torch.zeros(len(slide_embeddings)).float() + tl_labels = torch.zeros(len(slide_embeddings)).float() # pad with zero labels tl_labels[: len(slide_tiles)] = torch.tensor(slide_tiles["carcinoma"]).float() return slide_embeddings, tl_labels, sl_label, metadata diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index f779a234..f9468abb 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -3,6 +3,7 @@ from torch import Tensor +# How does one row in slides.parquet look like class TilingSlideMetadata(TypedDict): id: str path: str @@ -19,6 +20,7 @@ class TilingSlideMetadata(TypedDict): carcinoma: bool +# how does tile metadata look like in TL dataset class TileMetadata(TypedDict): slide: str x: int @@ -31,13 +33,14 @@ class TileMetadataBatch(TypedDict): y: Tensor -LabeledTileSample: TypeAlias = tuple[Tensor, Tensor, TileMetadata] -UnlabeledTileSample: TypeAlias = tuple[Tensor, TileMetadata] +LabeledTileSample: TypeAlias = tuple[Tensor, Tensor, TileMetadata] # Image | label | Metadata +UnlabeledTileSample: TypeAlias = tuple[Tensor, TileMetadata] # Image | Metadata -LabeledTileSampleBatch: TypeAlias = tuple[Tensor, Tensor, TileMetadataBatch] -UnlabeledTileSampleBatch: TypeAlias = tuple[Tensor, TileMetadataBatch] +LabeledTileSampleBatch: TypeAlias = tuple[Tensor, Tensor, TileMetadataBatch] # Images | labels | Metadata +UnlabeledTileSampleBatch: TypeAlias = tuple[Tensor, TileMetadataBatch] # Images | labels | Metadata +# how does slide metadata in bag dataset look like class SlideMetadata(TypedDict): slide_id: str slide_name: str From 8d1341d3e426511506f706213bd95ef134f15333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 21 Jul 2026 16:34:57 +0000 Subject: [PATCH 258/284] feat: prepare training --- .../classic_bag_of_embeddings_datamodule.yaml | 2 +- .../train/bag_of_embeddings/base.yaml | 1 - .../train/bag_of_embeddings/classic_base.yaml | 6 + .../train/bag_of_embeddings/classic_pgp.yaml | 16 +++ .../bag_of_embeddings/classic_virchow2.yaml | 16 +++ .../train/bag_of_embeddings/hybrid_base.yaml | 6 + .../{pgp.yaml => hybrid_pgp.yaml} | 2 +- .../{virchow2.yaml => hybrid_virchow2.yaml} | 2 +- configs/model/pgp_classic_mil_model.yaml | 1 + configs/model/virchow2_classic_mil_model.yaml | 1 + prostate_cancer/attention_mil_model.py | 26 ++-- prostate_cancer/classic_mil_model.py | 22 ++-- prostate_cancer/datamodule/__init__.py | 4 +- .../datamodule/bag_of_tiles_data_module.py | 112 ++++++++++-------- .../classic_bag_of_tiles_data_module.py | 34 ------ prostate_cancer/mil_model_base.py | 40 ++++++- 16 files changed, 165 insertions(+), 126 deletions(-) create mode 100644 configs/experiment/train/bag_of_embeddings/classic_base.yaml create mode 100644 configs/experiment/train/bag_of_embeddings/classic_pgp.yaml create mode 100644 configs/experiment/train/bag_of_embeddings/classic_virchow2.yaml create mode 100644 configs/experiment/train/bag_of_embeddings/hybrid_base.yaml rename configs/experiment/train/bag_of_embeddings/{pgp.yaml => hybrid_pgp.yaml} (83%) rename configs/experiment/train/bag_of_embeddings/{virchow2.yaml => hybrid_virchow2.yaml} (84%) delete mode 100644 prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py diff --git a/configs/datamodule/classic_bag_of_embeddings_datamodule.yaml b/configs/datamodule/classic_bag_of_embeddings_datamodule.yaml index 2980fbfb..3a23d90c 100644 --- a/configs/datamodule/classic_bag_of_embeddings_datamodule.yaml +++ b/configs/datamodule/classic_bag_of_embeddings_datamodule.yaml @@ -1,7 +1,7 @@ defaults: - datasets@train: embeddings/bag_of_embeddings_sl_labeled - datasets@val: embeddings/bag_of_embeddings_sl_labeled - - datasets@test: embeddings/bag_of_embeddings_sl_labeled + - datasets@test: embeddings/bag_of_embeddings_labeled - datasets@predict: embeddings/bag_of_embeddings_unlabeled - samplers@sampler: bag_of_tiles_stratified_weighted_random diff --git a/configs/experiment/train/bag_of_embeddings/base.yaml b/configs/experiment/train/bag_of_embeddings/base.yaml index 8c3a995f..627f0db4 100644 --- a/configs/experiment/train/bag_of_embeddings/base.yaml +++ b/configs/experiment/train/bag_of_embeddings/base.yaml @@ -4,7 +4,6 @@ defaults: - /experiment/train/base - /data/mmci/tl_train@train_data - /data/mmci/tl_val@val_data - - /datamodule/bag_of_embeddings_datamodule@datamodule - _self_ model: diff --git a/configs/experiment/train/bag_of_embeddings/classic_base.yaml b/configs/experiment/train/bag_of_embeddings/classic_base.yaml new file mode 100644 index 00000000..65cd3e8a --- /dev/null +++ b/configs/experiment/train/bag_of_embeddings/classic_base.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/train/bag_of_embeddings/base + - /datamodule/classic_bag_of_embeddings_datamodule@datamodule + - _self_ diff --git a/configs/experiment/train/bag_of_embeddings/classic_pgp.yaml b/configs/experiment/train/bag_of_embeddings/classic_pgp.yaml new file mode 100644 index 00000000..9d38d6d3 --- /dev/null +++ b/configs/experiment/train/bag_of_embeddings/classic_pgp.yaml @@ -0,0 +1,16 @@ +# @package _global_ + +defaults: + - /experiment/train/bag_of_embeddings/classic_base + - /model/pgp_classic_mil_model@model + - _self_ + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_w_pgp_uri_224} + val: + uris: + - ${val_data.tiles_filtered_w_pgp_uri_224} + +model_name: "PGP (Classic-MIL)" diff --git a/configs/experiment/train/bag_of_embeddings/classic_virchow2.yaml b/configs/experiment/train/bag_of_embeddings/classic_virchow2.yaml new file mode 100644 index 00000000..5c62bc7a --- /dev/null +++ b/configs/experiment/train/bag_of_embeddings/classic_virchow2.yaml @@ -0,0 +1,16 @@ +# @package _global_ + +defaults: + - /experiment/train/bag_of_embeddings/classic_base + - /model/virchow2_classic_mil_model@model + - _self_ + +datamodule: + train: + uris: + - ${train_data.tiles_filtered_w_virchow2_uri_224} + val: + uris: + - ${val_data.tiles_filtered_w_virchow2_uri_224} + +model_name: "Virchow2 (Classic-MIL)" diff --git a/configs/experiment/train/bag_of_embeddings/hybrid_base.yaml b/configs/experiment/train/bag_of_embeddings/hybrid_base.yaml new file mode 100644 index 00000000..40327814 --- /dev/null +++ b/configs/experiment/train/bag_of_embeddings/hybrid_base.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/train/bag_of_embeddings/base + - /datamodule/bag_of_embeddings_datamodule@datamodule + - _self_ diff --git a/configs/experiment/train/bag_of_embeddings/pgp.yaml b/configs/experiment/train/bag_of_embeddings/hybrid_pgp.yaml similarity index 83% rename from configs/experiment/train/bag_of_embeddings/pgp.yaml rename to configs/experiment/train/bag_of_embeddings/hybrid_pgp.yaml index 8c4439d0..1f04a336 100644 --- a/configs/experiment/train/bag_of_embeddings/pgp.yaml +++ b/configs/experiment/train/bag_of_embeddings/hybrid_pgp.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/bag_of_embeddings/base + - /experiment/train/bag_of_embeddings/hybrid_base - /model/pgp_hybrid_mil_model@model - _self_ diff --git a/configs/experiment/train/bag_of_embeddings/virchow2.yaml b/configs/experiment/train/bag_of_embeddings/hybrid_virchow2.yaml similarity index 84% rename from configs/experiment/train/bag_of_embeddings/virchow2.yaml rename to configs/experiment/train/bag_of_embeddings/hybrid_virchow2.yaml index 15adeb39..6cb5d81a 100644 --- a/configs/experiment/train/bag_of_embeddings/virchow2.yaml +++ b/configs/experiment/train/bag_of_embeddings/hybrid_virchow2.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/train/bag_of_embeddings/base + - /experiment/train/bag_of_embeddings/hybrid_base - /model/virchow2_hybrid_mil_model@model - _self_ diff --git a/configs/model/pgp_classic_mil_model.yaml b/configs/model/pgp_classic_mil_model.yaml index f9534646..42edbe12 100644 --- a/configs/model/pgp_classic_mil_model.yaml +++ b/configs/model/pgp_classic_mil_model.yaml @@ -1,4 +1,5 @@ _target_: prostate_cancer.classic_mil_model.ProstateCancerClassicMIL foundation: "pgp" lr: ??? +tl_threshold: ??? sl_threshold: ??? diff --git a/configs/model/virchow2_classic_mil_model.yaml b/configs/model/virchow2_classic_mil_model.yaml index 13221fcf..621bca68 100644 --- a/configs/model/virchow2_classic_mil_model.yaml +++ b/configs/model/virchow2_classic_mil_model.yaml @@ -1,4 +1,5 @@ _target_: prostate_cancer.classic_mil_model.ProstateCancerClassicMIL foundation: "virchow2" lr: ??? +tl_threshold: ??? sl_threshold: ??? diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 6005eaa7..ff6aa472 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -7,7 +7,7 @@ from torchmetrics import MetricCollection from prostate_cancer.mil_model_base import ProstateCancerMILBase, binary_metrics -from prostate_cancer.typing import LabeledBagOfTilesSampleBatch, MILModelOutput +from prostate_cancer.typing import LabeledBagOfTilesSampleBatch class ProstateCancerAttentionMIL(ProstateCancerMILBase): @@ -16,7 +16,12 @@ class ProstateCancerAttentionMIL(ProstateCancerMILBase): def __init__( self, foundation: str, lr: float, tl_threshold: float, sl_threshold: float ) -> None: - super().__init__(foundation=foundation, lr=lr, sl_threshold=sl_threshold) + super().__init__( + foundation=foundation, + lr=lr, + sl_threshold=sl_threshold, + tl_threshold=tl_threshold, + ) self.tl_criterion = nn.BCEWithLogitsLoss( reduction="none", pos_weight=torch.tensor([9.65]) @@ -29,7 +34,6 @@ def __init__( self.val_metrics_tl = MetricCollection( deepcopy(tl_metrics), prefix="tl_validation/" ) - self.test_metrics_tl = MetricCollection(deepcopy(tl_metrics), prefix="tl_test/") def training_step(self, batch: LabeledBagOfTilesSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide @@ -101,19 +105,3 @@ def validation_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: self.log_dict( self.val_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) - - def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: # type: ignore[override] - bags, tl_labels, sl_labels, _ = batch - - sl_outputs, tl_outputs, mask, attention = self(bags) - - self.test_metrics_sl.update(sl_outputs, sl_labels) - self.test_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) - - self.log_dict( - self.test_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) - ) - self.log_dict( - self.test_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) - ) - return sl_outputs.sigmoid(), tl_outputs.sigmoid(), mask, attention diff --git a/prostate_cancer/classic_mil_model.py b/prostate_cancer/classic_mil_model.py index 5974ed9a..de883170 100644 --- a/prostate_cancer/classic_mil_model.py +++ b/prostate_cancer/classic_mil_model.py @@ -1,11 +1,17 @@ from torch import Tensor from prostate_cancer.mil_model_base import ProstateCancerMILBase -from prostate_cancer.typing import MILModelOutput, SLLabeledBagOfTilesSampleBatch +from prostate_cancer.typing import SLLabeledBagOfTilesSampleBatch class ProstateCancerClassicMIL(ProstateCancerMILBase): - """Classic MIL: trained only on slide-level (SL) labels, no TL supervision.""" + """Classic MIL: trained only on slide-level (SL) labels, no TL supervision. + + `test_step` (SL + TL metrics) and the architecture are inherited unchanged + from `ProstateCancerMILBase` - TL ground truth is still used to evaluate + the (unsupervised) per-tile classifier at test time, it just never + contributes to the training loss here. + """ def training_step(self, batch: SLLabeledBagOfTilesSampleBatch) -> Tensor: # bag ~ all embeddings from a single slide @@ -35,15 +41,3 @@ def validation_step(self, batch: SLLabeledBagOfTilesSampleBatch) -> None: self.log_dict( self.val_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) ) - - def test_step(self, batch: SLLabeledBagOfTilesSampleBatch) -> MILModelOutput: # type: ignore[override] - bags, sl_labels, _ = batch - - sl_outputs, tl_outputs, mask, attention = self(bags) - - self.test_metrics_sl.update(sl_outputs, sl_labels) - self.log_dict( - self.test_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) - ) - - return sl_outputs.sigmoid(), tl_outputs.sigmoid(), mask, attention diff --git a/prostate_cancer/datamodule/__init__.py b/prostate_cancer/datamodule/__init__.py index b83a1012..d733127a 100644 --- a/prostate_cancer/datamodule/__init__.py +++ b/prostate_cancer/datamodule/__init__.py @@ -1,5 +1,5 @@ -from prostate_cancer.datamodule.bag_of_tiles_data_module import BagOfTilesDataModule -from prostate_cancer.datamodule.classic_bag_of_tiles_data_module import ( +from prostate_cancer.datamodule.bag_of_tiles_data_module import ( + BagOfTilesDataModule, ClassicBagOfTilesDataModule, ) from prostate_cancer.datamodule.tile_data_module import TileDataModule diff --git a/prostate_cancer/datamodule/bag_of_tiles_data_module.py b/prostate_cancer/datamodule/bag_of_tiles_data_module.py index 20bef27a..df3a09d4 100644 --- a/prostate_cancer/datamodule/bag_of_tiles_data_module.py +++ b/prostate_cancer/datamodule/bag_of_tiles_data_module.py @@ -1,5 +1,5 @@ -from abc import ABC, abstractmethod -from collections.abc import Iterable +from abc import ABC +from collections.abc import Callable, Iterable from typing import TYPE_CHECKING, Any, cast import torch @@ -18,19 +18,68 @@ from prostate_cancer.typing import ( LabeledBagOfTilesSample, LabeledBagOfTilesSampleBatch, + SLLabeledBagOfTilesSample, SLLabeledBagOfTilesSampleBatch, UnlabeledBagOfTilesSample, UnlabeledBagOfTilesSampleBatch, ) +def collate_fn_labeled( + batch: list[LabeledBagOfTilesSample], +) -> LabeledBagOfTilesSampleBatch: + inputs = [] + sl_labels = [] + tl_labels = [] + metadatas = [] + for input, sl_label, tl_label, metadata in batch: + inputs.append(input) + sl_labels.append(sl_label) + tl_labels.append(tl_label) + metadatas.append(metadata) + + inputs_tensor = torch.stack(inputs) + sl_labels_tensor = torch.stack(sl_labels) + tl_labels_tensor = torch.stack(tl_labels) + return inputs_tensor, sl_labels_tensor, tl_labels_tensor, metadatas + + +def collate_fn_sl_labeled( + batch: list[SLLabeledBagOfTilesSample], +) -> SLLabeledBagOfTilesSampleBatch: + inputs = [] + sl_labels = [] + metadatas = [] + for input, sl_label, metadata in batch: + inputs.append(input) + sl_labels.append(sl_label) + metadatas.append(metadata) + + inputs_tensor = torch.stack(inputs) + sl_labels_tensor = torch.stack(sl_labels) + return inputs_tensor, sl_labels_tensor, metadatas + + +def collate_fn_unlabeled( + batch: list[UnlabeledBagOfTilesSample], +) -> UnlabeledBagOfTilesSampleBatch: + inputs = [] + metadatas = [] + for input, metadata in batch: + inputs.append(input) + metadatas.append(metadata) + inputs_tensor = torch.stack(inputs) + return inputs_tensor, metadatas + + class BaseBagOfTilesDataModule(LightningDataModule, ABC): - """Shared plumbing for bag-of-tiles (MIL) datamodules. + """Shared plumbing for bag-of-tiles (MIL) datamodules.""" - Subclasses only provide the collate function for labeled samples, since - that is the only part that depends on which labels (SL only, or SL+TL) - the underlying dataset produces. - """ + # set by subclasses to the collate_fn matching their train/val dataset's labels + _collate_train_val: Callable[[list[Any]], Any] + + # test is always evaluated against SL+TL ground truth, regardless of subclass + _collate_test = staticmethod(collate_fn_labeled) def __init__( self, @@ -68,9 +117,6 @@ def setup(self, stage: str) -> None: instantiate(self.datasets["predict"]), ) - @abstractmethod - def _collate_labeled(self, batch: list[Any]) -> Any: ... - def train_dataloader( self, ) -> Iterable[LabeledBagOfTilesSampleBatch | SLLabeledBagOfTilesSampleBatch]: @@ -88,7 +134,7 @@ def train_dataloader( self.train, sampler=sampler, shuffle=shuffle, - collate_fn=self._collate_labeled, + collate_fn=self._collate_train_val, batch_size=self.batch_size, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, @@ -101,18 +147,16 @@ def val_dataloader( return DataLoader( self.val, batch_size=self.batch_size, - collate_fn=self._collate_labeled, + collate_fn=self._collate_train_val, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, ) - def test_dataloader( - self, - ) -> Iterable[LabeledBagOfTilesSampleBatch | SLLabeledBagOfTilesSampleBatch]: + def test_dataloader(self) -> Iterable[LabeledBagOfTilesSampleBatch]: return DataLoader( self.test, batch_size=self.batch_size, - collate_fn=self._collate_labeled, + collate_fn=self._collate_test, num_workers=self.num_workers, persistent_workers=self.num_workers > 0, ) @@ -130,38 +174,10 @@ def predict_dataloader(self) -> Iterable[UnlabeledBagOfTilesSampleBatch]: class BagOfTilesDataModule(BaseBagOfTilesDataModule): """Datamodule for hybrid MIL: labeled samples carry both SL and TL labels.""" - def _collate_labeled( - self, batch: list[LabeledBagOfTilesSample] - ) -> LabeledBagOfTilesSampleBatch: - return collate_fn_labeled(batch) + _collate_train_val = staticmethod(collate_fn_labeled) -def collate_fn_labeled( - batch: list[LabeledBagOfTilesSample], -) -> LabeledBagOfTilesSampleBatch: - inputs = [] - sl_labels = [] - tl_labels = [] - metadatas = [] - for input, sl_label, tl_label, metadata in batch: - inputs.append(input) - sl_labels.append(sl_label) - tl_labels.append(tl_label) - metadatas.append(metadata) +class ClassicBagOfTilesDataModule(BaseBagOfTilesDataModule): + """Datamodule for classic MIL: train/val only carry SL labels.""" - inputs_tensor = torch.stack(inputs) - sl_labels_tensor = torch.stack(sl_labels) - tl_labels_tensor = torch.stack(tl_labels) - return inputs_tensor, sl_labels_tensor, tl_labels_tensor, metadatas - - -def collate_fn_unlabeled( - batch: list[UnlabeledBagOfTilesSample], -) -> UnlabeledBagOfTilesSampleBatch: - inputs = [] - metadatas = [] - for input, metadata in batch: - inputs.append(input) - metadatas.append(metadata) - inputs_tensor = torch.stack(inputs) - return inputs_tensor, metadatas + _collate_train_val = staticmethod(collate_fn_sl_labeled) diff --git a/prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py b/prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py deleted file mode 100644 index 03cc44cf..00000000 --- a/prostate_cancer/datamodule/classic_bag_of_tiles_data_module.py +++ /dev/null @@ -1,34 +0,0 @@ -import torch - -from prostate_cancer.datamodule.bag_of_tiles_data_module import ( - BaseBagOfTilesDataModule, -) -from prostate_cancer.typing import ( - SLLabeledBagOfTilesSample, - SLLabeledBagOfTilesSampleBatch, -) - - -class ClassicBagOfTilesDataModule(BaseBagOfTilesDataModule): - """Datamodule for classic MIL: labeled samples only carry SL labels.""" - - def _collate_labeled( - self, batch: list[SLLabeledBagOfTilesSample] - ) -> SLLabeledBagOfTilesSampleBatch: - return collate_fn_sl_labeled(batch) - - -def collate_fn_sl_labeled( - batch: list[SLLabeledBagOfTilesSample], -) -> SLLabeledBagOfTilesSampleBatch: - inputs = [] - sl_labels = [] - metadatas = [] - for input, sl_label, metadata in batch: - inputs.append(input) - sl_labels.append(sl_label) - metadatas.append(metadata) - - inputs_tensor = torch.stack(inputs) - sl_labels_tensor = torch.stack(sl_labels) - return inputs_tensor, sl_labels_tensor, metadatas diff --git a/prostate_cancer/mil_model_base.py b/prostate_cancer/mil_model_base.py index 2eb23a6b..8424bc02 100644 --- a/prostate_cancer/mil_model_base.py +++ b/prostate_cancer/mil_model_base.py @@ -17,7 +17,11 @@ Specificity, ) -from prostate_cancer.typing import MILModelOutput, UnlabeledBagOfTilesSampleBatch +from prostate_cancer.typing import ( + LabeledBagOfTilesSampleBatch, + MILModelOutput, + UnlabeledBagOfTilesSampleBatch, +) def binary_metrics(threshold: float) -> dict[str, Metric | MetricCollection]: @@ -37,12 +41,17 @@ class ProstateCancerMILBase(LightningModule): """Attention-MIL architecture shared by hybrid (SL+TL) and classic (SL-only) models. The bag encoder/attention/classifier and the forward pass are identical - regardless of which labels supervise training - subclasses only differ in - which labels they consume in `training_step`/`validation_step`/`test_step` - and which losses/metrics they compute from them. + regardless of which labels supervise training. Both hybrid and classic + models are evaluated at test time against SL *and* TL ground truth (TL + metrics show how well the per-tile classifier localizes carcinoma even + when, for the classic model, it was never directly supervised on TL + labels) so `test_step` is shared here too. Subclasses only differ in + which labels drive `training_step`/`validation_step`. """ - def __init__(self, foundation: str, lr: float, sl_threshold: float) -> None: + def __init__( + self, foundation: str, lr: float, sl_threshold: float, tl_threshold: float + ) -> None: super().__init__() match foundation: case "pgp": @@ -80,6 +89,11 @@ def __init__(self, foundation: str, lr: float, sl_threshold: float) -> None: ) self.test_metrics_sl = MetricCollection(deepcopy(sl_metrics), prefix="sl_test/") + # TL is only ever evaluated (not necessarily trained on) - see class docstring + self.test_metrics_tl = MetricCollection( + deepcopy(binary_metrics(tl_threshold)), prefix="tl_test/" + ) + def forward(self, x: Tensor) -> MILModelOutput: # x has shape (batch_size, num_tiles_padded, embedding_dim) @@ -118,6 +132,22 @@ def forward(self, x: Tensor) -> MILModelOutput: attention_weights.squeeze(-1), ) # (batch_size,), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded), (batch_size, num_tiles_padded) + def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: # type: ignore[override] + bags, tl_labels, sl_labels, _ = batch + + sl_outputs, tl_outputs, mask, attention = self(bags) + + self.test_metrics_sl.update(sl_outputs, sl_labels) + self.test_metrics_tl.update(tl_outputs[mask.bool()], tl_labels[mask.bool()]) + + self.log_dict( + self.test_metrics_sl, on_epoch=True, on_step=False, batch_size=len(bags) + ) + self.log_dict( + self.test_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) + ) + return sl_outputs.sigmoid(), tl_outputs.sigmoid(), mask, attention + def predict_step(self, batch: UnlabeledBagOfTilesSampleBatch) -> MILModelOutput: sl_preds_raw, tl_preds_raw, mask, attention = self(batch[0]) return sl_preds_raw.sigmoid(), tl_preds_raw.sigmoid(), mask, attention From c6128110dd638d8f1efc0a2aaa347a85f3a5a965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 23 Jul 2026 16:49:15 +0000 Subject: [PATCH 259/284] feat: log full checkpoint --- prostate_cancer/callbacks/curves_callback_base.py | 2 ++ prostate_cancer/callbacks/nested_metrics_callback_mil.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/prostate_cancer/callbacks/curves_callback_base.py b/prostate_cancer/callbacks/curves_callback_base.py index e4077e2e..c8173da6 100644 --- a/prostate_cancer/callbacks/curves_callback_base.py +++ b/prostate_cancer/callbacks/curves_callback_base.py @@ -67,6 +67,7 @@ def _plot_roc( "lower right", ) mlflow.log_artifact(plot_path, artifact_path="plots") + mlflow.log_param("j_threshold", j_threshold) def _plot_precision_recall( self, y_pred: NDArray[np.float32], y_true: NDArray[np.float32] @@ -105,6 +106,7 @@ def _plot_precision_recall( "lower left", ) mlflow.log_artifact(plot_path, artifact_path="plots") + mlflow.log_param("pr_threshold", best_threshold) def _plot_and_clear(self) -> None: y_pred = torch.cat(self.preds).numpy() diff --git a/prostate_cancer/callbacks/nested_metrics_callback_mil.py b/prostate_cancer/callbacks/nested_metrics_callback_mil.py index 8710327e..954b1374 100644 --- a/prostate_cancer/callbacks/nested_metrics_callback_mil.py +++ b/prostate_cancer/callbacks/nested_metrics_callback_mil.py @@ -31,4 +31,6 @@ def on_test_batch_end( ] # Update slide-level metrics - self.nested_test_metrics.update(tl_outputs_valid.cpu(), targets_valid.cpu(), keys) + self.nested_test_metrics.update( + tl_outputs_valid.cpu(), targets_valid.cpu(), keys + ) From 895d5f44fbc8f005e6ca536babbece37ddea4cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 25 Jul 2026 06:56:56 +0000 Subject: [PATCH 260/284] feat: high prec thr --- configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml | 2 +- configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml index 90804e6b..4c5b99d9 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml @@ -3,7 +3,7 @@ defaults: - _self_ tile_threshold: 0.88 -slide_threshold: 0.98 +slide_threshold: 0.9783778786 checkpoint: "mlflow-artifacts:/65/7cb992adfe4b422790becc5984ff2a6f/artifacts/checkpoints/epoch=8-step=1583/checkpoint.ckpt" model_name: "PGP (Hybrid-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml index c0b0946a..11f36170 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml @@ -3,7 +3,7 @@ defaults: - _self_ tile_threshold: 0.85 -slide_threshold: 0.99 +slide_threshold: 0.9917988777 checkpoint: "mlflow-artifacts:/65/5e0c1d4699ca49c4938baf3c65e7f61c/artifacts/checkpoints/epoch=6-step=1176/checkpoint.ckpt" model_name: "Virchow2 (Hybrid-MIL)" From 9484266ba61aff3df6378118a84886796abc00a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Sat, 25 Jul 2026 11:00:20 +0000 Subject: [PATCH 261/284] feat: logging --- prostate_cancer/callbacks/curves_callback_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prostate_cancer/callbacks/curves_callback_base.py b/prostate_cancer/callbacks/curves_callback_base.py index c8173da6..ea8ac982 100644 --- a/prostate_cancer/callbacks/curves_callback_base.py +++ b/prostate_cancer/callbacks/curves_callback_base.py @@ -45,6 +45,7 @@ def _plot_roc( j = tpr - fpr optimal_idx = j.argmax() j_threshold = roc_thresholds[optimal_idx] + mlflow.log_param("j_threshold", j_threshold) j_fpr = fpr[optimal_idx] j_tpr = tpr[optimal_idx] @@ -67,7 +68,6 @@ def _plot_roc( "lower right", ) mlflow.log_artifact(plot_path, artifact_path="plots") - mlflow.log_param("j_threshold", j_threshold) def _plot_precision_recall( self, y_pred: NDArray[np.float32], y_true: NDArray[np.float32] @@ -86,6 +86,7 @@ def _plot_precision_recall( f1 = 2 * (precision * recall) / (precision + recall + 1e-8) best_idx = np.argmax(f1) best_threshold = thresholds[best_idx] + mlflow.log_param("pr_threshold", best_threshold) to_pinpoint.append((recall[best_idx], precision[best_idx])) labels.append(f"F1 Threshold = {best_threshold:.2f}") @@ -106,7 +107,6 @@ def _plot_precision_recall( "lower left", ) mlflow.log_artifact(plot_path, artifact_path="plots") - mlflow.log_param("pr_threshold", best_threshold) def _plot_and_clear(self) -> None: y_pred = torch.cat(self.preds).numpy() From 7a8778604b2350512f00c7194086e224660a2c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 06:20:14 +0000 Subject: [PATCH 262/284] fix: flatten index --- .../datasets/bag_of_embeddings_dataset.py | 10 ++++++++-- prostate_cancer/typing.py | 16 +++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py index fb024831..02133ab9 100644 --- a/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py +++ b/prostate_cancer/datamodule/datasets/bag_of_embeddings_dataset.py @@ -44,7 +44,13 @@ def __init__( ) -> None: self._meta = SlidesTilesLoader(uris=uris) self.slides = self._meta.slides - self.tiles = self._meta.tiles + + # tiles are loaded from many sharded parquet files and concatenated, + # leaving a fragmented backing table; flatten_indices() rewrites it + # into one contiguous Arrow file so filter_tiles_by_slide()'s + # per-sample .select() isn't gathering across hundreds of shards + self.tiles = self._meta.tiles.flatten_indices() + self._meta.tiles = self.tiles self.padding = padding @@ -155,7 +161,7 @@ def __getitem__(self, idx: int) -> LabeledBagOfTilesSample: sl_label = torch.tensor(slide_metadata["carcinoma"]).float() - tl_labels = torch.zeros(len(slide_embeddings)).float() # pad with zero labels + tl_labels = torch.zeros(len(slide_embeddings)).float() # pad with zero labels tl_labels[: len(slide_tiles)] = torch.tensor(slide_tiles["carcinoma"]).float() return slide_embeddings, tl_labels, sl_label, metadata diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index f9468abb..baa94020 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -33,11 +33,17 @@ class TileMetadataBatch(TypedDict): y: Tensor -LabeledTileSample: TypeAlias = tuple[Tensor, Tensor, TileMetadata] # Image | label | Metadata -UnlabeledTileSample: TypeAlias = tuple[Tensor, TileMetadata] # Image | Metadata - -LabeledTileSampleBatch: TypeAlias = tuple[Tensor, Tensor, TileMetadataBatch] # Images | labels | Metadata -UnlabeledTileSampleBatch: TypeAlias = tuple[Tensor, TileMetadataBatch] # Images | labels | Metadata +LabeledTileSample: TypeAlias = tuple[ + Tensor, Tensor, TileMetadata +] # Image | label | Metadata +UnlabeledTileSample: TypeAlias = tuple[Tensor, TileMetadata] # Image | Metadata + +LabeledTileSampleBatch: TypeAlias = tuple[ + Tensor, Tensor, TileMetadataBatch +] # Images | labels | Metadata +UnlabeledTileSampleBatch: TypeAlias = tuple[ + Tensor, TileMetadataBatch +] # Images | labels | Metadata # how does slide metadata in bag dataset look like From ba15d7709c1d649444f022a7666ecdf7783a3ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 06:46:46 +0000 Subject: [PATCH 263/284] fix: lint --- preprocessing/embeddings/tile_embeddings.py | 2 +- preprocessing/masks/quality_control_v2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/preprocessing/embeddings/tile_embeddings.py b/preprocessing/embeddings/tile_embeddings.py index 1ba54a2f..50057449 100644 --- a/preprocessing/embeddings/tile_embeddings.py +++ b/preprocessing/embeddings/tile_embeddings.py @@ -86,7 +86,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: slide_embeddings[start:end] = embeddings save_embeddings(slide_embeddings, partition, slide_name, dest) - except Exception as e: + except Exception as e: # noqa: BLE001 print(f"{e} occured during processing {slide_name}") logger.log_artifacts(local_dir=config.output_path) diff --git a/preprocessing/masks/quality_control_v2.py b/preprocessing/masks/quality_control_v2.py index e97d1cee..804118cc 100644 --- a/preprocessing/masks/quality_control_v2.py +++ b/preprocessing/masks/quality_control_v2.py @@ -70,7 +70,7 @@ async def qc_main( total=len(slides), ): if not result.success: - with open(output_path / "qc_errors.log", "a") as log_file: + with open(output_path / "qc_errors.log", "a") as log_file: # noqa: ASYNC230 log_file.write( f"Failed to process {result.wsi_path}: {result.error}\n" ) From 10f8b01b133eb4b81302f922e48d3c05963abbaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 06:50:41 +0000 Subject: [PATCH 264/284] fix: format --- preprocessing/embeddings/tile_embeddings.py | 2 +- preprocessing/masks/quality_control_v2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/preprocessing/embeddings/tile_embeddings.py b/preprocessing/embeddings/tile_embeddings.py index 50057449..b79dc315 100644 --- a/preprocessing/embeddings/tile_embeddings.py +++ b/preprocessing/embeddings/tile_embeddings.py @@ -86,7 +86,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: slide_embeddings[start:end] = embeddings save_embeddings(slide_embeddings, partition, slide_name, dest) - except Exception as e: # noqa: BLE001 + except Exception as e: # noqa: BLE001 print(f"{e} occured during processing {slide_name}") logger.log_artifacts(local_dir=config.output_path) diff --git a/preprocessing/masks/quality_control_v2.py b/preprocessing/masks/quality_control_v2.py index 804118cc..6282b13a 100644 --- a/preprocessing/masks/quality_control_v2.py +++ b/preprocessing/masks/quality_control_v2.py @@ -70,7 +70,7 @@ async def qc_main( total=len(slides), ): if not result.success: - with open(output_path / "qc_errors.log", "a") as log_file: # noqa: ASYNC230 + with open(output_path / "qc_errors.log", "a") as log_file: # noqa: ASYNC230 log_file.write( f"Failed to process {result.wsi_path}: {result.error}\n" ) From a0b79b74d1801eb2d93900269d9334499634106e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 06:53:50 +0000 Subject: [PATCH 265/284] fix: mypy --- preprocessing/embeddings/tile_embeddings.py | 2 +- preprocessing/masks/quality_control_v2.py | 2 +- prostate_cancer/attention_mil_model.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/preprocessing/embeddings/tile_embeddings.py b/preprocessing/embeddings/tile_embeddings.py index b79dc315..1ba54a2f 100644 --- a/preprocessing/embeddings/tile_embeddings.py +++ b/preprocessing/embeddings/tile_embeddings.py @@ -86,7 +86,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: slide_embeddings[start:end] = embeddings save_embeddings(slide_embeddings, partition, slide_name, dest) - except Exception as e: # noqa: BLE001 + except Exception as e: print(f"{e} occured during processing {slide_name}") logger.log_artifacts(local_dir=config.output_path) diff --git a/preprocessing/masks/quality_control_v2.py b/preprocessing/masks/quality_control_v2.py index 6282b13a..e97d1cee 100644 --- a/preprocessing/masks/quality_control_v2.py +++ b/preprocessing/masks/quality_control_v2.py @@ -70,7 +70,7 @@ async def qc_main( total=len(slides), ): if not result.success: - with open(output_path / "qc_errors.log", "a") as log_file: # noqa: ASYNC230 + with open(output_path / "qc_errors.log", "a") as log_file: log_file.write( f"Failed to process {result.wsi_path}: {result.error}\n" ) diff --git a/prostate_cancer/attention_mil_model.py b/prostate_cancer/attention_mil_model.py index 189eb533..dd424281 100644 --- a/prostate_cancer/attention_mil_model.py +++ b/prostate_cancer/attention_mil_model.py @@ -203,7 +203,7 @@ def validation_step(self, batch: LabeledBagOfTilesSampleBatch) -> None: self.val_metrics_tl, on_epoch=True, on_step=False, batch_size=len(bags) ) - def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: + def test_step(self, batch: LabeledBagOfTilesSampleBatch) -> MILModelOutput: # type: ignore[override] bags, tl_labels, sl_labels, _ = batch sl_outputs, tl_outputs, mask, attention = self(bags) From a0836c02a232f98a387fd02da298ec1e2c8c9ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 06:56:53 +0000 Subject: [PATCH 266/284] fix: lint --- preprocessing/embeddings/tile_embeddings.py | 2 +- preprocessing/masks/quality_control_v2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/preprocessing/embeddings/tile_embeddings.py b/preprocessing/embeddings/tile_embeddings.py index 1ba54a2f..b79dc315 100644 --- a/preprocessing/embeddings/tile_embeddings.py +++ b/preprocessing/embeddings/tile_embeddings.py @@ -86,7 +86,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: slide_embeddings[start:end] = embeddings save_embeddings(slide_embeddings, partition, slide_name, dest) - except Exception as e: + except Exception as e: # noqa: BLE001 print(f"{e} occured during processing {slide_name}") logger.log_artifacts(local_dir=config.output_path) diff --git a/preprocessing/masks/quality_control_v2.py b/preprocessing/masks/quality_control_v2.py index e97d1cee..6282b13a 100644 --- a/preprocessing/masks/quality_control_v2.py +++ b/preprocessing/masks/quality_control_v2.py @@ -70,7 +70,7 @@ async def qc_main( total=len(slides), ): if not result.success: - with open(output_path / "qc_errors.log", "a") as log_file: + with open(output_path / "qc_errors.log", "a") as log_file: # noqa: ASYNC230 log_file.write( f"Failed to process {result.wsi_path}: {result.error}\n" ) From 0e43ea889eb77dd330378a014a2a93914919e677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 07:01:05 +0000 Subject: [PATCH 267/284] fix: mypy --- prostate_cancer/callbacks/mil_prediction_callback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prostate_cancer/callbacks/mil_prediction_callback.py b/prostate_cancer/callbacks/mil_prediction_callback.py index 8a3e0b15..9e847262 100644 --- a/prostate_cancer/callbacks/mil_prediction_callback.py +++ b/prostate_cancer/callbacks/mil_prediction_callback.py @@ -122,7 +122,7 @@ def on_test_batch_end( self, trainer: Trainer, pl_module: LightningModule, - outputs: MILModelOutput, + outputs: MILModelOutput, # type: ignore[override] batch: LabeledBagOfTilesSampleBatch, batch_idx: int, dataloader_idx: int = 0, From 9ec0115d19dc01849b62d7ab88bc3ba2bb824110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 07:21:27 +0000 Subject: [PATCH 268/284] feat: resolve comment --- .../callbacks/curves_callback_base.py | 18 +++++++++++++----- .../callbacks/curves_callback_mil.py | 5 +++++ .../callbacks/curves_callback_sl.py | 5 +++++ .../callbacks/curves_callback_tl.py | 5 +++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/prostate_cancer/callbacks/curves_callback_base.py b/prostate_cancer/callbacks/curves_callback_base.py index ea8ac982..77155eb2 100644 --- a/prostate_cancer/callbacks/curves_callback_base.py +++ b/prostate_cancer/callbacks/curves_callback_base.py @@ -11,14 +11,18 @@ class CurvesCallbackBase(Callback, ABC): - def __init__(self, threshold: float, optimal_seek: bool = True) -> None: - """This callback creates tile-level ROC curve and Precision-Recall curve and marks selected + optimized thresholds used for metric computation. + def __init__( + self, threshold: float, tile_level: bool, optimal_seek: bool = True + ) -> None: + """This callback creates tile-level or slide-level ROC curve and Precision-Recall curve and marks selected + optimized thresholds used for metric computation. Args: threshold (float): pathologist selected threshold + tile_level (bool): whether the curves are computed on tile-level or slide-level predictions optimal_seek (bool): whether we are looking for optimal thresholds or just want to plot the curves """ super().__init__() + self.tile_level = tile_level self.optimal_seek = optimal_seek self.threshold = threshold self.preds: list[torch.Tensor] = [] @@ -53,7 +57,7 @@ def _plot_roc( labels.append(f"J Threshold = {j_threshold:.2f}") colors.append("green") - plot_path = "tile_roc.png" + plot_path = "tile_roc.png" if self.tile_level else "slide_roc.png" _plot_curve( fpr, tpr, @@ -92,7 +96,11 @@ def _plot_precision_recall( labels.append(f"F1 Threshold = {best_threshold:.2f}") colors.append("green") - plot_path = "tile_precision_recall.png" + plot_path = ( + "tile_precision_recall.png" + if self.tile_level + else "slide_precision_recall.png" + ) _plot_curve( recall, precision, @@ -103,7 +111,7 @@ def _plot_precision_recall( "Recall", "Precision", "Precision-Recall Curve", - "tile_precision_recall.png", + plot_path, "lower left", ) mlflow.log_artifact(plot_path, artifact_path="plots") diff --git a/prostate_cancer/callbacks/curves_callback_mil.py b/prostate_cancer/callbacks/curves_callback_mil.py index 7124a2db..ae3ad927 100644 --- a/prostate_cancer/callbacks/curves_callback_mil.py +++ b/prostate_cancer/callbacks/curves_callback_mil.py @@ -5,6 +5,11 @@ class CurvesCallbackMIL(CurvesCallbackBase): + def __init__(self, threshold: float, optimal_seek: bool) -> None: + super().__init__( + threshold=threshold, tile_level=True, optimal_seek=optimal_seek + ) + def on_test_batch_end( self, trainer: Trainer, diff --git a/prostate_cancer/callbacks/curves_callback_sl.py b/prostate_cancer/callbacks/curves_callback_sl.py index 704adb07..9a2bca89 100644 --- a/prostate_cancer/callbacks/curves_callback_sl.py +++ b/prostate_cancer/callbacks/curves_callback_sl.py @@ -12,6 +12,11 @@ class CurvesCallbackSL(CurvesCallbackBase): + def __init__(self, threshold: float, optimal_seek: bool) -> None: + super().__init__( + threshold=threshold, tile_level=False, optimal_seek=optimal_seek + ) + def setup( self, trainer: Trainer, pl_module: LightningModule, stage: str | None = None ) -> None: diff --git a/prostate_cancer/callbacks/curves_callback_tl.py b/prostate_cancer/callbacks/curves_callback_tl.py index e8223a9a..51b31bb3 100644 --- a/prostate_cancer/callbacks/curves_callback_tl.py +++ b/prostate_cancer/callbacks/curves_callback_tl.py @@ -7,6 +7,11 @@ class CurvesCallbackTile(CurvesCallbackBase): + def __init__(self, threshold: float, optimal_seek: bool) -> None: + super().__init__( + threshold=threshold, tile_level=True, optimal_seek=optimal_seek + ) + def on_test_batch_end( self, trainer: Trainer, From 8bca591ca9e1ee61629191f12885e280117cf38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 16:51:19 +0000 Subject: [PATCH 269/284] feat: public models --- configs/model/checkpoints/mmci_tl/resnet50_224.yaml | 2 +- configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml | 2 +- configs/model/checkpoints/mmci_tl/vgg16_224.yaml | 2 +- configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/model/checkpoints/mmci_tl/resnet50_224.yaml b/configs/model/checkpoints/mmci_tl/resnet50_224.yaml index efa9c5b4..d044e606 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_224.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_224.yaml @@ -9,7 +9,7 @@ slide_top_k_threshold: 0.9961571097 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/d9bd39a0207c4b9484ad92574c50b503/artifacts/checkpoints/epoch=0-step=13889/checkpoint.ckpt" -public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_224_checkpoint.ckpt" model_name: "ResNet50 (224x224)" slide_level_prediction_uris: diff --git a/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml b/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml index f59017ca..03de76bf 100644 --- a/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml +++ b/configs/model/checkpoints/mmci_tl/resnet50_stain_224.yaml @@ -9,7 +9,7 @@ slide_top_k_threshold: 0.9998824596 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/80543b4ec2d14ca48384e6d52a7418fb/artifacts/checkpoints/epoch=0-step=55556/checkpoint.ckpt" -public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_stain_checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/resnet50_stain_224_checkpoint.ckpt" model_name: "ResNet50 Stain (224x224)" slide_level_prediction_uris: diff --git a/configs/model/checkpoints/mmci_tl/vgg16_224.yaml b/configs/model/checkpoints/mmci_tl/vgg16_224.yaml index 993ba92f..a7d55962 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_224.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_224.yaml @@ -9,7 +9,7 @@ slide_top_k_threshold: 0.9999379516 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/2b4986681bb24e1abd85a9108a7a49bb/artifacts/checkpoints/epoch=1-step=97226/checkpoint.ckpt" -public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_224_checkpoint.ckpt" model_name: "VGG-16 (224x224)" slide_level_prediction_uris: diff --git a/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml b/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml index 912791ca..b27988d8 100644 --- a/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml +++ b/configs/model/checkpoints/mmci_tl/vgg16_stain_224.yaml @@ -9,7 +9,7 @@ slide_top_k_threshold: 0.9988395572 slide_span_threshold: 0.5 # arbitrary - span predictions are binary checkpoint: "mlflow-artifacts:/65/98e0d43027664084aef1beebaeac7fff/artifacts/checkpoints/epoch=1-step=111115/checkpoint.ckpt" -public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_stain_checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/vgg16_stain_224_checkpoint.ckpt" model_name: "VGG-16 Stain (224x224)" From 0be2892e9f1b3e70c225b7fdac0a59e664b75dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Mon, 27 Jul 2026 16:55:52 +0000 Subject: [PATCH 270/284] feat: cnn eval radboud --- .../test/images/resnet50/test_suite/radboud_test.yaml | 8 ++++++++ .../test/images/resnet50/test_suite/radboud_test_224.yaml | 8 ++++++++ .../images/resnet50/test_suite/stain_radboud_test.yaml | 8 ++++++++ .../resnet50/test_suite/stain_radboud_test_224.yaml | 8 ++++++++ .../test/images/vgg16/test_suite/radboud_test_224.yaml | 8 ++++++++ .../test/images/vgg16/test_suite/stain_radboud_test.yaml | 8 ++++++++ .../images/vgg16/test_suite/stain_radboud_test_224.yaml | 8 ++++++++ 7 files changed, 56 insertions(+) create mode 100644 configs/experiment/test/images/resnet50/test_suite/radboud_test.yaml create mode 100644 configs/experiment/test/images/resnet50/test_suite/radboud_test_224.yaml create mode 100644 configs/experiment/test/images/resnet50/test_suite/stain_radboud_test.yaml create mode 100644 configs/experiment/test/images/resnet50/test_suite/stain_radboud_test_224.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/radboud_test_224.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/stain_radboud_test.yaml create mode 100644 configs/experiment/test/images/vgg16/test_suite/stain_radboud_test_224.yaml diff --git a/configs/experiment/test/images/resnet50/test_suite/radboud_test.yaml b/configs/experiment/test/images/resnet50/test_suite/radboud_test.yaml new file mode 100644 index 00000000..dd61d2f0 --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/radboud_test.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/resnet50/test_suite/base + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/resnet50/test_suite/radboud_test_224.yaml b/configs/experiment/test/images/resnet50/test_suite/radboud_test_224.yaml new file mode 100644 index 00000000..8ff63dea --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/radboud_test_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/resnet50/test_suite/base_224 + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/resnet50/test_suite/stain_radboud_test.yaml b/configs/experiment/test/images/resnet50/test_suite/stain_radboud_test.yaml new file mode 100644 index 00000000..6b7bd7e8 --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/stain_radboud_test.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/resnet50/test_suite/stain_base + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/resnet50/test_suite/stain_radboud_test_224.yaml b/configs/experiment/test/images/resnet50/test_suite/stain_radboud_test_224.yaml new file mode 100644 index 00000000..4f2c2959 --- /dev/null +++ b/configs/experiment/test/images/resnet50/test_suite/stain_radboud_test_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/resnet50/test_suite/stain_base_224 + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/vgg16/test_suite/radboud_test_224.yaml b/configs/experiment/test/images/vgg16/test_suite/radboud_test_224.yaml new file mode 100644 index 00000000..0a5a9a6a --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/radboud_test_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/vgg16/test_suite/base_224 + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/vgg16/test_suite/stain_radboud_test.yaml b/configs/experiment/test/images/vgg16/test_suite/stain_radboud_test.yaml new file mode 100644 index 00000000..db1a9b4d --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/stain_radboud_test.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/vgg16/test_suite/stain_base + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/images/vgg16/test_suite/stain_radboud_test_224.yaml b/configs/experiment/test/images/vgg16/test_suite/stain_radboud_test_224.yaml new file mode 100644 index 00000000..e7e6e754 --- /dev/null +++ b/configs/experiment/test/images/vgg16/test_suite/stain_radboud_test_224.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /experiment/test/images/vgg16/test_suite/stain_base_224 + - /data/radboud/test@test_data + - _self_ + +checkpoint: ${model_data.public_checkpoint} From dbb53e626fe4a2fcfaaac87b0cd76d7ead361150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 28 Jul 2026 07:54:10 +0000 Subject: [PATCH 271/284] feat: radboud aggr eval --- .../embeddings/pgp/multi_aggregator_eval/radboud_test.yaml | 6 ++++++ .../pgp/multi_aggregator_eval/stain_radboud_test.yaml | 6 ++++++ .../virchow2/multi_aggregator_eval/radboud_test.yaml | 6 ++++++ .../virchow2/multi_aggregator_eval/stain_radboud_test.yaml | 6 ++++++ .../google_vit/multi_aggregator_eval/radboud_test.yaml | 6 ++++++ .../multi_aggregator_eval/stain_radboud_test.yaml | 6 ++++++ .../images/resnet50/multi_aggregator_eval/radboud_test.yaml | 6 ++++++ .../resnet50/multi_aggregator_eval/radboud_test_224.yaml | 6 ++++++ .../resnet50/multi_aggregator_eval/stain_radboud_test.yaml | 6 ++++++ .../multi_aggregator_eval/stain_radboud_test_224.yaml | 6 ++++++ .../images/vgg16/multi_aggregator_eval/radboud_test.yaml | 6 ++++++ .../vgg16/multi_aggregator_eval/radboud_test_224.yaml | 6 ++++++ .../vgg16/multi_aggregator_eval/stain_radboud_test.yaml | 6 ++++++ .../vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml | 6 ++++++ 14 files changed, 84 insertions(+) create mode 100644 configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml create mode 100644 configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml create mode 100644 configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml create mode 100644 configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml create mode 100644 configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml create mode 100644 configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml create mode 100644 configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml new file mode 100644 index 00000000..dc9316eb --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/multi_aggregator_eval/base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml new file mode 100644 index 00000000..89a98a94 --- /dev/null +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml new file mode 100644 index 00000000..d3df5d67 --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/virchow2/multi_aggregator_eval/base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml new file mode 100644 index 00000000..8fb04c4b --- /dev/null +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml new file mode 100644 index 00000000..18456817 --- /dev/null +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/google_vit/multi_aggregator_eval/base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml new file mode 100644 index 00000000..202aa9da --- /dev/null +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/google_vit/multi_aggregator_eval/stain_base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml new file mode 100644 index 00000000..95609737 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml new file mode 100644 index 00000000..8cf0256a --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/base_224 + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml new file mode 100644 index 00000000..9e5272db --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/stain_base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml new file mode 100644 index 00000000..51fe3c66 --- /dev/null +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/resnet50/multi_aggregator_eval/stain_base_224 + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml new file mode 100644 index 00000000..a710d883 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml new file mode 100644 index 00000000..daf2b323 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/base_224 + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml new file mode 100644 index 00000000..52b4b030 --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/stain_base + - /data/radboud/test@predict_data + - _self_ diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml new file mode 100644 index 00000000..cf82ab9d --- /dev/null +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224 + - /data/radboud/test@predict_data + - _self_ From 1e2818e8e5501f50d061eed39dda0dcd0b969b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 28 Jul 2026 08:32:23 +0000 Subject: [PATCH 272/284] feat: public checkpoints --- .../embeddings/pgp/multi_aggregator_eval/radboud_test.yaml | 2 ++ .../pgp/multi_aggregator_eval/stain_radboud_test.yaml | 2 ++ .../embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml | 2 ++ .../virchow2/multi_aggregator_eval/stain_radboud_test.yaml | 2 ++ .../images/google_vit/multi_aggregator_eval/radboud_test.yaml | 2 ++ .../google_vit/multi_aggregator_eval/stain_radboud_test.yaml | 2 ++ .../images/resnet50/multi_aggregator_eval/radboud_test.yaml | 2 ++ .../images/resnet50/multi_aggregator_eval/radboud_test_224.yaml | 2 ++ .../resnet50/multi_aggregator_eval/stain_radboud_test.yaml | 2 ++ .../resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml | 2 ++ .../images/vgg16/multi_aggregator_eval/radboud_test.yaml | 2 ++ .../images/vgg16/multi_aggregator_eval/radboud_test_224.yaml | 2 ++ .../images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml | 2 ++ .../vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml | 2 ++ 14 files changed, 28 insertions(+) diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml index dc9316eb..bd7323c9 100644 --- a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/embeddings/pgp/multi_aggregator_eval/base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml index 89a98a94..dcbcc96d 100644 --- a/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml +++ b/configs/experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/embeddings/pgp/multi_aggregator_eval/stain_base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml index d3df5d67..02478538 100644 --- a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/embeddings/virchow2/multi_aggregator_eval/base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml index 8fb04c4b..d994a82a 100644 --- a/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml +++ b/configs/experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/embeddings/virchow2/multi_aggregator_eval/stain_base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml index 18456817..a6875770 100644 --- a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/google_vit/multi_aggregator_eval/base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml index 202aa9da..08800bdc 100644 --- a/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml +++ b/configs/experiment/predict/images/google_vit/multi_aggregator_eval/stain_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/google_vit/multi_aggregator_eval/stain_base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml index 95609737..b76dbcc4 100644 --- a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/resnet50/multi_aggregator_eval/base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml index 8cf0256a..55ffa8cd 100644 --- a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/radboud_test_224.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/resnet50/multi_aggregator_eval/base_224 - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml index 9e5272db..f8eb8f19 100644 --- a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/resnet50/multi_aggregator_eval/stain_base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml index 51fe3c66..e9346f2a 100644 --- a/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml +++ b/configs/experiment/predict/images/resnet50/multi_aggregator_eval/stain_radboud_test_224.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/resnet50/multi_aggregator_eval/stain_base_224 - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml index a710d883..93ad0bac 100644 --- a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/vgg16/multi_aggregator_eval/base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml index daf2b323..47512064 100644 --- a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/radboud_test_224.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/vgg16/multi_aggregator_eval/base_224 - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml index 52b4b030..95828140 100644 --- a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/vgg16/multi_aggregator_eval/stain_base - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml index cf82ab9d..722914ec 100644 --- a/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml +++ b/configs/experiment/predict/images/vgg16/multi_aggregator_eval/stain_radboud_test_224.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/predict/images/vgg16/multi_aggregator_eval/stain_base_224 - /data/radboud/test@predict_data - _self_ + +checkpoint: ${model_data.public_checkpoint} From fe851937612611d41a05bd21ec1451662292ef71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 28 Jul 2026 17:41:29 +0000 Subject: [PATCH 273/284] feat: classic v th est --- configs/experiment/test/bag_of_embeddings/base.yaml | 1 - .../test/bag_of_embeddings/classic_base.yaml | 6 ++++++ .../test/bag_of_embeddings/hybrid_base.yaml | 6 ++++++ .../pgp/{base.yaml => hybrid_base.yaml} | 2 +- .../hybrid_tile_threshold.yaml} | 2 +- .../pgp/test_suite/{base.yaml => hybrid_base.yaml} | 2 +- .../test_suite/hybrid_mmci_tl_test.yaml} | 2 +- .../bag_of_embeddings/virchow2/classic_base.yaml | 12 ++++++++++++ .../virchow2/classic_tile_threshold.yaml | 10 ++++++++++ .../virchow2/{base.yaml => hybrid_base.yaml} | 2 +- .../hybrid_tile_threshold.yaml} | 2 +- .../test_suite/{base.yaml => hybrid_base.yaml} | 2 +- .../test_suite/hybrid_mmci_tl_test.yaml} | 2 +- .../model/checkpoints/mmci_tl/pgp_classic_mil.yaml | 9 +++++++++ .../checkpoints/mmci_tl/virchow2_classic_mil.yaml | 9 +++++++++ 15 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 configs/experiment/test/bag_of_embeddings/classic_base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/hybrid_base.yaml rename configs/experiment/test/bag_of_embeddings/pgp/{base.yaml => hybrid_base.yaml} (81%) rename configs/experiment/test/bag_of_embeddings/{virchow2/tile_threshold.yaml => pgp/hybrid_tile_threshold.yaml} (79%) rename configs/experiment/test/bag_of_embeddings/pgp/test_suite/{base.yaml => hybrid_base.yaml} (67%) rename configs/experiment/test/bag_of_embeddings/{virchow2/test_suite/mmci_tl_test.yaml => pgp/test_suite/hybrid_mmci_tl_test.yaml} (53%) create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/classic_base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/classic_tile_threshold.yaml rename configs/experiment/test/bag_of_embeddings/virchow2/{base.yaml => hybrid_base.yaml} (82%) rename configs/experiment/test/bag_of_embeddings/{pgp/tile_threshold.yaml => virchow2/hybrid_tile_threshold.yaml} (78%) rename configs/experiment/test/bag_of_embeddings/virchow2/test_suite/{base.yaml => hybrid_base.yaml} (65%) rename configs/experiment/test/bag_of_embeddings/{pgp/test_suite/mmci_tl_test.yaml => virchow2/test_suite/hybrid_mmci_tl_test.yaml} (51%) create mode 100644 configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml create mode 100644 configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml diff --git a/configs/experiment/test/bag_of_embeddings/base.yaml b/configs/experiment/test/bag_of_embeddings/base.yaml index 919deb91..5d021718 100644 --- a/configs/experiment/test/bag_of_embeddings/base.yaml +++ b/configs/experiment/test/bag_of_embeddings/base.yaml @@ -2,7 +2,6 @@ defaults: - /experiment/test/base - - /datamodule/bag_of_embeddings_datamodule@datamodule - _self_ slide_threshold: ${model_data.slide_threshold} diff --git a/configs/experiment/test/bag_of_embeddings/classic_base.yaml b/configs/experiment/test/bag_of_embeddings/classic_base.yaml new file mode 100644 index 00000000..a4be3141 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/classic_base.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/base + - /datamodule/classic_bag_of_embeddings_datamodule@datamodule + - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/hybrid_base.yaml b/configs/experiment/test/bag_of_embeddings/hybrid_base.yaml new file mode 100644 index 00000000..99d88861 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/hybrid_base.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/base + - /datamodule/bag_of_embeddings_datamodule@datamodule + - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/pgp/base.yaml b/configs/experiment/test/bag_of_embeddings/pgp/hybrid_base.yaml similarity index 81% rename from configs/experiment/test/bag_of_embeddings/pgp/base.yaml rename to configs/experiment/test/bag_of_embeddings/pgp/hybrid_base.yaml index 25db044e..613f5477 100644 --- a/configs/experiment/test/bag_of_embeddings/pgp/base.yaml +++ b/configs/experiment/test/bag_of_embeddings/pgp/hybrid_base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/test/bag_of_embeddings/base + - /experiment/test/bag_of_embeddings/hybrid_base - /model/pgp_hybrid_mil_model@model - /model/checkpoints/mmci_tl/pgp_hybrid_mil@model_data - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml b/configs/experiment/test/bag_of_embeddings/pgp/hybrid_tile_threshold.yaml similarity index 79% rename from configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml rename to configs/experiment/test/bag_of_embeddings/pgp/hybrid_tile_threshold.yaml index 8d5ac728..9ee56695 100644 --- a/configs/experiment/test/bag_of_embeddings/virchow2/tile_threshold.yaml +++ b/configs/experiment/test/bag_of_embeddings/pgp/hybrid_tile_threshold.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/tile_threshold_mil@callbacks - - /experiment/test/bag_of_embeddings/virchow2/base + - /experiment/test/bag_of_embeddings/pgp/hybrid_base - /data/mmci/tl_val@test_data - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/base.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_base.yaml similarity index 67% rename from configs/experiment/test/bag_of_embeddings/pgp/test_suite/base.yaml rename to configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_base.yaml index 32e8817b..e0138d48 100644 --- a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/base.yaml +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_base.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/combined_test_suite_mil@callbacks - - /experiment/test/bag_of_embeddings/pgp/base + - /experiment/test/bag_of_embeddings/pgp/hybrid_base - _self_ use_case: "TL Test" diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/mmci_tl_test.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_mmci_tl_test.yaml similarity index 53% rename from configs/experiment/test/bag_of_embeddings/virchow2/test_suite/mmci_tl_test.yaml rename to configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_mmci_tl_test.yaml index 6bb9ae18..a478a197 100644 --- a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/mmci_tl_test.yaml +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_mmci_tl_test.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/test/bag_of_embeddings/virchow2/test_suite/base + - /experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_base - /data/mmci/tl_test@test_data - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/classic_base.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/classic_base.yaml new file mode 100644 index 00000000..d53707ce --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/classic_base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/classic_base + - /model/virchow2_classic_mil_model@model + - /model/checkpoints/mmci_tl/virchow2_classic_mil@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_w_virchow2_uri_224} diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/classic_tile_threshold.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/classic_tile_threshold.yaml new file mode 100644 index 00000000..c70913f6 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/classic_tile_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold_mil@callbacks + - /experiment/test/bag_of_embeddings/virchow2/classic_base + - /data/mmci/tl_val@test_data + - _self_ + +tile_threshold: 0.9 # Pathologist's estimate for other models +use_case: "TL Threshold Estimation" diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/base.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/hybrid_base.yaml similarity index 82% rename from configs/experiment/test/bag_of_embeddings/virchow2/base.yaml rename to configs/experiment/test/bag_of_embeddings/virchow2/hybrid_base.yaml index c90e5378..c226c019 100644 --- a/configs/experiment/test/bag_of_embeddings/virchow2/base.yaml +++ b/configs/experiment/test/bag_of_embeddings/virchow2/hybrid_base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/test/bag_of_embeddings/base + - /experiment/test/bag_of_embeddings/hybrid_base - /model/virchow2_hybrid_mil_model@model - /model/checkpoints/mmci_tl/virchow2_hybrid_mil@model_data - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/pgp/tile_threshold.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/hybrid_tile_threshold.yaml similarity index 78% rename from configs/experiment/test/bag_of_embeddings/pgp/tile_threshold.yaml rename to configs/experiment/test/bag_of_embeddings/virchow2/hybrid_tile_threshold.yaml index 1e133ddd..6ca0cfc1 100644 --- a/configs/experiment/test/bag_of_embeddings/pgp/tile_threshold.yaml +++ b/configs/experiment/test/bag_of_embeddings/virchow2/hybrid_tile_threshold.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/tile_threshold_mil@callbacks - - /experiment/test/bag_of_embeddings/pgp/base + - /experiment/test/bag_of_embeddings/virchow2/hybrid_base - /data/mmci/tl_val@test_data - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/base.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_base.yaml similarity index 65% rename from configs/experiment/test/bag_of_embeddings/virchow2/test_suite/base.yaml rename to configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_base.yaml index 2bc6d4e4..44f929fc 100644 --- a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/base.yaml +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_base.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/combined_test_suite_mil@callbacks - - /experiment/test/bag_of_embeddings/virchow2/base + - /experiment/test/bag_of_embeddings/virchow2/hybrid_base - _self_ use_case: "TL Test" diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/mmci_tl_test.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_mmci_tl_test.yaml similarity index 51% rename from configs/experiment/test/bag_of_embeddings/pgp/test_suite/mmci_tl_test.yaml rename to configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_mmci_tl_test.yaml index f5f43efc..157bdbac 100644 --- a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/mmci_tl_test.yaml +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_mmci_tl_test.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/test/bag_of_embeddings/pgp/test_suite/base + - /experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_base - /data/mmci/tl_test@test_data - _self_ diff --git a/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml new file mode 100644 index 00000000..7146b60b --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml @@ -0,0 +1,9 @@ +defaults: + - base_foundation + - _self_ + +tile_threshold: 0.5 +slide_threshold: 0.5 + +checkpoint: "..." +model_name: "PGP (Classic-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml new file mode 100644 index 00000000..45989114 --- /dev/null +++ b/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml @@ -0,0 +1,9 @@ +defaults: + - base_foundation + - _self_ + +tile_threshold: 0.5 +slide_threshold: 0.5 + +checkpoint: "mlflow-artifacts:/65/8613d45c98664f63b5cdd7e40e190986/artifacts/checkpoints/epoch=5-step=950/checkpoint.ckpt" +model_name: "Virchow2 (Classic-MIL)" From b38b6076adb0a580ad51d14367040f459d76d142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 28 Jul 2026 18:44:19 +0000 Subject: [PATCH 274/284] feat: heatmaps --- .../experiment/predict/bag_of_embeddings/base.yaml | 5 ++--- .../predict/bag_of_embeddings/classic_base.yaml | 6 ++++++ .../predict/bag_of_embeddings/hybrid_base.yaml | 6 ++++++ .../pgp/{base.yaml => hybrid_base.yaml} | 2 +- .../hybrid_slide_threshold.yaml} | 2 +- .../mil_predictions/hybrid_base.yaml} | 2 +- .../{mmci_tl_val.yaml => hybrid_mci_tl_val.yaml} | 2 +- ...i_sl_group_1.yaml => hybrid_mmci_sl_group_1.yaml} | 2 +- .../bag_of_embeddings/virchow2/classic_base.yaml | 12 ++++++++++++ .../virchow2/classic_slide_threshold.yaml | 10 ++++++++++ .../virchow2/{base.yaml => hybrid_base.yaml} | 2 +- .../hybrid_slide_threshold.yaml} | 2 +- .../virchow2/mil_predictions/classic_base.yaml | 8 ++++++++ .../mil_predictions/classic_mmci_sl_group_1.yaml | 6 ++++++ .../mil_predictions/classic_mmci_tl_val.yaml | 6 ++++++ .../mil_predictions/hybrid_base.yaml} | 2 +- ...i_sl_group_1.yaml => hybrid_mmci_sl_group_1.yaml} | 2 +- .../{mmci_tl_val.yaml => hybrid_mmci_tl_val.yaml} | 2 +- 18 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 configs/experiment/predict/bag_of_embeddings/classic_base.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/hybrid_base.yaml rename configs/experiment/predict/bag_of_embeddings/pgp/{base.yaml => hybrid_base.yaml} (80%) rename configs/experiment/predict/bag_of_embeddings/{virchow2/slide_threshold.yaml => pgp/hybrid_slide_threshold.yaml} (78%) rename configs/experiment/predict/bag_of_embeddings/{virchow2/mil_predictions/base.yaml => pgp/mil_predictions/hybrid_base.yaml} (67%) rename configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/{mmci_tl_val.yaml => hybrid_mci_tl_val.yaml} (50%) rename configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/{mmci_sl_group_1.yaml => hybrid_mmci_sl_group_1.yaml} (52%) create mode 100644 configs/experiment/predict/bag_of_embeddings/virchow2/classic_base.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/virchow2/classic_slide_threshold.yaml rename configs/experiment/predict/bag_of_embeddings/virchow2/{base.yaml => hybrid_base.yaml} (81%) rename configs/experiment/predict/bag_of_embeddings/{pgp/slide_threshold.yaml => virchow2/hybrid_slide_threshold.yaml} (77%) create mode 100644 configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_base.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_sl_group_1.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_tl_val.yaml rename configs/experiment/predict/bag_of_embeddings/{pgp/mil_predictions/base.yaml => virchow2/mil_predictions/hybrid_base.yaml} (65%) rename configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/{mmci_sl_group_1.yaml => hybrid_mmci_sl_group_1.yaml} (90%) rename configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/{mmci_tl_val.yaml => hybrid_mmci_tl_val.yaml} (90%) diff --git a/configs/experiment/predict/bag_of_embeddings/base.yaml b/configs/experiment/predict/bag_of_embeddings/base.yaml index fa5dc20b..39a2a460 100644 --- a/configs/experiment/predict/bag_of_embeddings/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/base.yaml @@ -2,7 +2,6 @@ defaults: - /experiment/predict/base - - /datamodule/bag_of_embeddings_datamodule@datamodule - _self_ slide_threshold: ${model_data.slide_threshold} @@ -10,5 +9,5 @@ model: sl_threshold: ${slide_threshold} datamodule: - batch_size: 2 - num_workers: 4 + batch_size: 4 + num_workers: 8 diff --git a/configs/experiment/predict/bag_of_embeddings/classic_base.yaml b/configs/experiment/predict/bag_of_embeddings/classic_base.yaml new file mode 100644 index 00000000..a78c6262 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/classic_base.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/base + - /datamodule/classic_bag_of_embeddings_datamodule@datamodule + - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/hybrid_base.yaml b/configs/experiment/predict/bag_of_embeddings/hybrid_base.yaml new file mode 100644 index 00000000..c9e3c4e0 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/hybrid_base.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/base + - /datamodule/bag_of_embeddings_datamodule@datamodule + - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/hybrid_base.yaml similarity index 80% rename from configs/experiment/predict/bag_of_embeddings/pgp/base.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/hybrid_base.yaml index 78f975da..f701af45 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/hybrid_base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/predict/bag_of_embeddings/base + - /experiment/predict/bag_of_embeddings/hybrid_base - /model/pgp_hybrid_mil_model@model - /model/checkpoints/mmci_tl/pgp_hybrid_mil@model_data - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/slide_threshold.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/hybrid_slide_threshold.yaml similarity index 78% rename from configs/experiment/predict/bag_of_embeddings/virchow2/slide_threshold.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/hybrid_slide_threshold.yaml index dbcd62b5..dad11f6c 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/slide_threshold.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/hybrid_slide_threshold.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/slide_threshold_mil@callbacks - - /experiment/predict/bag_of_embeddings/virchow2/base + - /experiment/predict/bag_of_embeddings/pgp/hybrid_base - /data/mmci/sl_group_1@predict_data - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_base.yaml similarity index 67% rename from configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_base.yaml index 577459d9..8eabe193 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_base.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/mil_prediction_callback@callbacks - - /experiment/predict/bag_of_embeddings/virchow2/base + - /experiment/predict/bag_of_embeddings/pgp/hybrid_base - _self_ use_case: "MIL predictions" diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_mci_tl_val.yaml similarity index 50% rename from configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_mci_tl_val.yaml index c0c2aeba..2f622a9a 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_tl_val.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_mci_tl_val.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/base + - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_base - /data/mmci/tl_val@predict_data - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_sl_group_1.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_mmci_sl_group_1.yaml similarity index 52% rename from configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_sl_group_1.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_mmci_sl_group_1.yaml index ad7964be..5112fa0a 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/mmci_sl_group_1.yaml +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_mmci_sl_group_1.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/base + - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/hybrid_base - /data/mmci/sl_group_1@predict_data - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/classic_base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/classic_base.yaml new file mode 100644 index 00000000..3b22a5a4 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/classic_base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/classic_base + - /model/virchow2_classic_mil_model@model + - /model/checkpoints/mmci_tl/virchow2_classic_mil@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_w_virchow2_uri_224} diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/classic_slide_threshold.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/classic_slide_threshold.yaml new file mode 100644 index 00000000..4d58bfd2 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/classic_slide_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/slide_threshold_mil@callbacks + - /experiment/predict/bag_of_embeddings/virchow2/classic_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +slide_threshold: 0.99 # My estimate for other models +use_case: "SL Threshold Estimation" diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/hybrid_base.yaml similarity index 81% rename from configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/hybrid_base.yaml index cff0ca13..37be6954 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/hybrid_base.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - - /experiment/predict/bag_of_embeddings/base + - /experiment/predict/bag_of_embeddings/hybrid_base - /model/virchow2_hybrid_mil_model@model - /model/checkpoints/mmci_tl/virchow2_hybrid_mil@model_data - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/slide_threshold.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/hybrid_slide_threshold.yaml similarity index 77% rename from configs/experiment/predict/bag_of_embeddings/pgp/slide_threshold.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/hybrid_slide_threshold.yaml index 11f1d550..b59bc3dc 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/slide_threshold.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/hybrid_slide_threshold.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/slide_threshold_mil@callbacks - - /experiment/predict/bag_of_embeddings/pgp/base + - /experiment/predict/bag_of_embeddings/virchow2/hybrid_base - /data/mmci/sl_group_1@predict_data - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_base.yaml new file mode 100644 index 00000000..a81b0042 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/mil_prediction_callback@callbacks + - /experiment/predict/bag_of_embeddings/virchow2/classic_base + - _self_ + +use_case: "MIL predictions" diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_sl_group_1.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_sl_group_1.yaml new file mode 100644 index 00000000..a59daeaa --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_sl_group_1.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_base + - /data/mmci/sl_group_1@predict_data + - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_tl_val.yaml new file mode 100644 index 00000000..b7d6c054 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_mmci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/classic_base + - /data/mmci/tl_val@predict_data + - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_base.yaml similarity index 65% rename from configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_base.yaml index 3d239b1d..749c94fc 100644 --- a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/base.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_base.yaml @@ -2,7 +2,7 @@ defaults: - /callbacks/mil_prediction_callback@callbacks - - /experiment/predict/bag_of_embeddings/pgp/base + - /experiment/predict/bag_of_embeddings/virchow2/hybrid_base - _self_ use_case: "MIL predictions" diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_sl_group_1.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_mmci_sl_group_1.yaml similarity index 90% rename from configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_sl_group_1.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_mmci_sl_group_1.yaml index fe9179fb..b6383a10 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_sl_group_1.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_mmci_sl_group_1.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base + - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_base - /data/mmci/sl_group_1@predict_data - _self_ diff --git a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_mmci_tl_val.yaml similarity index 90% rename from configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml rename to configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_mmci_tl_val.yaml index d8acc5e3..e72a3f3c 100644 --- a/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/mmci_tl_val.yaml +++ b/configs/experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_mmci_tl_val.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/base + - /experiment/predict/bag_of_embeddings/virchow2/mil_predictions/hybrid_base - /data/mmci/tl_val@predict_data - _self_ From 8434c3e8c51e38fc40a6d6815e329a49fd0ee2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 29 Jul 2026 06:30:01 +0000 Subject: [PATCH 275/284] feat: tl test --- .../virchow2/test_suite/classic_base.yaml | 8 ++++++++ .../virchow2/test_suite/classic_mmci_tl_test.yaml | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_mmci_tl_test.yaml diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_base.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_base.yaml new file mode 100644 index 00000000..52e1e964 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite_mil@callbacks + - /experiment/test/bag_of_embeddings/virchow2/classic_base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_mmci_tl_test.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_mmci_tl_test.yaml new file mode 100644 index 00000000..b9cf0792 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/virchow2/test_suite/classic_base + - /data/mmci/tl_test@test_data + - _self_ From 0310b77b5935e330f4b679b7661fa2ef30990d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 29 Jul 2026 06:49:22 +0000 Subject: [PATCH 276/284] feat: tl t pgp --- .../test/bag_of_embeddings/pgp/classic_base.yaml | 12 ++++++++++++ .../pgp/classic_tile_threshold.yaml | 10 ++++++++++ .../model/checkpoints/mmci_tl/pgp_classic_mil.yaml | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/classic_base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/classic_tile_threshold.yaml diff --git a/configs/experiment/test/bag_of_embeddings/pgp/classic_base.yaml b/configs/experiment/test/bag_of_embeddings/pgp/classic_base.yaml new file mode 100644 index 00000000..f7f44a44 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/classic_base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/classic_base + - /model/pgp_classic_mil_model@model + - /model/checkpoints/mmci_tl/pgp_classic_mil@model_data + - _self_ + +datamodule: + test: + uris: + - ${test_data.tiles_filtered_w_pgp_uri_224} diff --git a/configs/experiment/test/bag_of_embeddings/pgp/classic_tile_threshold.yaml b/configs/experiment/test/bag_of_embeddings/pgp/classic_tile_threshold.yaml new file mode 100644 index 00000000..ebe77aaf --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/classic_tile_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/tile_threshold_mil@callbacks + - /experiment/test/bag_of_embeddings/pgp/classic_base + - /data/mmci/tl_val@test_data + - _self_ + +tile_threshold: 0.9 # Pathologist's estimate for other models +use_case: "TL Threshold Estimation" diff --git a/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml index 7146b60b..bd556f86 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml @@ -5,5 +5,5 @@ defaults: tile_threshold: 0.5 slide_threshold: 0.5 -checkpoint: "..." +checkpoint: "mlflow-artifacts:/65/9e9e6ec6817844c296004f0dc7369d1f/artifacts/checkpoints/epoch=6-step=1221/checkpoint.ckpt" model_name: "PGP (Classic-MIL)" From bfe34cd75c2c1a48c33129b11102c24999df2d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 29 Jul 2026 07:11:01 +0000 Subject: [PATCH 277/284] feat: classic sl t and heatmaps --- .../predict/bag_of_embeddings/pgp/classic_base.yaml | 12 ++++++++++++ .../pgp/classic_slide_threshold.yaml | 10 ++++++++++ .../pgp/mil_predictions/classic_base.yaml | 8 ++++++++ .../pgp/mil_predictions/classic_mci_tl_val.yaml | 6 ++++++ 4 files changed, 36 insertions(+) create mode 100644 configs/experiment/predict/bag_of_embeddings/pgp/classic_base.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/pgp/classic_slide_threshold.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_base.yaml create mode 100644 configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mci_tl_val.yaml diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/classic_base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/classic_base.yaml new file mode 100644 index 00000000..1dbaf40d --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/pgp/classic_base.yaml @@ -0,0 +1,12 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/classic_base + - /model/pgp_classic_mil_model@model + - /model/checkpoints/mmci_tl/pgp_classic_mil@model_data + - _self_ + +datamodule: + predict: + uris: + - ${predict_data.tiles_filtered_w_pgp_uri_224} diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/classic_slide_threshold.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/classic_slide_threshold.yaml new file mode 100644 index 00000000..eb302c57 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/pgp/classic_slide_threshold.yaml @@ -0,0 +1,10 @@ +# @package _global_ + +defaults: + - /callbacks/slide_threshold_mil@callbacks + - /experiment/predict/bag_of_embeddings/pgp/classic_base + - /data/mmci/sl_group_1@predict_data + - _self_ + +slide_threshold: 0.99 # My estimate for other models +use_case: "SL Threshold Estimation" diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_base.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_base.yaml new file mode 100644 index 00000000..1e6a8f54 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/mil_prediction_callback@callbacks + - /experiment/predict/bag_of_embeddings/pgp/classic_base + - _self_ + +use_case: "MIL predictions" diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mci_tl_val.yaml new file mode 100644 index 00000000..26fbb884 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mci_tl_val.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_base + - /data/mmci/tl_val@predict_data + - _self_ From e6d2c3b1182da2228dab8c66ab5338087f32afda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 29 Jul 2026 07:23:21 +0000 Subject: [PATCH 278/284] fix: typo --- .../{classic_mci_tl_val.yaml => classic_mmci_tl_val.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/{classic_mci_tl_val.yaml => classic_mmci_tl_val.yaml} (100%) diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mci_tl_val.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mmci_tl_val.yaml similarity index 100% rename from configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mci_tl_val.yaml rename to configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mmci_tl_val.yaml From b7e1bf7a3692a9fb99cd9c0c17f4a8ee70c79cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 29 Jul 2026 09:45:34 +0000 Subject: [PATCH 279/284] feat: g1 heatmaps pgp --- .../pgp/mil_predictions/classic_mmci_sl_group_1.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mmci_sl_group_1.yaml diff --git a/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mmci_sl_group_1.yaml b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mmci_sl_group_1.yaml new file mode 100644 index 00000000..a46d9ed6 --- /dev/null +++ b/configs/experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_mmci_sl_group_1.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/predict/bag_of_embeddings/pgp/mil_predictions/classic_base + - /data/mmci/sl_group_1@predict_data + - _self_ From e2b03e378c11ee6131b8a78167383e3000887705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Wed, 29 Jul 2026 11:54:46 +0000 Subject: [PATCH 280/284] feat: pgp test --- .../bag_of_embeddings/pgp/test_suite/classic_base.yaml | 8 ++++++++ .../pgp/test_suite/classic_mmci_tl_test.yaml | 6 ++++++ configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml | 4 ++-- .../model/checkpoints/mmci_tl/virchow2_classic_mil.yaml | 4 ++-- 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_base.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_mmci_tl_test.yaml diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_base.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_base.yaml new file mode 100644 index 00000000..65918647 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_base.yaml @@ -0,0 +1,8 @@ +# @package _global_ + +defaults: + - /callbacks/combined_test_suite_mil@callbacks + - /experiment/test/bag_of_embeddings/pgp/classic_base + - _self_ + +use_case: "TL Test" diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_mmci_tl_test.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_mmci_tl_test.yaml new file mode 100644 index 00000000..1fcab4ea --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_mmci_tl_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/pgp/test_suite/classic_base + - /data/mmci/tl_test@test_data + - _self_ diff --git a/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml index bd556f86..371a74ed 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml @@ -2,8 +2,8 @@ defaults: - base_foundation - _self_ -tile_threshold: 0.5 -slide_threshold: 0.5 +tile_threshold: 0.65 +slide_threshold: 0.1796019971 checkpoint: "mlflow-artifacts:/65/9e9e6ec6817844c296004f0dc7369d1f/artifacts/checkpoints/epoch=6-step=1221/checkpoint.ckpt" model_name: "PGP (Classic-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml index 45989114..781c1a43 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml @@ -2,8 +2,8 @@ defaults: - base_foundation - _self_ -tile_threshold: 0.5 -slide_threshold: 0.5 +tile_threshold: 0.77 +slide_threshold: 0.1469607204 checkpoint: "mlflow-artifacts:/65/8613d45c98664f63b5cdd7e40e190986/artifacts/checkpoints/epoch=5-step=950/checkpoint.ckpt" model_name: "Virchow2 (Classic-MIL)" From b202b0ccfebef5b53c0d76a2d3325394e6257710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 30 Jul 2026 07:09:28 +0000 Subject: [PATCH 281/284] feat: test mil models --- .../pgp/test_suite/classic_radboud_test.yaml | 6 ++++++ .../pgp/test_suite/hybrid_radboud_test.yaml | 6 ++++++ .../virchow2/test_suite/classic_radboud_test.yaml | 6 ++++++ .../virchow2/test_suite/hybrid_radboud_test.yaml | 6 ++++++ configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml | 2 ++ configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml | 2 ++ configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml | 2 ++ configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml | 2 ++ 8 files changed, 32 insertions(+) create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml create mode 100644 configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml new file mode 100644 index 00000000..3d4a50bb --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/pgp/test_suite/classic_base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml new file mode 100644 index 00000000..ca9c2d2d --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml new file mode 100644 index 00000000..9d05d029 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/virchow2/test_suite/classic_base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml new file mode 100644 index 00000000..361c1f84 --- /dev/null +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml @@ -0,0 +1,6 @@ +# @package _global_ + +defaults: + - /experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_base + - /data/radboud/test@test_data + - _self_ diff --git a/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml index 371a74ed..bf9eab49 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_classic_mil.yaml @@ -6,4 +6,6 @@ tile_threshold: 0.65 slide_threshold: 0.1796019971 checkpoint: "mlflow-artifacts:/65/9e9e6ec6817844c296004f0dc7369d1f/artifacts/checkpoints/epoch=6-step=1221/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/pgp_classic_mil_checkpoint.ckpt" + model_name: "PGP (Classic-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml b/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml index 4c5b99d9..d4baa827 100644 --- a/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/pgp_hybrid_mil.yaml @@ -6,4 +6,6 @@ tile_threshold: 0.88 slide_threshold: 0.9783778786 checkpoint: "mlflow-artifacts:/65/7cb992adfe4b422790becc5984ff2a6f/artifacts/checkpoints/epoch=8-step=1583/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/pgp_hybrid_mil_checkpoint.ckpt" + model_name: "PGP (Hybrid-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml index 781c1a43..c3629376 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_classic_mil.yaml @@ -6,4 +6,6 @@ tile_threshold: 0.77 slide_threshold: 0.1469607204 checkpoint: "mlflow-artifacts:/65/8613d45c98664f63b5cdd7e40e190986/artifacts/checkpoints/epoch=5-step=950/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/virchow2_classic_mil_checkpoint.ckpt" + model_name: "Virchow2 (Classic-MIL)" diff --git a/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml b/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml index 11f36170..cb602b38 100644 --- a/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml +++ b/configs/model/checkpoints/mmci_tl/virchow2_hybrid_mil.yaml @@ -6,4 +6,6 @@ tile_threshold: 0.85 slide_threshold: 0.9917988777 checkpoint: "mlflow-artifacts:/65/5e0c1d4699ca49c4938baf3c65e7f61c/artifacts/checkpoints/epoch=6-step=1176/checkpoint.ckpt" +public_checkpoint: "mlflow-artifacts:/38/abde571304794242be0c1630b7bd16ce/artifacts/virchow2_hybrid_mil_checkpoint.ckpt" + model_name: "Virchow2 (Hybrid-MIL)" From b48840fdd87ffc5b30b18c81f09a81fe175b86d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Thu, 30 Jul 2026 07:31:35 +0000 Subject: [PATCH 282/284] feat: public ckpt --- .../bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml | 2 ++ .../bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml | 2 ++ .../virchow2/test_suite/classic_radboud_test.yaml | 2 ++ .../virchow2/test_suite/hybrid_radboud_test.yaml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml index 3d4a50bb..f05a3f29 100644 --- a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/classic_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/bag_of_embeddings/pgp/test_suite/classic_base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml index ca9c2d2d..5507086a 100644 --- a/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml +++ b/configs/experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/bag_of_embeddings/pgp/test_suite/hybrid_base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml index 9d05d029..0b9dc7fd 100644 --- a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/classic_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/bag_of_embeddings/virchow2/test_suite/classic_base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} diff --git a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml index 361c1f84..debfccc8 100644 --- a/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml +++ b/configs/experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_radboud_test.yaml @@ -4,3 +4,5 @@ defaults: - /experiment/test/bag_of_embeddings/virchow2/test_suite/hybrid_base - /data/radboud/test@test_data - _self_ + +checkpoint: ${model_data.public_checkpoint} From 250b3974227b2ee851a569bf264531e821f7c7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 4 Aug 2026 12:21:58 +0000 Subject: [PATCH 283/284] fix: remove unwanted changes --- .../embeddings/tile_embeddings_v2.py | 2 +- .../datamodule/transforms/__init__.py | 0 .../transforms/stain_normalization.py | 22 ------------------- prostate_cancer/typing.py | 8 ++----- prostate_cancer/vit_model.py | 3 --- 5 files changed, 3 insertions(+), 32 deletions(-) delete mode 100644 prostate_cancer/datamodule/transforms/__init__.py delete mode 100644 prostate_cancer/datamodule/transforms/stain_normalization.py diff --git a/preprocessing/embeddings/tile_embeddings_v2.py b/preprocessing/embeddings/tile_embeddings_v2.py index 877bb4a1..f15ca253 100644 --- a/preprocessing/embeddings/tile_embeddings_v2.py +++ b/preprocessing/embeddings/tile_embeddings_v2.py @@ -91,7 +91,7 @@ def main(config: DictConfig, logger: MLFlowLogger) -> None: shutil.rmtree(slides_parquet_dir) slides_parquet_dir.mkdir(parents=True, exist_ok=True) - slides.to_parquet(output_path / "slides" / "slides.parquet", index=False) + slides.to_parquet(slides_parquet_dir / "slides.parquet", index=False) ds.write_parquet(str(tiles_parquet_dir), max_rows_per_file=config.rows_per_file) logger.log_artifacts(str(output_path), f"{config.data.data_name}") diff --git a/prostate_cancer/datamodule/transforms/__init__.py b/prostate_cancer/datamodule/transforms/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/prostate_cancer/datamodule/transforms/stain_normalization.py b/prostate_cancer/datamodule/transforms/stain_normalization.py deleted file mode 100644 index 4b3fd33d..00000000 --- a/prostate_cancer/datamodule/transforms/stain_normalization.py +++ /dev/null @@ -1,22 +0,0 @@ -from collections.abc import Sequence - -from rationai.staining import ColorConversion, NormalizeStainingTransform - - -def build_normalize_staining_transform( - stain1: Sequence[float], - stain2: Sequence[float], - stain3: Sequence[float], - target_stain1: Sequence[float], - target_stain2: Sequence[float], - target_stain3: Sequence[float], -) -> NormalizeStainingTransform: - conversion = ColorConversion.from_stain_vectors( - tuple(stain1), tuple(stain2), tuple(stain3) - ) - return NormalizeStainingTransform( - rgb2stain=conversion.matrix, - target_stain1=tuple(target_stain1), - target_stain2=tuple(target_stain2), - target_stain3=tuple(target_stain3), - ) diff --git a/prostate_cancer/typing.py b/prostate_cancer/typing.py index baa94020..327725d4 100644 --- a/prostate_cancer/typing.py +++ b/prostate_cancer/typing.py @@ -38,12 +38,8 @@ class TileMetadataBatch(TypedDict): ] # Image | label | Metadata UnlabeledTileSample: TypeAlias = tuple[Tensor, TileMetadata] # Image | Metadata -LabeledTileSampleBatch: TypeAlias = tuple[ - Tensor, Tensor, TileMetadataBatch -] # Images | labels | Metadata -UnlabeledTileSampleBatch: TypeAlias = tuple[ - Tensor, TileMetadataBatch -] # Images | labels | Metadata +LabeledTileSampleBatch: TypeAlias = tuple[Tensor, Tensor, TileMetadataBatch] +UnlabeledTileSampleBatch: TypeAlias = tuple[Tensor, TileMetadataBatch] # how does slide metadata in bag dataset look like diff --git a/prostate_cancer/vit_model.py b/prostate_cancer/vit_model.py index dac7c77a..9c3b645b 100644 --- a/prostate_cancer/vit_model.py +++ b/prostate_cancer/vit_model.py @@ -1,6 +1,3 @@ -from collections.abc import Mapping -from typing import Any - from torch import Tensor, nn from transformers import ViTModel From 2dada5f5d02d196f2fc82d453d808fdc265fce1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C5=BEado=C5=88?= <524839@mail.muni.cz> Date: Tue, 4 Aug 2026 12:27:29 +0000 Subject: [PATCH 284/284] fix: base --- configs/experiment/test/base.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/configs/experiment/test/base.yaml b/configs/experiment/test/base.yaml index ac5aa364..dc6a491f 100644 --- a/configs/experiment/test/base.yaml +++ b/configs/experiment/test/base.yaml @@ -8,7 +8,6 @@ model: tile_threshold: ${model_data.tile_threshold} carcinoma_roi_t: ${test_data.thresholds.carcinoma_roi_t} -stains: ${test_data.stains} checkpoint: ${model_data.checkpoint} model_name: ${model_data.model_name} mean: ${model_data.stats.mean}